Mybatis源码笔记(五)、ResultSetHandler

StatementHandler 返回的结果主要是通过 ResultSetHandler 接口来处理,BaseStatementHandler 的构造方法中有如下代码初始化 StatementHandler,返回的是 StatementHandler 的实现类

 this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);```
 通过configration的newResultSetHandler返回ResultSetHandler对象。

   public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
        // 这里返回的是DefaultResultSetHandler对象
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }
   // DefaultResultSetHandler实现了ResultSetHandler
  public class DefaultResultSetHandler implements ResultSetHandler {
    ......
  }

ResultSetHandler 接口的方法如下

public interface ResultSetHandler {

  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

我们来开下 DefaultResultSetHandler 的具体实现

  @Override
  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);

// 从mappedStatement获取解析出的ResultMaps
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    // 判断是否设置了resultMap或resultSet
    validateResultMapsCount(rsw, resultMapCount);
    // 处理返回结果resultMap
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    // 通过mappedStatement获取多结果集数组对象
    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 {
        // 如果resultHandler为空,构建一个对象去处理
        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 {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }

  // 通过ResultHandler处理result
  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);
    }
  }


    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);
        //通过resultHandler操作存储结果
      storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
  }
// 行记录的处理
  private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    //  创建返回的result映射对象
    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 void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
    if (parentMapping != null) {
      linkToParents(rs, parentMapping, rowValue);
    } else {
      callResultHandler(resultHandler, resultContext, rowValue);
    }
  }
// resultHandler处理结果
  @SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
  private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
    resultContext.nextResultObject(rowValue);
    ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
  }

至此,我们可以清晰地看到 ResultSetHandler 根据 mappedStatement 中设置的 ResultMap来处理 statement 执行返回的结果,处理映射的逻辑写的还是很精彩的,考虑的比较全面。

下一节,我们将看下 ResultHandler 是如何处理存储返回的 rowValue 的。

Mybatis源码笔记(四)、StatementHandler

在上一节中我们看到,Statement 处理主要由 StatementHandler 接口实现,StatementHandler 由 configuration.newStatementHandler 创建

 StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);


public StatementHandler newStatementHandler(Executor executor,
MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

//   根据 不同的类型创建不同的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());
    }

  }

我们看一下 StatementHandler 接口的方法

public interface StatementHandler {

Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;

void parameterize(Statement statement)
throws SQLException;

void batch(Statement statement)
throws SQLException;

int update(Statement statement)
throws SQLException;

<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;

<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;

BoundSql getBoundSql();

ParameterHandler getParameterHandler();

}

我们选取其中的 SimpleStatementHandler 来具体分析下,SimpleStatementHandler 继承了 BaseStatementHandler,BaseStatementHandler 实现了 StatementHandler 接口


public abstract class BaseStatementHandler implements StatementHandler { // 配置 protected final Configuration configuration; protected final ObjectFactory objectFactory; protected final TypeHandlerRegistry typeHandlerRegistry; //结果处理 protected final ResultSetHandler resultSetHandler; //参数处理 protected final ParameterHandler parameterHandler; // 具体的执行器 protected final Executor executor; // mappedStatement protected final MappedStatement mappedStatement; ...... } // 继承实现类SimpleStatementHandler public class SimpleStatementHandler extends BaseStatementHandler { ...... @Override public int update(Statement statement) throws SQLException { // 执行SQL String sql = boundSql.getSql(); // SQL param Object parameterObject = boundSql.getParameterObject(); // 主键生成方式 KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); int rows; // 根据不同的keyGenerator,传入 不同的参数执行,最后返回结果 if (keyGenerator instanceof Jdbc3KeyGenerator) { // 很熟悉的味道 statement.execute(sql, Statement.RETURN_GENERATED_KEYS); rows = statement.getUpdateCount(); keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else if (keyGenerator instanceof SelectKeyGenerator) { statement.execute(sql); rows = statement.getUpdateCount(); keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else { statement.execute(sql); rows = statement.getUpdateCount(); } // 返回结果 return rows; } ...... }

上面的 update 方法我们终于找到了熟悉的东西,JDBC 的 statement 执行,至此,一切都清楚了。其实万变不离其宗,只是框架让我们可以脱离一些低级、重复劳动,优化性能,比我们自己徒手实现的要好。但是归根结底还是基础,所以基础还是要打牢的。

Mybatis源码笔记(三)、Executor

闲言少叙,书接上文,上一节我们读到 session 的执行,这一节我们来看下 Executor。Executor 也是一个接口,我们来大致看一下这个接口

public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;

int update(MappedStatement ms, Object parameter) throws SQLException;

<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;

List<BatchResult> flushStatements() throws SQLException;

void commit(boolean required) throws SQLException;

void rollback(boolean required) throws SQLException;

CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);

boolean isCached(MappedStatement ms, CacheKey key);

void clearLocalCache();

void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);

Transaction getTransaction();

void close(boolean forceRollback);

boolean isClosed();

void setExecutorWrapper(Executor executor);
}

在前面章节的 openSession 中我们介绍了 Executor 的创建,主要包含如下三种 Executor,分别是批量 BatchExecutor、复用 ReuseExecutor、以及简单 SimpleExecutor,默认是 SimpleExecutor。

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

我们可以发现,这三种 Executor 都继承了抽象类 BaseExecutor

public class BatchExecutor extends BaseExecutor {
 ......
}
public class ReuseExecutor extends BaseExecutor {
 ......
}
public class SimpleExecutor extends BaseExecutor {
 ......
}

我们继续追踪到 Executor,我们看看上一节的 update 实现

public abstract class BaseExecutor implements Executor {
......
// Executor的wrapper对象,如果开启缓存,那么会创建Executor的代理CachingExecutor,
// 通过delegate.setExecutorWrapper(this)将CachingExecutor设置进来,后期的缓存操作就委托给了子类,
// 由于mybatis本身的实现问题,在分布式环境下有可能会存在脏数据问题,所以开启缓存是要注意
  protected Executor wrapper;
 ......


  @Override
  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();
    // 最终进入到doUpdate方法
    return doUpdate(ms, parameter);
  }

// 抽象方法,具体的继承类实现
    protected abstract int doUpdate(MappedStatement ms, Object parameter)
      throws SQLException;

}

上述代码可以看到,具体的操作其实是委托给子类进行处理的。看一下具体的实现

public class SimpleExecutor extends BaseExecutor {

  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    // 调用父类构造函数
    super(configuration, transaction);
  }

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
    // 获取配置
      Configuration configuration = ms.getConfiguration();
    //   获取handler
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
    //   交由handler执行
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }
  ......
}

总结:

这一节我们主要看了 Executor 极其实现,鉴于篇幅,我们选取了其中一个方法来说明,其实还是很简单的。下一节我们将看下 Statement 是如何执行的。

Mybatis源码笔记(二)、SqlSession

书接前文,我们这一节来看看 SqlSession。


// SqlSession 是 mybatis 主要的接口之一,通过这个接口, // 我们可以实现执行命令,获取 mappers,以及事务的管理 public interface SqlSession extends Closeable { ...... }

该接口的方法大致如下,有了这些方法,我们就可以进行各种操作了
sqlSession
上一节中,我们通过 SqlSessionFactoryBean 的 buildSqlSessionFactory 方法获得到了 DefaultSqlSessionFactory。下面我们来看下是如何通过 SqlSessionFactory 获得 SqlSession 的,我们取其中的一个例子。


@Override public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // 获取Environment,Environment对象中有TransactionFactory和DataSource对象, // 使用经典的建造者模式,通过链式调用返回创建。 final Environment environment = configuration.getEnvironment(); // 通过environment 获取transactionFactory,来创建transaction, // 这里的autoCommit为false,即默认不自动提交。 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 根据不同的执行类型获取Executor执行器 final Executor executor = configuration.newExecutor(tx, execType); // 这一步,创建了SqlSession的一个实现类DefaultSqlSession, // 这样我们就获取到了SqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { // 异常了关闭Transaction closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } // 这个关闭很简单,就是异常了如果Transaction对象不为空,关闭,做垃圾清理 private void closeTransaction(Transaction tx) { if (tx != null) { try { tx.close(); } catch (SQLException ignore) { // Intentionally ignore. Prefer previous error. } } }

好了,我么来看下 SqlSession 的实现

public class DefaultSqlSession implements SqlSession {
// 获取配置
  private final Configuration configuration;
  // 这个是核心,后面的执行都是他来做的。
  private final Executor executor;

  private final boolean autoCommit;
  // 脏数据标记
  private boolean dirty;
  private List<Cursor<?>> cursorList;

  public DefaultSqlSession(Configuration configuration,
  Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }
  ......
}

题外话,有时候我们需要最一些特殊的操作,比如我们根据业务要求要进行大量数据的批量提交,并包含一定的逻辑,这时候我们想获得 Connection,怎么做呢?
这时候我们可以通过 DefaultSqlSession 的 getConnection 方法获取,但是需要注意的是,这个时候我们要控制好事务,这一点尤为重要。

  @Override
  public Connection getConnection() {
    try {
      return executor.getTransaction().getConnection();
    } catch (SQLException e) {
      throw ExceptionFactory.wrapException("Error getting a new connection.  Cause: " + e, e);
    }
  }

接下来,我们以其中一个方法为突破口,来分析一下,不失一般性,我们以 insert 为例

  @Override
  public int insert(String statement, Object parameter) {
    return update(statement, parameter);
  }


  @Override
  public int update(String statement, Object parameter) {
    try {
        // 脏数据标记
      dirty = true;
    //   在SqlSessionFactoty中,我们加载MapperLocation的mapper,
    // 通过parse方法,解析到Configuration对象中。
    // 这里再通过statement这个key从mappedStatements这个map中把它取出来
      MappedStatement ms = configuration.getMappedStatement(statement);
    // 通过DefaultSqlSession的executor执行相应的方法,这里的executor也是一个接口,
    // 在前面openSession中我们介绍了,它根据不同的执行类型创建对应的实现
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

总结:

总体来说,session 的构建还是很简单的,需要注意的是,采用面向接口编程,需要根据不同的参数实例化选择不同实现对象,比较绕。通过 SqlSession 获取 session,session 里包含执行代码需要的其他对象,这些对象根据实际需要构建,最后统一进入到 executor 中去执行。下一节,我们将分析一下 Executor

Mybatis源码笔记(一)、SqlSessionFactory

MyBatis 是基于 Java 的数据持久层框架,主要解决数据和对象的映射关系。相较于 Hibernate,因为其简单轻量,所以使用也比较广泛,是主流的 ORM 框架之一。与 hibernate 相比,mybatis 不能完全算得上是一个完全的 ORM 框架。因为他只完成了一半。不过话说回来,之所以大家喜欢用它,很大一部分原因就是基于它的轻量便捷。

闲言少叙,开刀问斩。首先我们谈一谈传统方式是怎样使用的。传统意义上讲,连接数据库的方式比较常用 JDBC 大致如下:

        ......
        try {
            // 加载驱动
            Class.forName(driver);
            // 获取connection连接
            Connection conn = DriverManager.getConnection(url,user,password);
            // 获取statement对象
            Statement statement = conn.createStatement();
            try {
                // 控制事务提交
                conn.setAutoCommit(false);
                // 执行SQL语句
                statement.executeUpdate("UPDATE USER SET AGE=1 WHERE ID=1");
                // 事务提交
                conn.commit();
            } catch (Exception e) {
                // 异常回滚
                conn.rollback();
                throw e;
            } finally {
                // 关闭statement
                if (statement != null) {
                    statement.close();
                    statement = null;
                }
                // 关闭connection
                if (conn != null) {
                    conn.close();
                    conn = null;
                }
            }

相较于传统的 JDBC 方式,我们使用 mybatis 略有不同,不过本质还是一样使用 JDBC。使用 mybatis 我们不在手动获取 Connection 连接,而是将它交由 mybatis 来管理。在 mybatis 中使用 SqlSession 来管理资源的连接与关闭,同时通过 SqlSession 来进行各种增删改查操作。

具体如何实现呢,我们需要使用 SqlSessionFactory 来管理 SqlSession,SqlSessionFactory 接口主要是用于打开 session 获取 SqlSession 对象的,具体的实现方法在实现类对象中实现


// SqlSessionFactory接口 public interface SqlSessionFactory { SqlSession openSession(); SqlSession openSession(boolean autoCommit); SqlSession openSession(Connection connection); SqlSession openSession(TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType); SqlSession openSession(ExecutorType execType, boolean autoCommit); SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); SqlSession openSession(ExecutorType execType, Connection connection); Configuration getConfiguration(); }

那 SqlSession 究竟是如创建的呢?我们以 spring 容器管理为例,通过该 SqlSessionFactoryBean 创建 SqlSessionFactory,通过 SqlSessionFactory 获取 SqlSession:

public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean,
ApplicationListener<ApplicationEvent> {

实现了 InitializingBean 接口,追踪到 afterPropertiesSet()方法

  @Override
  public void afterPropertiesSet() throws Exception {
    ......
    this.sqlSessionFactory = buildSqlSessionFactory();
  }

这里的 sqlSessionFactory 通过 buildSqlSessionFactory 方法创建

  protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

    final Configuration targetConfiguration;

    XMLConfigBuilder xmlConfigBuilder = null;
// configuration不为空,直接加载
    if (this.configuration != null) {
      targetConfiguration = this.configuration;
      if (targetConfiguration.getVariables() == null) {
        targetConfiguration.setVariables(this.configurationProperties);
      } else if (this.configurationProperties != null) {
        targetConfiguration.getVariables().putAll(this.configurationProperties);
      }
//   否则通过configLocation加载
    } else if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      targetConfiguration = xmlConfigBuilder.getConfiguration();
//   以上均不能加载,则默认new一个Configuration对象塞进去
    } else {
      LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
      targetConfiguration = new Configuration();
      Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
    }

    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
// 注册类型别名包
    if (hasLength(this.typeAliasesPackage)) {
      scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType)
          .forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
    }
// 注册类型别名
    if (!isEmpty(this.typeAliases)) {
      Stream.of(this.typeAliases).forEach(typeAlias -> {
        targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
        LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
      });
    }
// 注册各种插件
    if (!isEmpty(this.plugins)) {
      Stream.of(this.plugins).forEach(plugin -> {
        targetConfiguration.addInterceptor(plugin);
        LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
      });
    }
// 注册类型处理包以及类型处理器
    if (hasLength(this.typeHandlersPackage)) {
      scanClasses(this.typeHandlersPackage, TypeHandler.class).stream()
          .filter(clazz -> !clazz.isInterface())
          .filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
          .filter(clazz -> ClassUtils.getConstructorIfAvailable(clazz) != null)
          .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
    }

    if (!isEmpty(this.typeHandlers)) {
      Stream.of(this.typeHandlers).forEach(typeHandler -> {
        targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
        LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
      });
    }
// 根据databaseIdProvider获取数据库类型
    if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
      try {
        targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();
        LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }
// 注册事务
    targetConfiguration.setEnvironment(new Environment(this.environment,
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
        this.dataSource));
// 通过mapperLocations加载**.Mapper.xml文件,解析mapper文件存入Configuration对象的属性中
    if (this.mapperLocations != null) {
      if (this.mapperLocations.length == 0) {
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
      } else {
        for (Resource mapperLocation : this.mapperLocations) {
          if (mapperLocation == null) {
            continue;
          }
          try {
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
            xmlMapperBuilder.parse();
          } catch (Exception e) {
            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
          } finally {
            ErrorContext.instance().reset();
          }
          LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }
// 通过上面的配置创建SqlSessionFactory
    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
  }
// 这里返回的是SqlSessionFactory默认实现类DefaultSqlSessionFactory,通过SqlSessionFactory我们就可以拿到sqlSession了
    public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

总结:

如果我们自己在单个项目中使用 mybatis 也是很简单的,通过上面的代码可以看到,我们无非就是想获取一个 SqlSessionFactory,通过 SqlSessionFactory 进一步获取 SqlSession,从而执行具体的增删改查。所以我们可以按照上述思路,构建好 Configuration 对象,直接 new 出 SqlSessionFactory。下一节,我们将分析一下 SqlSession。

JVM异常诊断(二)、脚本

在上文介绍的基础上,我们写了个简单的小脚本,大致步骤如下

    1.通过名称获取进程Id
    2.循环获取进程内线程的执行情况
    3.筛选出耗用大于30%的线程
    4.获取线程栈信息
#!/bin/bash
#进程ID
processName = ''
x=  ps -ef | grep ${processName} | grep -v grep| awk '{print $2}';
#echo $x;
#循环五次,记录每次的前20名线程cpu耗用
for i in {1..5};do

    y= ps -ef | grep  ${processName} | grep -v grep| awk '{print $2}'|xargs top -b -n 1 -H -p |grep PID -A 20|awk  '{print $1"\t"$9"\t"$11}'> stack.res;
    #echo | awk  '{print z}' z="$y";

#将线程ID 和 cpu耗用时间存在数组中
    PIDArr=($(awk '{print $1}' stack.res));
    CPUArr=($(awk '{print $2}' stack.res));
    MEMArr=($(awk '{print $3}' stack.res)); 

#选出每次top出的大于30的线程
    len=${#CPUArr[@]}
    for((i=1;i<len;++i))
    do

         c=`echo "${CPUArr[$i]}"|awk 'BEGIN{FS="."} {print $1}'` 
        if [ $c -le 10  ]; then
               unset CPUArr[$i]
               unset PIDArr[$i]
               unset MEMArr[$i]
        fi
    done
#打印出结果

    echo ${PIDArr[@]}
    echo ${CPUArr[@]}
    echo ${MEMArr[@]}
    echo ""
    sleep 1

done;
 ps -ef | grep  ${processName}| grep -v grep| awk '{print $2"\t"$1}' > pid.res
 #PID = ($(awk '{print $1}' pid.res));
 #echo $PID
#打印出最终线程栈
len=${#PIDArr[@]}
for((i=1;i<len;++i))
do
    d=`echo "${PIDArr[$i]}"|awk '{printf("%x\n",$0)}'`
    echo ${PIDArr[$i]};
    echo $d
    /usr/java/jdk1.7.0_55/bin/jstack $(awk '{print $1}' pid.res)|grep $d -A 50  
done

JVM异常诊断(一)、工具

作为一个高级Java开发人员,经常会碰到一些jvm的异常问题。譬如一些JVM引起的进程内存耗用过高,CPU负载高,等等。那么遇到这些问题应该怎么解决呢?接下来我们将就此问题展开:
首先,我们需要找出资源耗用异常的进程,我们以Linux为例,执行top命令:

    1.top 


假设我们获取到第一个进程的资源耗用异常,紧接着我们需要找到进程内资源耗用异常的线程,我们可以执行命令:

    2.top -H PID


拿到异常线程id之后,我们需要找到JVM线程栈里的信息,因为在线程栈中,线程id是以十六进制存在的,所以,我们需要把它转成十六进制:

    3.printf "%x\n" TID

之后,我们就可以通过线程栈信息获得线程内执行的内容:

    4.jstack PID|grep TID


我们也可把全部的线程栈打印出来,通过ha工具分析

    jstack PID > <filename>

接下来就很简单了,我们只要分析一下线程栈里的代码就能定位到问题了。十有八九是我们自己写的代码有bug。

这里还有一套组合拳,可以定位出进程内线程资源耗用排行

    ps -mp PID -o THREAD,tid,time | sort -rn

有时候通过线程栈不能很好的显示出问题,我们也可从侧面看看JVM的配置是否合理:

    jinfo -flags PID

我们还可以通过jstat看看GC的情况,

     jstat -gcutil pid 

有时候jstat并不能很好的显示gc信息,我们需要打印gc日志,需要添加jvm启动参数:

     -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:<file-path>

还有一些方法,譬如说,我们想查看某一线程的系统调用,从系统调用中找出一些端倪:

     strace -p tid 

我们还可以通过jmap命令打出线程的内存信息:

     jmap -dump:format=b,file=<filename> PID

根据经验来看,一般来说资源耗用异常十有八九是我们自己的代码问题。
最后,介绍两个耗用的工具,一个是堆栈查看工具,一个是内存查看工具:ha和jca

ha456.jar下载

jca457.jar下载