SqlSession 是 MyBatis 核心接口之一,也是 MyBatis 接口层的主要组成部分,对外提供 MyBatis 常用的 API。mybatis 提供了两个 SqlSession 接口的实现,分别为 DefaultSqlSession、SqlSessionManager,其中最常用的是 DefaultSqlSession。另外,跟前面分析过的源码 mybatis 的源码一样,mybatis 也为 SqlSession 提供了相应的工厂接口 SqlSessionFactory,及实现该接口的实现 DefaultSqlSessionFactory(SqlSessionManager 同时实现了 SqlSession 和 SqlSessionFactory 接口)。

1 SqlSession

在 SqlSession 中定义了常用的数据库操作以及事务的相关操作,为了方便用户使用,每种类型的操作都提供了多种重载。

  1. public interface SqlSession extends Closeable {
  2. // 泛型方法,参数是要执行查询的sql语句,返回值为查询的结果对象
  3. <T> T selectOne(String statement);
  4. // 第二个参数表示 需要用户传入的实参,即 sql语句绑定的实参
  5. <T> T selectOne(String statement, Object parameter);
  6. // 查询结果有多条记录,会封装成 结果对象列表 并返回
  7. <E> List<E> selectList(String statement);
  8. // 参数 + 多记录结果集
  9. <E> List<E> selectList(String statement, Object parameter);
  10. // 参数RowBounds主要用于逻辑分页,逻辑分页会将所有的结果都查询到,
  11. // 然后根据RowBounds中提供的offset和limit值来获取最后的结果
  12. <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
  13. // mapKey表示将结果集中的哪一列(如 主键列或编码列)作为Map的key,
  14. // value则为列值 对应的那条记录
  15. <K, V> Map<K, V> selectMap(String statement, String mapKey);
  16. // 多了个parameter参数,其它与上面相同
  17. <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
  18. // 多了个RowBounds参数,其它与上面相同
  19. <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
  20. // 除了返回值是Cursor对象,其它与selectList相同
  21. <T> Cursor<T> selectCursor(String statement);
  22. <T> Cursor<T> selectCursor(String statement, Object parameter);
  23. <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
  24. // 查询出的结果集 将由传入的ResultHandler对象处理,其它与selectList相同
  25. void select(String statement, Object parameter, ResultHandler handler);
  26. void select(String statement, ResultHandler handler);
  27. void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
  28. // 执行insert语句
  29. int insert(String statement);
  30. int insert(String statement, Object parameter);
  31. // 执行update语句
  32. int update(String statement);
  33. int update(String statement, Object parameter);
  34. // 执行delete语句
  35. int delete(String statement);
  36. int delete(String statement, Object parameter);
  37. // 提交事务
  38. void commit();
  39. void commit(boolean force);
  40. // 回滚事务
  41. void rollback();
  42. void rollback(boolean force);
  43. // 将对数据库的操作请求 刷到数据库
  44. List<BatchResult> flushStatements();
  45. // 关闭当前session
  46. void close();
  47. // 清空缓存
  48. void clearCache();
  49. // 获取Configuration对象
  50. Configuration getConfiguration();
  51. // 获取type对应的Mapper对象
  52. <T> T getMapper(Class<T> type);
  53. // 获取该SqlSession对应的数据库连接
  54. Connection getConnection();
  55. }

1.1 DefaultSqlSession

DefaultSqlSession 是单独使用 MyBatis 进行开发时,最常用的 SqISession 接口实现。其实现了 SqISession 接口中定义的方法,及各方法的重载。select()系列方法、selectOne()系列方法、selectList()系列方法、selectMap()系列方法之间的调用关系如下图,殊途同归,它们最终都会调用 Executor 的 query()方法。

avatar

上述重载方法最终都是通过调用 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 参数共同决定是否提交/回滚事务。该方法的返回值将作为 Executor 的 commit()方法和 rollback()方法的参数。

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

2 SqlSessionFactory

SqlSessionFactory 负责创建 SqlSession 对象,其中包含了多个 openSession()方法的重载,可以通过其参数指定事务的隔离级别、底层使用 Executor 的类型、以及是否自动提交事务等方面的配置。

  1. public interface SqlSessionFactory {
  2. // 提供了openSession()方法的多种重载,根据相应的参数 可以指定事务的隔离级别、
  3. // 底层使用的Executor类型、以及是否自动提交事务等配置
  4. SqlSession openSession();
  5. SqlSession openSession(boolean autoCommit);
  6. SqlSession openSession(Connection connection);
  7. SqlSession openSession(TransactionIsolationLevel level);
  8. SqlSession openSession(ExecutorType execType);
  9. SqlSession openSession(ExecutorType execType, boolean autoCommit);
  10. SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  11. SqlSession openSession(ExecutorType execType, Connection connection);
  12. Configuration getConfiguration();
  13. }

