SqlSession
SqlSession 是 MyBatis 对外暴露的最核心接口,用于操作 SQL 的执行,以屏蔽掉底层 JDBC 操作数据库的繁琐,可以直接调用其申明的各种方便的方法,如查询/新增/更新/删除/提交事务/回滚事务/获取 Mapper 代理对象等。
接口定义
public interface SqlSession extends Closeable { // 查询单个结果 <T> T selectOne(String statement); <T> T selectOne(String statement, Object parameter); // 查询 List<E> 结果,可指定 RowBounds 参数 <E> List<E> selectList(String statement); <E> List<E> selectList(String statement, Object parameter); <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds); // 返回 Map 的查询方式 <K, V> Map<K, V> selectMap(String statement, String mapKey); <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey); <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds); // 返回懒处理迭代器 Cursor 游标的查询,注意这种方式的查询非常适合与数百万数据项的查询(不会直接加载进内存中) <T> Cursor<T> selectCursor(String statement); <T> Cursor<T> selectCursor(String statement, Object parameter); <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds); // 指定 resultHandler 参数的查询 void select(String statement, Object parameter, ResultHandler handler); void select(String statement, ResultHandler handler); void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler); // insert 相关 int insert(String statement); int insert(String statement, Object parameter); // update 相关 int update(String statement); int update(String statement, Object parameter); // delete 相关 int delete(String statement); int delete(String statement, Object parameter); // 事务相关,force 参数代表是否强制提交/回滚事务 void commit(); void commit(boolean force); void rollback(); void rollback(boolean force); // 获取当前 MyBatis 配置对象 Configuration getConfiguration(); // 获取一个绑定到当前 SqlSession 对象的 type 类型的 Mapper 代理对象 <T> T getMapper(Class<T> type); // 获取内部持有的数据库连接对象 Connection getConnection(); }
继承关系
实现类
SqlSession 接口的主要实现类是 DefaultSqlSession,SqlSessionFactory 的主要实现是 DefaultSqlSessionFactory。不过从类图我们看到有一个SqlSessionManager 类,他实现了两个接口,一方面具备生产 SqlSession 的能力,另一方面又具备 SqlSession 的能力。
我们比较一下两个实现类的区别:
- DefaultSqlSession:每次访问数据库创建一个新的 SqlSession,一个 SqlSession 代表一次数据库连接(默认,也是我们重点分析的)。
- SqlsessionManager:内部通过 ThreadLocal 来保证一个线程只创建一个 SqlSession,但是也可以每次创建一个新的(支持两种模式)。
DefaultSqlSession
SqlSession 的默认实现是 DefaultSqlSession。SqlSession 提供的数据库操作在底层都是调用 Executor 来执行。
类定义
public class DefaultSqlSession implements SqlSession {...}
可以看到这里 DefaultSqlSession 仅实现了 SqlSession 接口。
内部属性定义
// 重要属性,持有 MyBatis Configuration 实例 private final Configuration configuration; // 持有执行器 Executor,后面会详细分析 private final Executor executor; // 是否自动提交事务 private final boolean autoCommit; // 是否已过期,执行 update 操作就会置为 true private boolean dirty; // 当查询数以百万计的数据时用到 private List<Cursor<?>> cursorList;
每个 SqlSession 实例内部都持有一个 MyBatis 的配置实例以及一个执行器 Executor 实例,且在其构造函数中就需要初始化这两个属性了。
构造函数
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.dirty = false; this.autoCommit = autoCommit; } public DefaultSqlSession(Configuration configuration, Executor executor) { this(configuration, executor, false); }
核心代码
// ... @Override public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { // selectMap 底层走的还是 selectList 方法 final List<? extends V> list = selectList(statement, parameter, rowBounds); // 因为是按照 resultMap 的形式返回,因此拿到结果集之后,需要对结果进行处理,通过 mapResultHandler 处理 final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey, configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory()); final DefaultResultContext<V> context = new DefaultResultContext<V>(); for (V o : list) { context.nextResultObject(o); mapResultHandler.handleResult(context); } // 返回处理后的结果集 return mapResultHandler.getMappedResults(); } @Override public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds); registerCursor(cursor); return cursor; } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } @Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } @Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } @Override 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(); } } // ...
SqlSession 提供很多增删改查的方法,在 DefaultSqlSession 中看方法执行流程,所有的查询方法最后都会走到一个方法,底层调用 Executor 的 query,更新和删除也类似,都会走到 Executor 的更新方法。
这些核心方法的基本逻辑如下:
- 首先从 configuratin 实例中获取到 MappedStatement 实例。
- 调用执行器 executor 的相关方法,并传入刚刚得到的 MappedStatement 实例作为参数。
SqlSession 真正的 SQL 执行交给了 Executor 执行器,前文不是说过 SqlSession 是 MyBatis 提供的顶层 API 嘛,通过 SqlSession 就可以操作数据库的各种 SQL 操作了,这里怎么又出来一个执行器呢?实际上,MyBatis的 SqlSession 非常聪明,将各种 SQL 操作的脏活、累活都委托给了 Executor 执行器干,自己则坐享其成。
SqlSessionFactory
SqlSessionFactory 是创建 SqlSession 的工厂。
接口定义
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(); }
继承关系
实现类
DefaultSqlSessionFactory
DefaultSqlSessionFactory 是 SqlSessionFactory 的重要实现类,它是创建 SqlSession 的工厂。创建 SqlSession 有两种方式,从数据源获取或者从数据库连接获取。DefaultSqlSessionFactory 提供了很多创建的方法,底层最后都是走这两个方法,具体看下面代码和注释:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); // 从数据源 DataSource 获取 Transaction,这是两者最大的区别,其他的都差不多 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(); } } 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; } final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); // 从连接对象获取 Transaction,这是两者最大的区别,其他的都差不多 final Transaction tx = transactionFactory.newTransaction(connection); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
总结一下
- SqlSession 是用户使用 MyBatis 编程的关键接口,我们通过 SqlSessionFactory 获取 SqlSession。
- SqlSessionFactory 的初始化过程在配置初始化阶段完成,之后就可以通过 SqlSessionFactory 获取 SqlSession 了。
- 通过 SqlSession,我们可以获取接口的代理对象,继而进行增删改查操作,经过一层代理之后,真正的查询入口还是 SqlSession 的方法。
- SqlSession 中很多数据库读取的重载方法,这些方法最后都走到一个方法里面,并且会使用 Executor 组件去访问数据库,Executor 会引出其他三大对象来完成数据库的读写。