MyBatis之魂:探索核心接口SqlSession的神秘力量
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 会引出其他三大对象来完成数据库的读写。
- 点赞
- 收藏
- 关注作者
评论(0)