2.1 DefaultSqlSessionFactory

DefaultSqlSessionFactory 是 SqlSessionFactory 接口的默认实现,主要提供了两种创建 DefaultSqlSession 对象的方式,一种方式是通过数据源获取数据库连接,并创建 Executor 对象以及 DefaultSqlSession 对象;另一种方式是用户提供数据库连接对象,DefaultSqlSessionFactory 根据该数据库连接对象获取 autoCommit 属性,创建 Executor 对象以及 DefaultSqlSession 对象。

DefaultSqISessionFactory 提供的所有 openSession()方法重载都是基于上述两种方式创建 DefaultSqlSession 对象的。

  1. public class DefaultSqlSessionFactory implements SqlSessionFactory {
  2. private final Configuration configuration;
  3. public DefaultSqlSessionFactory(Configuration configuration) {
  4. this.configuration = configuration;
  5. }
  6. private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  7. Transaction tx = null;
  8. try {
  9. // 获取配置的Environment对象
  10. final Environment environment = configuration.getEnvironment();
  11. // 从environment中获取TransactionFactory对象,如果没有,就创建一个ManagedTransactionFactory实例并返回
  12. final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
  13. // 从事务工厂中获取一个事务对象
  14. tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
  15. // 根据事务对象tx和配置的Executor类型execType创建Executor实例
  16. // ExecutorType是个枚举类型,有三个值 SIMPLE, REUSE, BATCH,分别对应了
  17. // SimpleExecutor、ReuseExecutor、BatchExecutor
  18. final Executor executor = configuration.newExecutor(tx, execType);
  19. // 创建DefaultSqlSession对象
  20. return new DefaultSqlSession(configuration, executor, autoCommit);
  21. } catch (Exception e) {
  22. closeTransaction(tx); // may have fetched a connection so lets call close()
  23. throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
  24. } finally {
  25. ErrorContext.instance().reset();
  26. }
  27. }
  28. private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
  29. try {
  30. boolean autoCommit;
  31. try {
  32. // 根据当前连接对象获取autoCommit属性(是否自动提交事务)
  33. autoCommit = connection.getAutoCommit();
  34. } catch (SQLException e) {
  35. autoCommit = true;
  36. }
  37. // 除了获取autoCommit属性的方式和上面不一样外,下面的处理都与上面完全相同
  38. final Environment environment = configuration.getEnvironment();
  39. final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
  40. final Transaction tx = transactionFactory.newTransaction(connection);
  41. final Executor executor = configuration.newExecutor(tx, execType);
  42. return new DefaultSqlSession(configuration, executor, autoCommit);
  43. } catch (Exception e) {
  44. throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
  45. } finally {
  46. ErrorContext.instance().reset();
  47. }
  48. }
  49. private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
  50. if (environment == null || environment.getTransactionFactory() == null) {
  51. return new ManagedTransactionFactory();
  52. }
  53. return environment.getTransactionFactory();
  54. }
  55. private void closeTransaction(Transaction tx) {
  56. if (tx != null) {
  57. try {
  58. tx.close();
  59. } catch (SQLException ignore) {
  60. // Intentionally ignore. Prefer previous error.
  61. }
  62. }
  63. }
  64. @Override
  65. public SqlSession openSession() {
  66. return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  67. }
  68. @Override
  69. public SqlSession openSession(boolean autoCommit) {
  70. return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
  71. }
  72. @Override
  73. public SqlSession openSession(ExecutorType execType) {
  74. return openSessionFromDataSource(execType, null, false);
  75. }
  76. @Override
  77. public SqlSession openSession(TransactionIsolationLevel level) {
  78. return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
  79. }
  80. @Override
  81. public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
  82. return openSessionFromDataSource(execType, level, false);
  83. }
  84. @Override
  85. public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
  86. return openSessionFromDataSource(execType, null, autoCommit);
  87. }
  88. @Override
  89. public SqlSession openSession(Connection connection) {
  90. return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
  91. }
  92. @Override
  93. public SqlSession openSession(ExecutorType execType, Connection connection) {
  94. return openSessionFromConnection(execType, connection);
  95. }
  96. @Override
  97. public Configuration getConfiguration() {
  98. return configuration;
  99. }
  100. }

2.2 SqlSessionManager

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

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

