• 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  List 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  List 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 list = (List) 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  List 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 list;
      try {
        queryStack++;
        list = resultHandler == null ? (List) 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  List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
      List 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  List 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 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  List 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 handleResultSets(Statement stmt) throws SQLException {
      ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
    
      final List multipleResults = new ArrayList<>();
      int resultSetCount = 0;
      ResultSetWrapper rsw = getFirstResultSet(stmt);
    
      List 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 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 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 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">
      
    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) 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>) 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 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 getMapper(Class type) {
      return configuration.getMapper(type, this);
    }
    
    // Configuration
    public  T getMapper(Class type, SqlSession sqlSession) {
      return mapperRegistry.getMapper(type, sqlSession);
    }
    
    // MapperRegistry
    public  T getMapper(Class type, SqlSession sqlSession) {
      // 获取缓存对应的MapperProxyFactory
      final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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 mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
      return newInstance(mapperProxy);
    }
    protected T newInstance(MapperProxy 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  void addMapper(Class 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;
      }
    }
    
  • 相关阅读:
    【Focal Net】NeuralPS2022 论文+代码解读 Focal Modulation Networks
    [CMake] CMake 基础命令
    SpringBoot整合WebSocket【代码】
    【Linux】进程间通信介绍及匿名管道使用
    2022年0630-Com.Java.Basis第一课《Java的入门程序》
    前端 javascript 基础常见面试题
    Robot Framework结合Jenkins进行持续集成
    [附源码]计算机毕业设计JAVA乡村振兴惠农推介系统
    不太一样的Go Web框架—编程范式
    系统可用性:SRE口中的3个9,4个9...到底是个什么东西?
  • 原文地址:https://www.cnblogs.com/Xianhuii/p/17625685.html