Java操作数据库需要经过3个大步骤:
- 获取数据库连接
- 执行SQL语句
- 关闭数据库连接
Mybatis将这几个步骤进行了封装,将获取数据库连接的给工作交给了SqlSessionFactory,将执行SQL的工作交给了SqlSession。
1 获取SqlSession#
在程序启动时,会根据配置创建SqlSessionFactory:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
SqlSessionFactory是一个工厂,在整个程序中应该是单例的存在。它会保存所有配置信息,需要操作数据库时,通过调用SqlSessionFactory#openSession()方法,可以根据配置信息生成新的SqlSession。
SqlSessionFactory#openSessionFromDataSource()展示了创建SqlSession的流程,本质上只是创建了Executor对象,并对其进行了封装:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);final Executor executor = configuration.newExecutor(tx, execType);return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
需要注意的是,Configuration#newExecutor()方法会对执行器进行缓存代理和插件代理。默认情况下,生成从外到内Interceptor -> CachingExecutor -> SimpleExecutor的层级结构:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor executor;if (ExecutorType.BATCH == executorType) {executor = new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);} else {executor = new SimpleExecutor(this, transaction);}if (cacheEnabled) {executor = new CachingExecutor(executor);}executor = (Executor) interceptorChain.pluginAll(executor);return executor;}public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
2 执行SQL语句#
Mybatis提供了两种执行SQL的方式:
- 根据statementId执行
- 使用映射接口代理对象执行
2.1 根据statementId执行#
SqlSession提供了selectXxx/insert/update/delete等方法操作数据库。
默认情况下,DefaultSqlSession会将selectXxx(除了selectCursor)方法统一交给selectList
方法处理,将insert/update/delete方法统一交给update
方法处理。它们的处理方式相同:
- 从Configuration中获取对应的MappedStatement
- 使用Executor执行SQL
DefaultSqlSession#selectList:
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {try {MappedStatement ms = configuration.getMappedStatement(statement);return executor.query(ms, wrapCollection(parameter), rowBounds, handler);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
DefaultSqlSession#update:
public int update(String statement, Object parameter) {try {dirty = true;MappedStatement ms = configuration.getMappedStatement(statement);return executor.update(ms, wrapCollection(parameter));} catch (Exception e) {throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}public int update(String statement, Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }public int update(String statement, Object parameter) { try { dirty = true; MappedStatement ms = configuration.getMappedStatement(statement); return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
2.2.1 Executor#selectList#
除了自定义Interceptor,首先会进入到CachingExecutor#query方法,它主要对全局缓存进行了处理:
- 根据查询条件(包括id、offset、limit、sql、查询参数和environmentId)创建CacheKey
- 获取MappedStatement的缓存对象Cache
- 从Cache中获取查询条件对应的缓存:如果有缓存,直接返回;如果没有缓存,进一步查询,并添加缓存
CachingExecutor#query:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {Cache cache = ms.getCache();if (cache != null) {flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, boundSql);@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;}}return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); @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; } } return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); @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; } } return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
之所以说CachingExecutor#query方法处理的是全局缓存,是因为这个缓存存在于MappedStatement,是所有会话共享的对象。
接着,会执行BaseExecutor#query方法,它主要对会话缓存进行处理:
- 从执行器localCache中获取查询条件对应的缓存
- 如果有缓存,直接返回;如果没有缓存,进一步查询数据库,并添加缓存
之所以说BaseExecutor#query方法处理的是会话缓存,是应为这个缓存存在于BaseExecutor,只有当前SqlSession才可以访问。
BaseExecutor#query和BaseExecutor#queryFromDatabase:
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {queryStack++;list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {clearLocalCache();}}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 {list = 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;}public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); } } 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 { list = 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; }public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); } } 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 { list = 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执行步骤如下:
- 创建StatementHandler
- 创建Statement(参数解析)
- 执行Statement
- 结果集封装
SimpleExecutor#doQuery:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();// 创建StatementHandlerStatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 创建Statement(参数解析)stmt = prepareStatement(handler, ms.getStatementLog());// 执行Statement&结果集封装return handler.query(stmt, resultHandler);} finally {closeStatement(stmt);}}public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); // 创建StatementHandler StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 创建Statement(参数解析) stmt = prepareStatement(handler, ms.getStatementLog()); // 执行Statement&结果集封装 return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } }public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); // 创建StatementHandler StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 创建Statement(参数解析) stmt = prepareStatement(handler, ms.getStatementLog()); // 执行Statement&结果集封装 return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } }
默认会创建RoutingStatementHandler,它只是一个代理对象,会根据MappedStatement创建具体SimpleStatementHandler/PreparedStatementHandler/CallableStatementHandler实现类。
在创建StatementHandler过程中,还会使用Interceptor进行代理,因此最终返回的对象是Interceptor -> RoutingStatementHandler -> 具体实现类的层级结构。
默认情况下,创建的StatementHandler具体实现类一般是PreparedStatementHandler。
BaseStatementHandler是SimpleStatementHandler/PreparedStatementHandler/CallableStatementHandler的共同父类,它的构造函数会创建ParameterHander和ResultSetHandler对象(并使用插件代理),用于参数解析和结果集封装。
Configuration#newStatementHandler、RoutingStatementHandler构造函数和BaseStatementHandler构造函数:
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {// 创建RoutingStatementHandlerStatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);// 使用插件代理statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;}public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {switch (ms.getStatementType()) {case STATEMENT:delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case PREPARED: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());}}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 StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 创建RoutingStatementHandler StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 使用插件代理 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; } public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: 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()); } } 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 statement generateKeys(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 StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 创建RoutingStatementHandler StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 使用插件代理 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; } public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: 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()); } } 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 statement generateKeys(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); }
SimpleExecutor#prepareStatement会创建Statement,并设置参数:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;Connection connection = getConnection(statementLog);// 创建Statementstmt = handler.prepare(connection, transaction.getTimeout());// 设置参数handler.parameterize(stmt);return stmt;}private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); // 创建Statement stmt = handler.prepare(connection, transaction.getTimeout()); // 设置参数 handler.parameterize(stmt); return stmt; }private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); // 创建Statement stmt = handler.prepare(connection, transaction.getTimeout()); // 设置参数 handler.parameterize(stmt); return stmt; }
设置参数时,除了自定义Interceptor,默认会执行PreparedStatementHandler#parameterize方法,实际上会调用ParameterHandler(默认是DefaultParameterHandler)进行处理:
- 获取参数映射配置信息
- 遍历参数映射配置信息,使用TypeHandler设置参数
PreparedStatementHandler#parameterize、DefaultParameterHandler#setParameters和BaseTypeHandler#setParameter:
public void parameterize(Statement statement) throws SQLException {parameterHandler.setParameters((PreparedStatement) statement);}public void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings != null) {for (int i = 0; i < parameterMappings.size(); i++) {ParameterMapping parameterMapping = parameterMappings.get(i);if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional paramsvalue = boundSql.getAdditionalParameter(propertyName);} else if (parameterObject == null) {value = null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value = parameterObject;} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (TypeException | SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);}}}}}public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {if (parameter == null) {if (jdbcType == null) {throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");}try {ps.setNull(i, jdbcType.TYPE_CODE);} catch (SQLException e) {throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "+ "Cause: " + e, e);}} else {try {setNonNullParameter(ps, i, parameter, jdbcType);} catch (Exception e) {throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "+ "Try setting a different JdbcType for this parameter or a different configuration property. "+ "Cause: " + e, e);}}}public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException | SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } } public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { if (parameter == null) { if (jdbcType == null) { throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters."); } try { ps.setNull(i, jdbcType.TYPE_CODE); } catch (SQLException e) { throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " + "Cause: " + e, e); } } else { try { setNonNullParameter(ps, i, parameter, jdbcType); } catch (Exception e) { throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different configuration property. " + "Cause: " + e, e); } } }public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException | SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } } public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { if (parameter == null) { if (jdbcType == null) { throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters."); } try { ps.setNull(i, jdbcType.TYPE_CODE); } catch (SQLException e) { throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " + "Cause: " + e, e); } } else { try { setNonNullParameter(ps, i, parameter, jdbcType); } catch (Exception e) { throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different configuration property. " + "Cause: " + e, e); } } }
接下来,默认情况下使用PreparedStatementHandler#query查询数据库,它会执行Statement,然后使用ResultSetHandler对结果集进行封装:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.handleResultSets(ps);}public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.handleResultSets(ps); }public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.handleResultSets(ps); }
ResultSetHandler的默认实现类是DefaultResultSetHandler,处理步骤如下:
- 从MappedStatement获取结果集字段映射关系ResultMap
- 根据ResultMap,使用ResultHandler对字段进行封装
DefaultResultSetHandler#handleResultSets、DefaultResultSetHandler#handleResultSet、DefaultResultSetHandler#handleRowValues、DefaultResultSetHandler#handleRowValuesForSimpleResultMap:
public List<Object> handleResultSets(Statement stmt) throws SQLException {ErrorContext.instance().activity("handling results").object(mappedStatement.getId());final List<Object> multipleResults = new ArrayList<>();int resultSetCount = 0;ResultSetWrapper rsw = getFirstResultSet(stmt);List<ResultMap> resultMaps = mappedStatement.getResultMaps();int resultMapCount = resultMaps.size();validateResultMapsCount(rsw, resultMapCount);while (rsw != null && resultMapCount > resultSetCount) {ResultMap resultMap = resultMaps.get(resultSetCount);handleResultSet(rsw, resultMap, multipleResults, null);rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}String[] resultSets = mappedStatement.getResultSets();if (resultSets != null) {while (rsw != null && resultSetCount < resultSets.length) {ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);if (parentMapping != null) {String nestedResultMapId = parentMapping.getNestedResultMapId();ResultMap resultMap = configuration.getResultMap(nestedResultMapId);handleResultSet(rsw, resultMap, null, parentMapping);}rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}}return collapseSingleResultList(multipleResults);}private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults,ResultMapping parentMapping) throws SQLException {try {if (parentMapping != null) {handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);} else if (resultHandler == null) {DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);multipleResults.add(defaultResultHandler.getResultList());} else {handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}} finally {closeResultSet(rsw.getResultSet());}}public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler,RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {if (resultMap.hasNestedResultMaps()) {ensureNoRowBounds();checkResultHandler();handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);} else {handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);}}public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; ResultSetWrapper rsw = getFirstResultSet(stmt); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } String[] resultSets = mappedStatement.getResultSets(); if (resultSets != null) { while (rsw != null && resultSetCount < resultSets.length) { ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]); if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } } return collapseSingleResultList(multipleResults); } private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException { try { if (parentMapping != null) { handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping); } else if (resultHandler == null) { DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory); handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null); multipleResults.add(defaultResultHandler.getResultList()); } else { handleRowValues(rsw, resultMap, resultHandler, rowBounds, null); } } finally { closeResultSet(rsw.getResultSet()); } } public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { if (resultMap.hasNestedResultMaps()) { ensureNoRowBounds(); checkResultHandler(); handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } else { handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } }public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; ResultSetWrapper rsw = getFirstResultSet(stmt); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } String[] resultSets = mappedStatement.getResultSets(); if (resultSets != null) { while (rsw != null && resultSetCount < resultSets.length) { ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]); if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } } return collapseSingleResultList(multipleResults); } private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException { try { if (parentMapping != null) { handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping); } else if (resultHandler == null) { DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory); handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null); multipleResults.add(defaultResultHandler.getResultList()); } else { handleRowValues(rsw, resultMap, resultHandler, rowBounds, null); } } finally { closeResultSet(rsw.getResultSet()); } } public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { if (resultMap.hasNestedResultMaps()) { ensureNoRowBounds(); checkResultHandler(); handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } else { handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping); } }
结果集封装主要有两个步骤:
- 根据ResultMap初始化响应Java对象
- 根据的属性-列字段名映射关系,从结果集获取对应值,设置到响应对象中
- 将该行结果集封装的Java对象,添加到返回值列表中
DefaultResultSetHandler#handleRowValuesForSimpleResultMap、DefaultResultSetHandler#getRowValue和DefaultResultSetHandler#applyAutomaticMappings:
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap,ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {DefaultResultContext<Object> resultContext = new DefaultResultContext<>();ResultSet resultSet = rsw.getResultSet();skipRows(resultSet, rowBounds);while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);// 获取每一行的响应对象Object rowValue = getRowValue(rsw, discriminatedResultMap, null);// 添加返回值列表storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);}}private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {final ResultLoaderMap lazyLoader = new ResultLoaderMap();Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {final MetaObject metaObject = configuration.newMetaObject(rowValue);boolean foundValues = this.useConstructorMappings;if (shouldApplyAutomaticMappings(resultMap, false)) {// 自动映射foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;}foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;foundValues = lazyLoader.size() > 0 || foundValues;rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;}return rowValue;}private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,String columnPrefix) throws SQLException {List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);boolean foundValues = false;if (!autoMapping.isEmpty()) {// 遍历属性-列字段名映射for (UnMappedColumnAutoMapping mapping : autoMapping) {// 将列数据封装成对应Java类型的属性值final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);if (value != null) {foundValues = true;}if (value != null || configuration.isCallSettersOnNulls() && !mapping.primitive) {// 将属性值设置到响应对象的成员变量中metaObject.setValue(mapping.property, value);}}}return foundValues;}private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { DefaultResultContext<Object> resultContext = new DefaultResultContext<>(); ResultSet resultSet = rsw.getResultSet(); skipRows(resultSet, rowBounds); while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) { ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null); // 获取每一行的响应对象 Object rowValue = getRowValue(rsw, discriminatedResultMap, null); // 添加返回值列表 storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet); } } private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException { final ResultLoaderMap lazyLoader = new ResultLoaderMap(); Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix); if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { final MetaObject metaObject = configuration.newMetaObject(rowValue); boolean foundValues = this.useConstructorMappings; if (shouldApplyAutomaticMappings(resultMap, false)) { // 自动映射 foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues; } foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues; foundValues = lazyLoader.size() > 0 || foundValues; rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null; } return rowValue; } private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); boolean foundValues = false; if (!autoMapping.isEmpty()) { // 遍历属性-列字段名映射 for (UnMappedColumnAutoMapping mapping : autoMapping) { // 将列数据封装成对应Java类型的属性值 final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); if (value != null) { foundValues = true; } if (value != null || configuration.isCallSettersOnNulls() && !mapping.primitive) { // 将属性值设置到响应对象的成员变量中 metaObject.setValue(mapping.property, value); } } } return foundValues; }private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException { DefaultResultContext<Object> resultContext = new DefaultResultContext<>(); ResultSet resultSet = rsw.getResultSet(); skipRows(resultSet, rowBounds); while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) { ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null); // 获取每一行的响应对象 Object rowValue = getRowValue(rsw, discriminatedResultMap, null); // 添加返回值列表 storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet); } } private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException { final ResultLoaderMap lazyLoader = new ResultLoaderMap(); Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix); if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) { final MetaObject metaObject = configuration.newMetaObject(rowValue); boolean foundValues = this.useConstructorMappings; if (shouldApplyAutomaticMappings(resultMap, false)) { // 自动映射 foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues; } foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues; foundValues = lazyLoader.size() > 0 || foundValues; rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null; } return rowValue; } private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException { List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix); boolean foundValues = false; if (!autoMapping.isEmpty()) { // 遍历属性-列字段名映射 for (UnMappedColumnAutoMapping mapping : autoMapping) { // 将列数据封装成对应Java类型的属性值 final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); if (value != null) { foundValues = true; } if (value != null || configuration.isCallSettersOnNulls() && !mapping.primitive) { // 将属性值设置到响应对象的成员变量中 metaObject.setValue(mapping.property, value); } } } return foundValues; }
2.1.2 Executor#update#
除了自定义Interceptor,首先会进入到CachingExecutor#update方法,它主要对全局缓存进行清除工作,然后再调用代理对象的BaseExecutor#update方法。
CachingExecutor#update和CachingExecutor#flushCacheIfRequired:
public int update(MappedStatement ms, Object parameterObject) throws SQLException {// 清除全局缓存flushCacheIfRequired(ms);// 执行代理对象更新方法return delegate.update(ms, parameterObject);}private void flushCacheIfRequired(MappedStatement ms) {// 从MappedStatement中获取缓存Cache cache = ms.getCache();if (cache != null && ms.isFlushCacheRequired()) {// 清除缓存tcm.clear(cache);}}public int update(MappedStatement ms, Object parameterObject) throws SQLException { // 清除全局缓存 flushCacheIfRequired(ms); // 执行代理对象更新方法 return delegate.update(ms, parameterObject); } private void flushCacheIfRequired(MappedStatement ms) { // 从MappedStatement中获取缓存 Cache cache = ms.getCache(); if (cache != null && ms.isFlushCacheRequired()) { // 清除缓存 tcm.clear(cache); } }public int update(MappedStatement ms, Object parameterObject) throws SQLException { // 清除全局缓存 flushCacheIfRequired(ms); // 执行代理对象更新方法 return delegate.update(ms, parameterObject); } private void flushCacheIfRequired(MappedStatement ms) { // 从MappedStatement中获取缓存 Cache cache = ms.getCache(); if (cache != null && ms.isFlushCacheRequired()) { // 清除缓存 tcm.clear(cache); } }
BaseExecutor#update会对局部缓存进行清除,然后调用实现类的doUpdate方法。
BaseExecutor#update和BaseExecutor#clearLocalCache:
public int update(MappedStatement ms, Object parameter) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}// 清除局部缓存clearLocalCache();// 执行实现类更新方法return doUpdate(ms, parameter);}public void clearLocalCache() {if (!closed) {localCache.clear();localOutputParameterCache.clear();}}public int update(MappedStatement ms, Object parameter) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } // 清除局部缓存 clearLocalCache(); // 执行实现类更新方法 return doUpdate(ms, parameter); } public void clearLocalCache() { if (!closed) { localCache.clear(); localOutputParameterCache.clear(); } }public int update(MappedStatement ms, Object parameter) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } // 清除局部缓存 clearLocalCache(); // 执行实现类更新方法 return doUpdate(ms, parameter); } public void clearLocalCache() { if (!closed) { localCache.clear(); localOutputParameterCache.clear(); } }
默认执行器实现类为SimpleExecutor,与doQuery方法类似,其doUpdate方法会执行如下逻辑:
- 创建StatementHandler
- 创建Statement(参数解析)
- 执行Statement
- 结果集封装
SimpleExecutor#doUpdate:
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();// 创建StatementHandlerStatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);// 创建Statement(参数解析)stmt = prepareStatement(handler, ms.getStatementLog());// 执行Statement&结果集封装return handler.update(stmt);} finally {closeStatement(stmt);}}public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); // 创建StatementHandler StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); // 创建Statement(参数解析) stmt = prepareStatement(handler, ms.getStatementLog()); // 执行Statement&结果集封装 return handler.update(stmt); } finally { closeStatement(stmt); } }public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); // 创建StatementHandler StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); // 创建Statement(参数解析) stmt = prepareStatement(handler, ms.getStatementLog()); // 执行Statement&结果集封装 return handler.update(stmt); } finally { closeStatement(stmt); } }
前2个步骤与doQuery方法相同,这里不再赘述,主要看后两个步骤,对应为StatementHandler#update方法,默认实现类为PreparedStatementHandler。
由于数据库insert/update/delete操作返回的都是影响条数,同查询相比,它们的结果集封装就显得十分简单:直接返回即可。
相反,由于数据库insert操作会新增主键,为了将主键值回显给入参对象,这里还需要进行特殊处理。
public int update(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;// 执行SQLps.execute();// 获取影响条数int rows = ps.getUpdateCount();// 回显主键值Object parameterObject = boundSql.getParameterObject();KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);return rows;}public int update(Statement statement) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; // 执行SQL ps.execute(); // 获取影响条数 int rows = ps.getUpdateCount(); // 回显主键值 Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject); return rows; }public int update(Statement statement) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; // 执行SQL ps.execute(); // 获取影响条数 int rows = ps.getUpdateCount(); // 回显主键值 Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject); return rows; }
如果没有设置回显主键(默认情况下),KeyGenerator实现类为NoKeyGenerator,其processAfter是个空方法:
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {// Do Nothing}public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) { // Do Nothing }public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) { // Do Nothing }
通过以下方式可以开启回显主键功能:
<insert id="createUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id"><!-- SQL --></insert><insert id="createUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id"> <!-- SQL --> </insert><insert id="createUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id"> <!-- SQL --> </insert>
此时默认KeyGenerator实现类为Jdbc3KeyGenerator,它会从结果集中获取对应数据库主键值(keyColumn),设置到入参对象的对应属性(keyProperty):
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {processBatch(ms, stmt, parameter);}public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {final String[] keyProperties = ms.getKeyProperties();if (keyProperties == null || keyProperties.length == 0) {return;}// 遍历结果集try (ResultSet rs = stmt.getGeneratedKeys()) {final ResultSetMetaData rsmd = rs.getMetaData();final Configuration configuration = ms.getConfiguration();if (rsmd.getColumnCount() < keyProperties.length) {// Error?} else {// 回显主键assignKeys(configuration, rs, rsmd, keyProperties, parameter);}} catch (Exception e) {throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);}}private void assignKeys(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties,Object parameter) throws SQLException {if (parameter instanceof ParamMap || parameter instanceof StrictMap) {// Multi-param or single param with @ParamassignKeysToParamMap(configuration, rs, rsmd, keyProperties, (Map<String, ?>) parameter);} else if (parameter instanceof ArrayList && !((ArrayList<?>) parameter).isEmpty()&& ((ArrayList<?>) parameter).get(0) instanceof ParamMap) {// Multi-param or single param with @Param in batch operationassignKeysToParamMapList(configuration, rs, rsmd, keyProperties, (ArrayList<ParamMap<?>>) parameter);} else {// Single param without @ParamassignKeysToParam(configuration, rs, rsmd, keyProperties, parameter);}}private void assignKeysToParam(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,String[] keyProperties, Object parameter) throws SQLException {Collection<?> params = collectionize(parameter);if (params.isEmpty()) {return;}List<KeyAssigner> assignerList = new ArrayList<>();for (int i = 0; i < keyProperties.length; i++) {assignerList.add(new KeyAssigner(configuration, rsmd, i + 1, null, keyProperties[i]));}Iterator<?> iterator = params.iterator();while (rs.next()) {if (!iterator.hasNext()) {throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, params.size()));}Object param = iterator.next();// 设值assignerList.forEach(x -> x.assign(rs, param));}}protected void assign(ResultSet rs, Object param) {if (paramName != null) {// If paramName is set, param is ParamMapparam = ((ParamMap<?>) param).get(paramName);}MetaObject metaParam = configuration.newMetaObject(param);try {if (typeHandler == null) {if (!metaParam.hasSetter(propertyName)) {throw new ExecutorException("No setter found for the keyProperty '" + propertyName + "' in '"+ metaParam.getOriginalObject().getClass().getName() + "'.");}Class<?> propertyType = metaParam.getSetterType(propertyName);typeHandler = typeHandlerRegistry.getTypeHandler(propertyType,JdbcType.forCode(rsmd.getColumnType(columnPosition)));}if (typeHandler == null) {// Error?} else {// 获取数据库主键值Object value = typeHandler.getResult(rs, columnPosition);// 设置入参metaParam.setValue(propertyName, value);}} catch (SQLException e) {throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e,e);}}public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) { processBatch(ms, stmt, parameter); } public void processBatch(MappedStatement ms, Statement stmt, Object parameter) { final String[] keyProperties = ms.getKeyProperties(); if (keyProperties == null || keyProperties.length == 0) { return; } // 遍历结果集 try (ResultSet rs = stmt.getGeneratedKeys()) { final ResultSetMetaData rsmd = rs.getMetaData(); final Configuration configuration = ms.getConfiguration(); if (rsmd.getColumnCount() < keyProperties.length) { // Error? } else { // 回显主键 assignKeys(configuration, rs, rsmd, keyProperties, parameter); } } catch (Exception e) { throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e); } } private void assignKeys(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties, Object parameter) throws SQLException { if (parameter instanceof ParamMap || parameter instanceof StrictMap) { // Multi-param or single param with @Param assignKeysToParamMap(configuration, rs, rsmd, keyProperties, (Map<String, ?>) parameter); } else if (parameter instanceof ArrayList && !((ArrayList<?>) parameter).isEmpty() && ((ArrayList<?>) parameter).get(0) instanceof ParamMap) { // Multi-param or single param with @Param in batch operation assignKeysToParamMapList(configuration, rs, rsmd, keyProperties, (ArrayList<ParamMap<?>>) parameter); } else { // Single param without @Param assignKeysToParam(configuration, rs, rsmd, keyProperties, parameter); } } private void assignKeysToParam(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties, Object parameter) throws SQLException { Collection<?> params = collectionize(parameter); if (params.isEmpty()) { return; } List<KeyAssigner> assignerList = new ArrayList<>(); for (int i = 0; i < keyProperties.length; i++) { assignerList.add(new KeyAssigner(configuration, rsmd, i + 1, null, keyProperties[i])); } Iterator<?> iterator = params.iterator(); while (rs.next()) { if (!iterator.hasNext()) { throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, params.size())); } Object param = iterator.next(); // 设值 assignerList.forEach(x -> x.assign(rs, param)); } } protected void assign(ResultSet rs, Object param) { if (paramName != null) { // If paramName is set, param is ParamMap param = ((ParamMap<?>) param).get(paramName); } MetaObject metaParam = configuration.newMetaObject(param); try { if (typeHandler == null) { if (!metaParam.hasSetter(propertyName)) { throw new ExecutorException("No setter found for the keyProperty '" + propertyName + "' in '" + metaParam.getOriginalObject().getClass().getName() + "'."); } Class<?> propertyType = metaParam.getSetterType(propertyName); typeHandler = typeHandlerRegistry.getTypeHandler(propertyType, JdbcType.forCode(rsmd.getColumnType(columnPosition))); } if (typeHandler == null) { // Error? } else { // 获取数据库主键值 Object value = typeHandler.getResult(rs, columnPosition); // 设置入参 metaParam.setValue(propertyName, value); } } catch (SQLException e) { throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e); } }public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) { processBatch(ms, stmt, parameter); } public void processBatch(MappedStatement ms, Statement stmt, Object parameter) { final String[] keyProperties = ms.getKeyProperties(); if (keyProperties == null || keyProperties.length == 0) { return; } // 遍历结果集 try (ResultSet rs = stmt.getGeneratedKeys()) { final ResultSetMetaData rsmd = rs.getMetaData(); final Configuration configuration = ms.getConfiguration(); if (rsmd.getColumnCount() < keyProperties.length) { // Error? } else { // 回显主键 assignKeys(configuration, rs, rsmd, keyProperties, parameter); } } catch (Exception e) { throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e); } } private void assignKeys(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties, Object parameter) throws SQLException { if (parameter instanceof ParamMap || parameter instanceof StrictMap) { // Multi-param or single param with @Param assignKeysToParamMap(configuration, rs, rsmd, keyProperties, (Map<String, ?>) parameter); } else if (parameter instanceof ArrayList && !((ArrayList<?>) parameter).isEmpty() && ((ArrayList<?>) parameter).get(0) instanceof ParamMap) { // Multi-param or single param with @Param in batch operation assignKeysToParamMapList(configuration, rs, rsmd, keyProperties, (ArrayList<ParamMap<?>>) parameter); } else { // Single param without @Param assignKeysToParam(configuration, rs, rsmd, keyProperties, parameter); } } private void assignKeysToParam(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties, Object parameter) throws SQLException { Collection<?> params = collectionize(parameter); if (params.isEmpty()) { return; } List<KeyAssigner> assignerList = new ArrayList<>(); for (int i = 0; i < keyProperties.length; i++) { assignerList.add(new KeyAssigner(configuration, rsmd, i + 1, null, keyProperties[i])); } Iterator<?> iterator = params.iterator(); while (rs.next()) { if (!iterator.hasNext()) { throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, params.size())); } Object param = iterator.next(); // 设值 assignerList.forEach(x -> x.assign(rs, param)); } } protected void assign(ResultSet rs, Object param) { if (paramName != null) { // If paramName is set, param is ParamMap param = ((ParamMap<?>) param).get(paramName); } MetaObject metaParam = configuration.newMetaObject(param); try { if (typeHandler == null) { if (!metaParam.hasSetter(propertyName)) { throw new ExecutorException("No setter found for the keyProperty '" + propertyName + "' in '" + metaParam.getOriginalObject().getClass().getName() + "'."); } Class<?> propertyType = metaParam.getSetterType(propertyName); typeHandler = typeHandlerRegistry.getTypeHandler(propertyType, JdbcType.forCode(rsmd.getColumnType(columnPosition))); } if (typeHandler == null) { // Error? } else { // 获取数据库主键值 Object value = typeHandler.getResult(rs, columnPosition); // 设置入参 metaParam.setValue(propertyName, value); } } catch (SQLException e) { throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e); } }
2.2 使用映射接口代理对象执行#
通过SqlSession#getMapper方法,可以获取映射接口的代理对象。
该代理对象实际上是从Configuration中获取的,Configuration又是从MapperRegistry中获取的,MapperRegistry会从缓存中获取MapperProxyFactory创建SqlSession的代理对象。
DefaultSqlSession#getMapper、Configuration#getMapper、MapperRegistry#getMapper和MapperProxyFactory#newInstance:
// DefaultSqlSessionpublic <T> T getMapper(Class<T> type) {return configuration.getMapper(type, this);}// Configurationpublic <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);}// MapperRegistrypublic <T> T getMapper(Class<T> type, SqlSession sqlSession) {// 获取缓存对应的MapperProxyFactoryfinal MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {// 创建代理对象return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}// MapperProxyFactorypublic T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}// DefaultSqlSession public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this); } // Configuration public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } // MapperRegistry public <T> T getMapper(Class<T> type, SqlSession sqlSession) { // 获取缓存对应的MapperProxyFactory final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { // 创建代理对象 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } // MapperProxyFactory public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }// DefaultSqlSession public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this); } // Configuration public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } // MapperRegistry public <T> T getMapper(Class<T> type, SqlSession sqlSession) { // 获取缓存对应的MapperProxyFactory final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { // 创建代理对象 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } // MapperProxyFactory public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
MapperProxyFactory使用JDK的Proxy创建代理对象,实际逻辑位于InvocationHandler实现类MapperProxy的invoke方法中,它会创建MapperMethodInvoker对象(PlainMethodInvoker)并执行其invoke方法:
// MapperProxypublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);}return cachedInvoker(method).invoke(proxy, method, args, sqlSession);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {try {return MapUtil.computeIfAbsent(methodCache, method, m -> {if (!m.isDefault()) {// 对于映射接口方法,会创建此对象return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));}try {if (privateLookupInMethod == null) {return new DefaultMethodInvoker(getMethodHandleJava8(method));} else {return new DefaultMethodInvoker(getMethodHandleJava9(method));}} catch (IllegalAccessException | InstantiationException | InvocationTargetException| NoSuchMethodException e) {throw new RuntimeException(e);}});} catch (RuntimeException re) {Throwable cause = re.getCause();throw cause == null ? re : cause;}}// PlainMethodInvokerpublic Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return mapperMethod.execute(sqlSession, args);}// MapperProxy public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } return cachedInvoker(method).invoke(proxy, method, args, sqlSession); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } private MapperMethodInvoker cachedInvoker(Method method) throws Throwable { try { return MapUtil.computeIfAbsent(methodCache, method, m -> { if (!m.isDefault()) { // 对于映射接口方法,会创建此对象 return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } try { if (privateLookupInMethod == null) { return new DefaultMethodInvoker(getMethodHandleJava8(method)); } else { return new DefaultMethodInvoker(getMethodHandleJava9(method)); } } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); } }); } catch (RuntimeException re) { Throwable cause = re.getCause(); throw cause == null ? re : cause; } } // PlainMethodInvoker public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { return mapperMethod.execute(sqlSession, args); }// MapperProxy public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } return cachedInvoker(method).invoke(proxy, method, args, sqlSession); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } private MapperMethodInvoker cachedInvoker(Method method) throws Throwable { try { return MapUtil.computeIfAbsent(methodCache, method, m -> { if (!m.isDefault()) { // 对于映射接口方法,会创建此对象 return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } try { if (privateLookupInMethod == null) { return new DefaultMethodInvoker(getMethodHandleJava8(method)); } else { return new DefaultMethodInvoker(getMethodHandleJava9(method)); } } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); } }); } catch (RuntimeException re) { Throwable cause = re.getCause(); throw cause == null ? re : cause; } } // PlainMethodInvoker public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable { return mapperMethod.execute(sqlSession, args); }
PlainMethodInvoker#invoke又会执行MapperMethod#execute方法,从该方法中可以明显看到,映射接口代理对象只不过是对SqlSession对象进行的封装,实际操作数据库还是得依靠SqlSession(此时又回到了2.1根据statementId执行的流程):
public Object execute(SqlSession sqlSession, Object[] args) {Object result;switch (command.getType()) {// 插入case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);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()) {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);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;}public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { // 插入 case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); 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()) { 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); 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; }public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { // 插入 case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); 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()) { 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); 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; }
还有个问题,MapperProxyFactory是什么时候生成的?实际上,在Mybatis启动时,通过MapperRegistry#addMapper方法就会创建MapperProxyFactory缓存(具体流程可以查看Mybatis如何添加映射接口和映射文件?):
public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {// 添加MapperProxyFactory缓存knownMappers.put(type, new MapperProxyFactory<>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { // 添加MapperProxyFactory缓存 knownMappers.put(type, new MapperProxyFactory<>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { // 添加MapperProxyFactory缓存 knownMappers.put(type, new MapperProxyFactory<>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
我们也可以发现,MapperProxyFactory缓存与MappedStatement并没有直接关系,它们之间需要SqlSession进行连接。即映射接口代理对象 -> SqlSession -> Configuration -> MapperRegistry -> 映射文件。
3 关闭SqlSession#
数据库连接是计算机网络资源,使用完需要及时关闭,避免资源泄露,造成服务器性能下降。
如果使用了数据库连接池,则需要清理本次会话的缓存数据,将连接放回连接池。
使用SqlSession#close()可以关闭本次会话连接,由于SqlSession实现了Closeable接口,通常使用以下方式自动关闭:
try (SqlSession session = sqlSessionFactory.openSession(true)) {// 执行SQL语句}try (SqlSession session = sqlSessionFactory.openSession(true)) { // 执行SQL语句 }try (SqlSession session = sqlSessionFactory.openSession(true)) { // 执行SQL语句 }
SqlSession#close()方法会本次会话的执行器Executor和游标Cursor,具体怎么关闭资源,实际上是由执行器和游标实现类自己去实现的。总体来说会完成一下工作:
- 清除会话缓存数据。
- 关闭数据库连接。如果使用连接池,则会本次连接放回连接池。
以下为相关源码。
SqlSession#close()关闭executor和cursor:
public void close() {try {executor.close(isCommitOrRollbackRequired(false));closeCursors();dirty = false;} finally {ErrorContext.instance().reset();}}public void close() { try { executor.close(isCommitOrRollbackRequired(false)); closeCursors(); dirty = false; } finally { ErrorContext.instance().reset(); } }public void close() { try { executor.close(isCommitOrRollbackRequired(false)); closeCursors(); dirty = false; } finally { ErrorContext.instance().reset(); } }
CachingExecutor#close()会回滚/提交本次会话的缓存,然后关闭代理执行器资源:
public void close(boolean forceRollback) {try {// issues #499, #524 and #573if (forceRollback) {tcm.rollback();} else {tcm.commit();}} finally {delegate.close(forceRollback);}}public void close(boolean forceRollback) { try { // issues #499, #524 and #573 if (forceRollback) { tcm.rollback(); } else { tcm.commit(); } } finally { delegate.close(forceRollback); } }public void close(boolean forceRollback) { try { // issues #499, #524 and #573 if (forceRollback) { tcm.rollback(); } else { tcm.commit(); } } finally { delegate.close(forceRollback); } }
BaseExecutor#close()会关闭事务,并清除本地缓存:
public void close(boolean forceRollback) {try {try {rollback(forceRollback);} finally {if (transaction != null) {transaction.close();}}} catch (SQLException e) {// Ignore. There's nothing that can be done at this point.log.warn("Unexpected exception on closing transaction. Cause: " + e);} finally {transaction = null;deferredLoads = null;localCache = null;localOutputParameterCache = null;closed = true;}}public void close(boolean forceRollback) { try { try { rollback(forceRollback); } finally { if (transaction != null) { transaction.close(); } } } catch (SQLException e) { // Ignore. There's nothing that can be done at this point. log.warn("Unexpected exception on closing transaction. Cause: " + e); } finally { transaction = null; deferredLoads = null; localCache = null; localOutputParameterCache = null; closed = true; } }public void close(boolean forceRollback) { try { try { rollback(forceRollback); } finally { if (transaction != null) { transaction.close(); } } } catch (SQLException e) { // Ignore. There's nothing that can be done at this point. log.warn("Unexpected exception on closing transaction. Cause: " + e); } finally { transaction = null; deferredLoads = null; localCache = null; localOutputParameterCache = null; closed = true; } }
Transaction#close()会关闭数据库连接,具体怎么实现交给子类决定。
例如,JdbcTransaction#Close()会直接关闭connection:
public void close() throws SQLException {if (connection != null) {resetAutoCommit();if (log.isDebugEnabled()) {log.debug("Closing JDBC Connection [" + connection + "]");}connection.close();}}public void close() throws SQLException { if (connection != null) { resetAutoCommit(); if (log.isDebugEnabled()) { log.debug("Closing JDBC Connection [" + connection + "]"); } connection.close(); } }public void close() throws SQLException { if (connection != null) { resetAutoCommit(); if (log.isDebugEnabled()) { log.debug("Closing JDBC Connection [" + connection + "]"); } connection.close(); } }
SqlSession#closeCursors()遍历关闭所有corsor:
private void closeCursors() {if (cursorList != null && !cursorList.isEmpty()) {for (Cursor<?> cursor : cursorList) {try {cursor.close();} catch (IOException e) {throw ExceptionFactory.wrapException("Error closing cursor. Cause: " + e, e);}}cursorList.clear();}}private void closeCursors() { if (cursorList != null && !cursorList.isEmpty()) { for (Cursor<?> cursor : cursorList) { try { cursor.close(); } catch (IOException e) { throw ExceptionFactory.wrapException("Error closing cursor. Cause: " + e, e); } } cursorList.clear(); } }private void closeCursors() { if (cursorList != null && !cursorList.isEmpty()) { for (Cursor<?> cursor : cursorList) { try { cursor.close(); } catch (IOException e) { throw ExceptionFactory.wrapException("Error closing cursor. Cause: " + e, e); } } cursorList.clear(); } }
DefaultCursor#close()关闭结果集ResultSet:
public void close() {if (isClosed()) {return;}ResultSet rs = rsw.getResultSet();try {if (rs != null) {rs.close();}} catch (SQLException e) {// ignore} finally {status = CursorStatus.CLOSED;}}public void close() { if (isClosed()) { return; } ResultSet rs = rsw.getResultSet(); try { if (rs != null) { rs.close(); } } catch (SQLException e) { // ignore } finally { status = CursorStatus.CLOSED; } }public void close() { if (isClosed()) { return; } ResultSet rs = rsw.getResultSet(); try { if (rs != null) { rs.close(); } } catch (SQLException e) { // ignore } finally { status = CursorStatus.CLOSED; } }