SqlSessionManager 的构造方法是唯一且私有的,如果要创建 SqlSessionManager 对象,需要调用其 newInstance()方法(但需要注意的是,这不是单例模式,因为每次调用 newInstance()方法都返回了一个新的对象)。

SqlSessionManager 的 openSession()系列方法,都是通过直接调用其持有的 DefaultSqlSessionFactory 实例来实现的。

  1. public class SqlSessionManager implements SqlSessionFactory, SqlSession {
  2. // 通过持有DefaultSqlSessionFactory对象 来产生SqlSession对象
  3. private final SqlSessionFactory sqlSessionFactory;
  4. // 用于记录一个与当前线程绑定的SqlSession对象
  5. private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
  6. // localSqlSession中记录的SqlSession对象的代理对象(JDK动态代理)
  7. // SqlSessionManager初始化时 生成本代理对象,可以看下 下面的构造函数
  8. private final SqlSession sqlSessionProxy;
  9. // 私有的构造函数,也是SqlSessionManager唯一的构造函数
  10. private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
  11. // 传入的这个SqlSessionFactory对象 往往是DefaultSqlSessionFactory的实例
  12. this.sqlSessionFactory = sqlSessionFactory;
  13. // JDK动态代理生成代理对象,可以看得出,SqlSessionInterceptor一定实现了
  14. // InvocationHandler接口
  15. this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
  16. SqlSessionFactory.class.getClassLoader(),
  17. new Class[]{SqlSession.class},
  18. new SqlSessionInterceptor());
  19. }
  20. // 通过newInstance()方法创建SqlSessionManager对象,有多种重载,
  21. // 但最后都是new了一个DefaultSqlSessionFactory的实例
  22. public static SqlSessionManager newInstance(Reader reader) {
  23. return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, null));
  24. }
  25. public static SqlSessionManager newInstance(Reader reader, String environment) {
  26. return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, environment, null));
  27. }
  28. public static SqlSessionManager newInstance(Reader reader, Properties properties) {
  29. return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, properties));
  30. }
  31. public static SqlSessionManager newInstance(InputStream inputStream) {
  32. return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, null));
  33. }
  34. public static SqlSessionManager newInstance(InputStream inputStream, String environment) {
  35. return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, environment, null));
  36. }
  37. public static SqlSessionManager newInstance(InputStream inputStream, Properties properties) {
  38. return new SqlSessionManager(new SqlSessionFactoryBuilder().build(inputStream, null, properties));
  39. }
  40. public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) {
  41. return new SqlSessionManager(sqlSessionFactory);
  42. }
  43. // openSession()系列方法都是通过当前SqlSessionManager对象持有的
  44. // DefaultSqlSessionFactory实例的openSession()实现的
  45. @Override
  46. public SqlSession openSession() {
  47. return sqlSessionFactory.openSession();
  48. }
  49. @Override
  50. public SqlSession openSession(boolean autoCommit) {
  51. return sqlSessionFactory.openSession(autoCommit);
  52. }
  53. @Override
  54. public SqlSession openSession(Connection connection) {
  55. return sqlSessionFactory.openSession(connection);
  56. }
  57. @Override
  58. public SqlSession openSession(TransactionIsolationLevel level) {
  59. return sqlSessionFactory.openSession(level);
  60. }
  61. @Override
  62. public SqlSession openSession(ExecutorType execType) {
  63. return sqlSessionFactory.openSession(execType);
  64. }
  65. @Override
  66. public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
  67. return sqlSessionFactory.openSession(execType, autoCommit);
  68. }
  69. @Override
  70. public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
  71. return sqlSessionFactory.openSession(execType, level);
  72. }
  73. @Override
  74. public SqlSession openSession(ExecutorType execType, Connection connection) {
  75. return sqlSessionFactory.openSession(execType, connection);
  76. }
  77. }

