Mybatis操作数据库流程源码

Java操作数据库需要经过3个大步骤:

  1. 获取数据库连接
  2. 执行SQL语句
  3. 关闭数据库连接

Mybatis将这几个步骤进行了封装,将获取数据库连接的给工作交给了SqlSessionFactory,将执行SQL的工作交给了SqlSession。

1 获取SqlSession

在程序启动时,会根据配置创建SqlSessionFactory:

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();   } } 

需要注意的是,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; } 

2 执行SQL语句

Mybatis提供了两种执行SQL的方式:

  1. 根据statementId执行
  2. 使用映射接口代理对象执行

2.1 根据statementId执行

SqlSession提供了selectXxx/insert/update/delete等方法操作数据库。

默认情况下,DefaultSqlSession会将selectXxx(除了selectCursor)方法统一交给selectList方法处理,将insert/update/delete方法统一交给update方法处理。它们的处理方式相同:

  1. 从Configuration中获取对应的MappedStatement
  2. 使用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();   } } 

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();   } } 

2.2.1 Executor#selectList

除了自定义Interceptor,首先会进入到CachingExecutor#query方法,它主要对全局缓存进行了处理:

  1. 根据查询条件(包括id、offset、limit、sql、查询参数和environmentId)创建CacheKey
  2. 获取MappedStatement的缓存对象Cache
  3. 从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); } 

之所以说CachingExecutor#query方法处理的是全局缓存,是因为这个缓存存在于MappedStatement,是所有会话共享的对象。

接着,会执行BaseExecutor#query方法,它主要对会话缓存进行处理:

  1. 从执行器localCache中获取查询条件对应的缓存
  2. 如果有缓存,直接返回;如果没有缓存,进一步查询数据库,并添加缓存

之所以说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; } 

具体查询数据库逻辑交给子类实现,默认SimpleExecutor执行步骤如下:

  1. 创建StatementHandler
  2. 创建Statement(参数解析)
  3. 执行Statement
  4. 结果集封装

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();     // 创建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) {   // 创建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);   // 创建Statement   stmt = handler.prepare(connection, transaction.getTimeout());   // 设置参数   handler.parameterize(stmt);   return stmt; } 

设置参数时,除了自定义Interceptor,默认会执行PreparedStatementHandler#parameterize方法,实际上会调用ParameterHandler(默认是DefaultParameterHandler)进行处理:

  1. 获取参数映射配置信息
  2. 遍历参数映射配置信息,使用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 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); } 

ResultSetHandler的默认实现类是DefaultResultSetHandler,处理步骤如下:

  1. 从MappedStatement获取结果集字段映射关系ResultMap
  2. 根据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);   } } 

结果集封装主要有两个步骤:

  1. 根据ResultMap初始化响应Java对象
  2. 根据的属性-列字段名映射关系,从结果集获取对应值,设置到响应对象中
  3. 将该行结果集封装的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; } 

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);   } } 

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();   } } 

默认执行器实现类为SimpleExecutor,与doQuery方法类似,其doUpdate方法会执行如下逻辑:

  1. 创建StatementHandler
  2. 创建Statement(参数解析)
  3. 执行Statement
  4. 结果集封装

SimpleExecutor#doUpdate:

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;   // 执行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 } 

通过以下方式可以开启回显主键功能:

<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 @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:

// 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方法:

// 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; } 

还有个问题,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);       }     }   } } 

我们也可以发现,MapperProxyFactory缓存与MappedStatement并没有直接关系,它们之间需要SqlSession进行连接。即映射接口代理对象 -> SqlSession -> Configuration -> MapperRegistry -> 映射文件。

3 关闭SqlSession

数据库连接是计算机网络资源,使用完需要及时关闭,避免资源泄露,造成服务器性能下降。

如果使用了数据库连接池,则需要清理本次会话的缓存数据,将连接放回连接池。

使用SqlSession#close()可以关闭本次会话连接,由于SqlSession实现了Closeable接口,通常使用以下方式自动关闭:

try (SqlSession session = sqlSessionFactory.openSession(true)) {   // 执行SQL语句 } 

SqlSession#close()方法会本次会话的执行器Executor和游标Cursor,具体怎么关闭资源,实际上是由执行器和游标实现类自己去实现的。总体来说会完成一下工作:

  1. 清除会话缓存数据。
  2. 关闭数据库连接。如果使用连接池,则会本次连接放回连接池。

以下为相关源码。

SqlSession#close()关闭executor和cursor:

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 #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;   } } 

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();   } } 

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();   } } 

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;   } } 

发表评论

评论已关闭。

相关文章