mybatis之SqlSession

.SqlSession

DefaultSqlSession核心字段的含义如下:

 // 配置对象
  private final Configuration configuration;
  // 底层依赖的Executor对象
  private final Executor executor;

  // 是否自动提交事务
  private final boolean autoCommit;
  // 当前缓存是否有脏数据
  private boolean dirty;
  // 为防止用户忘记关闭已打开的游标对象,会通过cursorList字段记录由该SqlSession对象生成的游标对象,
  // 在DefaultSqlSession.close()方法中会统一关闭这些游标对象
  private List<Cursor<?>> cursorList;

DefaultSqlSession中使用到了策略模式,DefaultSq!Session扮演了 Context 的角色,而将所有数据库相关的操作全部封装到Executor接口实现中,并通过executor字段选择不同的Executor实现。

DefaultSqlSession中实现了SqISession接口中定义的方法,并且为每种数据库操作提供了多个重载。重载方法最终都是通过调用Executor.query(MappedStatement, Object, RowBounds, ResultHandler)方法实现数据库查询操作的,但各自对结果对象进行相应的调整,例如selectOne()方法是从结果对象集合中获取了第一个元素返回:selectMap()方法会将List类型的结果对象集合转换成Map类型集合返回; select()方法是将结果对象集合交由用户指定的ResultHandler 象处理,且没有返回值;selectList()方法则是直接返回结果对象集合。

DefaultSqlSession.insert()方法、update()方法、delete()方法也有重载,它们最后是通过调用的 DefaultSqlSession.update(String, Object)方法实现的,该重载首先会将dirty字段置为true,然后再通过Executor.update()方法完成数据库修改操作。

DefaultSqlSession.commit()方法、rollback()方法以及close()方法都会调用Executor中相应的方法,其中就会涉及清空缓存的操作,之后dirty字段设置为 false。

上述的dirty字段主要在isCommitOrRollbackRequired()方法中,autoCommit字段以及用户传入的force参数共同决定是否提交/回滚事务,具体实现如下所示:

private boolean isCommitOrRollbackRequired(boolean force) {
    return (!autoCommit && dirty) || force;
  }
2.DefaultSqlSessionFactory

DefaultSqlSessionFactory是一个具体工厂类,实现了SqlSessionFactory接口。DefaultSqlSessionFactory主要提供了两种创建DefaultSqlSession的方式,一种方式通过数据源获取数据库连接,并创建Executor对象以及DefaultSqlSession 该方式的具体实现如下所示:

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 获取配置文件中的Environment对象
      final Environment environment = configuration.getEnvironment();
      // 获取TransactionFactory对象
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 创建Transaction对象
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 根据配置创建Executor对象
      final Executor executor = configuration.newExecutor(tx, execType);
      // 创建DefaultSqlSession对象
      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();
    }
  }

另一种方式是用户提供数据库连接对象DefaultSqlSessionFactory使用数据库连接对象创建Executor对象以及DefaultSqlSession对象,具体实现如下:

private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
    try {
      boolean autoCommit;
      try {
        // 获取当前连接的事务是否为自动提交方式
        autoCommit = connection.getAutoCommit();
      } catch (SQLException e) {
        // Failover to true, as most poor drivers
        // or databases won't support transactions
        // 当前数据库驱动提供的连接不支持事务,则可能会抛 异常
        autoCommit = true;
      }
      // 获取配置文件中的Environment对象
      final Environment environment = configuration.getEnvironment();
      // 获取TransactionFactory对象
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 创建Transaction对象
      final Transaction tx = transactionFactory.newTransaction(connection);
      // 根据配置创建Executor对象
      final Executor executor = configuration.newExecutor(tx, execType);
      // 创建DefaultSqlSession对象
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

DefaultSqlSessionFactory提供的所有openSession()方法重载都是基于上述两种方式创建DefaultSqlSession对象的。这里不再赘述。

3.SqlSessionManager

SqlSessionManager同时实现 SqlSession接口和SqlSessionFactory接口 ,也就同时提供了SqlSessionFactory创建SqlSession以及SqlSession操纵数据库的功能。

SqISessionManager 各个字段的含义如下:

 // 底层封装的SqlSessionFactory对象
  private final SqlSessionFactory sqlSessionFactory;
  // localSqlSession中记录的SqlSession对象的代理对象
  private final SqlSession sqlSessionProxy;
  // ThreadLocal变量,记录一个与当前线程绑定的SqlSession对象
  private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();

SqlSessionManager和DefaultSqlSessionFactory的主要不同点是SqlSessionManager提供了两种模式:一种模式与DefaultSqlSessionFactory的行为相同,同一线程每次通过SqlSessionManager对象访问数据库时,都会创建新的DefaultSession对象完成数据库操作;第二种模式是SqlSessionManager通过localSqlSession这个ThreadLocal,记录与当前线程绑定的SqlSession对象,供当前线程循环使用,从而避免在同一线程多次创建SqlSession对象带来的性能损失。

首先看SqlSessionManager的构造方法,其构造方法都是私有的,如果要创建SqlSessionManager对象 需要调用其newInstance()方法。

 private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
    this.sqlSessionFactory = sqlSessionFactory;
    // 使用动态代理的方式生成SqlSession的代理对象
    this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[]{SqlSession.class},
        new SqlSessionInterceptor());
  }

  public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) {
    return new SqlSessionManager(sqlSessionFactory);
  }

SqlSessionManager.openSession()方法以及其重载是直接通过调用其中底层封装的SqlSessionFactory.openSession方法来创建SqISession对象的。

SqlSessionManager中实现的SqlSession接口方法,例如select*()、update()等,都是直接调用sqlSessionProxy字段记录的SqlSession代理对象的相应方法实现的。在创建该代理对象时使用的InvocationHandler对象是SqlSessionlnterceptor对象,它是定义在SqISessionManager中的内部类,invoke()方法实现如下:

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 获取当前线程绑定的SqlSession对象
      final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
      if (sqlSession != null) {
        try {
          // 调用真正的SqlSession对象,完成数据库的相关操作
          return method.invoke(sqlSession, args);
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
      } else {
        try (SqlSession autoSqlSession = openSession()) {
          try {
            // 通过新建SqlSession对象完成数据库操作
            final Object result = method.invoke(autoSqlSession, args);
            // 提交事务
            autoSqlSession.commit();
            return result;
          } catch (Throwable t) {
            // /回滚事务
            autoSqlSession.rollback();
            throw ExceptionUtil.unwrapThrowable(t);
          }
        }
      }
    }

通过对SqlSessionInterceptor的分析可知,第一种模式中新建的SqlSession在使用完成后会关闭。在第二种模式中 与当前线程绑定的SqlSession对象需要先通过SqlSessionManager.startManagedSession()方法进行设置,具体实现如下:

public void startManagedSession() {
  this.localSqlSession.set(openSession());
}

当需要提交/回滚事务或是关闭IocalSqlSession中记录的SqlSession对象时,需要通过SqlSessionManager.commit()、rollback()以及close()方法完成,其中会先检测当前线程是否绑定SqlSession对象,如果未绑定则抛出异常 ,如果绑定了则调用该SqlSession对象的相应方法。

你可能感兴趣的:(Mybatis)