SqlSessionManager 中实现的 SqlSession 接口方法,例如 select ()系列方法、update()系列方法等,都是直接调用 sqlSessionProxy 代理对象对应的方法实现的。在创建该代理对象时使用的 InvocationHandler 对象是 SqlSessionlnterceptor,它是 SqISessionManager 的内部类。

  1. private class SqlSessionInterceptor implements InvocationHandler {
  2. public SqlSessionInterceptor() { }
  3. @Override
  4. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  5. // 获取 与当前线程绑定的SqlSession
  6. final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
  7. // 如果有绑定的SqlSession对象
  8. if (sqlSession != null) { // 模式二
  9. try {
  10. // 调用真正的sqlSession对象,完成数据库操作
  11. return method.invoke(sqlSession, args);
  12. } catch (Throwable t) {
  13. throw ExceptionUtil.unwrapThrowable(t);
  14. }
  15. // 如果没有绑定的SqlSession对象
  16. } else { // 模式一
  17. // 创建一个新的SqlSession对象
  18. final SqlSession autoSqlSession = openSession();
  19. try {
  20. // 通过反射调用该SqlSession对象的方法,完成数据库操作
  21. final Object result = method.invoke(autoSqlSession, args);
  22. // 提交事务
  23. autoSqlSession.commit();
  24. return result;
  25. } catch (Throwable t) {
  26. // 出异常就回滚
  27. autoSqlSession.rollback();
  28. throw ExceptionUtil.unwrapThrowable(t);
  29. } finally {
  30. // 关闭该SqlSession对象
  31. autoSqlSession.close();
  32. }
  33. }
  34. }
  35. }

通过对 SqlSessionlnterceptor 的分析可知,第一种模式中新建的 SqlSession 在使用完成后会立即关闭。在第二种模式中,与当前线程绑定的 SqISession 对象需要先通过 SqlSessionManager 的 startManagedSession()方法进行设置,此方法也存在多种重载,但都彼此相似 且简单。

  1. public void startManagedSession() {
  2. this.localSqlSession.set(openSession());
  3. }
  4. public void startManagedSession(boolean autoCommit) {
  5. this.localSqlSession.set(openSession(autoCommit));
  6. }
  7. public void startManagedSession(Connection connection) {
  8. this.localSqlSession.set(openSession(connection));
  9. }
  10. public void startManagedSession(TransactionIsolationLevel level) {
  11. this.localSqlSession.set(openSession(level));
  12. }
  13. public void startManagedSession(ExecutorType execType) {
  14. this.localSqlSession.set(openSession(execType));
  15. }
  16. public void startManagedSession(ExecutorType execType, boolean autoCommit) {
  17. this.localSqlSession.set(openSession(execType, autoCommit));
  18. }
  19. public void startManagedSession(ExecutorType execType, TransactionIsolationLevel level) {
  20. this.localSqlSession.set(openSession(execType, level));
  21. }
  22. public void startManagedSession(ExecutorType execType, Connection connection) {
  23. this.localSqlSession.set(openSession(execType, connection));
  24. }
  25. public boolean isManagedSessionStarted() {
  26. return this.localSqlSession.get() != null;
  27. }

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

  1. @Override
  2. public void clearCache() {
  3. final SqlSession sqlSession = localSqlSession.get();
  4. if (sqlSession == null) {
  5. throw new SqlSessionException("Error: Cannot clear the cache. No managed session is started.");
  6. }
  7. sqlSession.clearCache();
  8. }
  9. @Override
  10. public void commit() {
  11. final SqlSession sqlSession = localSqlSession.get();
  12. if (sqlSession == null) {
  13. throw new SqlSessionException("Error: Cannot commit. No managed session is started.");
  14. }
  15. sqlSession.commit();
  16. }
  17. @Override
  18. public void commit(boolean force) {
  19. final SqlSession sqlSession = localSqlSession.get();
  20. if (sqlSession == null) {
  21. throw new SqlSessionException("Error: Cannot commit. No managed session is started.");
  22. }
  23. sqlSession.commit(force);
  24. }
  25. @Override
  26. public void rollback() {
  27. final SqlSession sqlSession = localSqlSession.get();
  28. if (sqlSession == null) {
  29. throw new SqlSessionException("Error: Cannot rollback. No managed session is started.");
  30. }
  31. sqlSession.rollback();
  32. }
  33. @Override
  34. public void rollback(boolean force) {
  35. final SqlSession sqlSession = localSqlSession.get();
  36. if (sqlSession == null) {
  37. throw new SqlSessionException("Error: Cannot rollback. No managed session is started.");
  38. }
  39. sqlSession.rollback(force);
  40. }
  41. @Override
  42. public List<BatchResult> flushStatements() {
  43. final SqlSession sqlSession = localSqlSession.get();
  44. if (sqlSession == null) {
  45. throw new SqlSessionException("Error: Cannot rollback. No managed session is started.");
  46. }
  47. return sqlSession.flushStatements();
  48. }
  49. @Override
  50. public void close() {
  51. final SqlSession sqlSession = localSqlSession.get();
  52. if (sqlSession == null) {
  53. throw new SqlSessionException("Error: Cannot close. No managed session is started.");
  54. }
  55. try {
  56. sqlSession.close();
  57. } finally {
  58. localSqlSession.set(null);
  59. }
  60. }