4 SQL执行
再次回到demo的示例代码,
@org.junit.jupiter.api.Testvoid queryBySn() throws IOException {//1 读取配置文件InputStream in = Resources.getResourceAsStream("mybatis-config.xml");//2 加载解析配置文件并获取SqlSessionFactory对象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);//3 获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession();//4 通过SqlSession中提供的API方法来操作数据库DeviceMapper deviceMapper = sqlSession.getMapper(DeviceMapper.class);Device device = deviceMapper.queryBySn(Device.builder().sn("2234567").build());System.out.println(device);//5 关闭会话sqlSession.close();}
看下SQL语句执行的入口:
Device device = deviceMapper.queryBySn(Device.builder().sn("2234567").build());
由于所有的Mapper都是JDK动态代理对象,所以任意方法都是执行触发管理类MapperProxy的invoke()
MapperProxy.invoke()
进入invoke方法,然后进入到 PlainMethodInvoker(内部类)的invoke方法:
public class MapperProxy<T> implements InvocationHandler, Serializable {......@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// toString hashCode equals getClass等方法,无需走到执行SQL的流程if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {// 提升获取 mapperMethod 的效率,到 MapperMethodInvoker(内部接口) 的 invoke// 普通方法会走到 PlainMethodInvoker(内部类) 的 invokereturn cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}......private static class PlainMethodInvoker implements MapperMethodInvoker {private final MapperMethod mapperMethod;public PlainMethodInvoker(MapperMethod mapperMethod) {super();this.mapperMethod = mapperMethod;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {// SQL执行的真正起点return mapperMethod.execute(sqlSession, args);}}......
}
MapperMethod.execute
public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) { // 根据SQL语句的类型调用SqlSession对应的方法case INSERT: {// 通过 ParamNameResolver 处理args[] 数组 将用户传入的实参和指定参数名称关联起来Object param = method.convertArgsToSqlCommandParam(args);// sqlSession.insert(command.getName(), param) 调用SqlSession的insert方法// rowCountResult 方法会根据 method 字段中记录的方法的返回值类型对结果进行转换result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {// 返回值为空 且 ResultSet通过 ResultHandler处理的方法executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {// 返回值为 单一对象的方法Object param = method.convertArgsToSqlCommandParam(args);// 普通 select 语句的执行入口 >>result = sqlSession.selectOne(command.getName(), param);if (method.returnsOptional()&& (result == null || !method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName()+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;}
这一步根据不同的type(INSERT、UPDATE、DELETE、SELECT)和返回类型:
① 调用convertArgsToSqlCommandParam() 讲方法参数转换为SQL的参数。
② 调用sqlSession的insert()、update()、delete()、selectOne() 方法。
以查询为例,会走到selectOne方法。
// 返回值为 单一对象的方法Object param = method.convertArgsToSqlCommandParam(args);// 普通 select 语句的执行入口 >>result = sqlSession.selectOne(command.getName(), param);
SqlSession.selectOne
对外接口的默认实现类:DefaultSqlSession
public class DefaultSqlSession implements SqlSession {......@Overridepublic <T> T selectOne(String statement, Object parameter) {List<T> list = this.selectList(statement, parameter);if (list.size() == 1) {return list.get(0);} else if (list.size() > 1) {throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());} else {return null;}}......@Overridepublic <E> List<E> selectList(String statement, Object parameter) {return this.selectList(statement, parameter, RowBounds.DEFAULT);}......@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms = configuration.getMappedStatement(statement);// 如果 cacheEnabled = true(默认),Executor会被 CachingExecutor装饰return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}......
}
- selectOne() 最终也是调用了 selectList()。
- 在selectList()中,我们先根据command name(Statement ID)从Configuration 中拿到MappedStatement。ms里面有xml中增删改查标签配置的所有属性,包括id、statementType、sqlSource、useCache、入参、出参等。
- 然后执行了Executor的query()方法。
- ExecutoropenSession的时候创建的,创建了执行器基本类型后,依次执行了二级缓存装饰、插件包装。如果有被插件包装,这里会先走到插件的逻辑。如果没有显式地在settings中配置 CacheEnable=false,再走到 CacheingExecutor 的逻辑,然后会走到BaseExecutor的query方法。
CachingExecutor.query
public class CachingExecutor implements Executor {@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {// 获取SQLBoundSql boundSql = ms.getBoundSql(parameterObject);// 创建CacheKey:什么样的SQL是同一条SQL? >>CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}
}
CacheKey 生成之后,调用另一个query()方法。
BaseExecutor.query
public abstract class BaseExecutor implements Executor {@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {Cache cache = ms.getCache();// cache 对象是在哪里创建的? XMLMapperBuilder类 xmlconfigurationElement()// 由 <cache> 标签决定if (cache != null) {// flushCache="true" 清空一级二级缓存 >>flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, boundSql);// 获取二级缓存// 缓存通过 TransactionalCacheManager、TransactionalCache 管理@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);if (list == null) {list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);// 写入二级缓存tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}......private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {List<E> list;// 先占位localCache.putObject(key, EXECUTION_PLACEHOLDER);try {// 三种 Executor 的区别,看doUpdate// 默认Simplelist = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {// 移除占位符localCache.removeObject(key);}// 写入一级缓存localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}
}
SimpleExecutor.doQuery
public class SimpleExecutor extends BaseExecutor {......@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();// 注意,已经来到SQL处理的关键对象 StatementHandler >>StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 获取一个 Statement对象stmt = prepareStatement(handler, ms.getStatementLog());// 执行查询return handler.query(stmt, resultHandler);} finally {// 用完就关闭closeStatement(stmt);}}......
}
在Configuration.newStatementHandler()中,new 一个StatementHandler,先得到 RoutingStatementHandler。
RoutingStatementHandler里面没有任何的实现,是用来创建基本的StatementHandler的。这里会根据MappedStatement里面的statementType决定 StatementHandler 的类型。默认是 PREPARED(STATEMENT、PREPARED、CALLABLE)
/*** @author Clinton Begin* 实际上是用来选择具体的 StatementHandler 类型的* 持有了具体的 StatementHandler 的委托* 调用具体 StatementHandler 对象的方法*/
public class RoutingStatementHandler implements StatementHandler {......public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {// StatementType 是怎么来的? 增删改查标签中的 statementType="PREPARED",默认值 PREPAREDswitch (ms.getStatementType()) {case STATEMENT:delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case PREPARED:// 创建 StatementHandler 的时候做了什么? >>delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case CALLABLE:delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;default:throw new ExecutorException("Unknown statement type: " + ms.getStatementType());}}......
}
StatementHandler
里面包含了处理参数的 ParameterHandler
和处理结果集的ResultSetHandler。这两个对象都是在上面new的时候创建的。
public abstract class BaseStatementHandler implements StatementHandler {......protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {this.configuration = mappedStatement.getConfiguration();this.executor = executor;this.mappedStatement = mappedStatement;this.rowBounds = rowBounds;this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();this.objectFactory = configuration.getObjectFactory();if (boundSql == null) { // issue #435, get the key before calculating the statementgenerateKeys(parameterObject);boundSql = mappedStatement.getBoundSql(parameterObject);}this.boundSql = boundSql;// 创建了四大对象的其它两大对象 >>// 创建这两大对象的时候分别做了什么?this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);}......
这三个对象都是可以被插件拦截的四大对象之一,所以在创建之后都要用拦截器进行包装的方法。
public class Configuration {......public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);// 植入插件逻辑(返回代理对象)parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler;}public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,ResultHandler resultHandler, BoundSql boundSql) {ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);// 植入插件逻辑(返回代理对象)resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);return resultSetHandler;}public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);// 植入插件逻辑(返回代理对象)statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;}......
}
创建 Statement,用new出来的StatementHandler 创建 Statement对象。
public class SimpleExecutor extends BaseExecutor {......@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();// 注意,已经来到SQL处理的关键对象 StatementHandler >>StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 获取一个 Statement对象stmt = prepareStatement(handler, ms.getStatementLog());// 执行查询return handler.query(stmt, resultHandler);} finally {// 用完就关闭closeStatement(stmt);}}......
}
执行查询操作,如果有插件包装,会先走到被拦截的业务逻辑。
// 执行查询
return handler.query(stmt, resultHandler);
进入到 PreparedStatementHandler
中处理
public class PreparedStatementHandler extends BaseStatementHandler {......@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;// 到了JDBC的流程ps.execute();// 处理结果集return resultSetHandler.handleResultSets(ps);}......
}
执行 PreparedStatement 的 execute 方法,后面就是 JDBC 包中的PreparedStatement的执行了。
ResultSetHandler处理结果集,如果有插件包装,会先走到被拦截的业务逻辑。
SQL 执行时序图