mybatis-SqlSession
时间:2022-03-15 16:07
1. 概述
在前面,我们已经详细解析了 MyBatis 执行器 Executor 相关的内容,但是显然,Executor 是不适合直接暴露给用户使用的,而是需要通过 SqlSession 。
流程如下图:
示例代码如下:
// 仅仅是示例哈 |
而本文解析的类,都在 session
包下,整体类图如下:
老艿艿:省略了一部分前面已经解析过的类。
- 核心是 SqlSession 。
- SqlSessionFactory ,负责创建 SqlSession 对象的工厂。
- SqlSessionFactoryBuilder ,是 SqlSessionFactory 的构建器。
下面,我们按照 SqlSessionFactoryBuilder => SqlSessionFactory => SqlSession 来详细解析。
2. SqlSessionFactoryBuilder
org.apache.ibatis.session.SqlSessionFactoryBuilder
,SqlSessionFactory 构造器。代码如下:
// SqlSessionFactory.java |
- 提供了各种 build 的重载方法,核心的套路都是解析出 Configuration 配置对象,从而创建出 DefaultSqlSessionFactory 对象。
3. SqlSessionFactory
org.apache.ibatis.session.SqlSessionFactory
,SqlSession 工厂接口。代码如下:
// SqlSessionFactory.java |
- 定义了
#openSession(...)
和#getConfiguration()
两类方法。
3.1 DefaultSqlSessionFactory
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
,实现 SqlSessionFactory 接口,默认的 SqlSessionFactory 实现类。
3.1.1 构造方法
// DefaultSqlSessionFactory.java |
3.1.2 openSession
// DefaultSqlSessionFactory.java |
-
调用
#openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)
方法,获得 SqlSession 对象。代码如下:// DefaultSqlSessionFactory.java
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获得 Environment 对象
final Environment environment = configuration.getEnvironment();
// 创建 Transaction 对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
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();
}
}- DefaultSqlSession 的创建,需要
configuration
、executor
、autoCommit
三个参数。
- DefaultSqlSession 的创建,需要
-
#openSessionFromConnection(ExecutorType execType, Connection connection)
方法,获得 SqlSession 对象。代码如下:// DefaultSqlSessionFactory.java
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();
// 创建 Transaction 对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
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();
}
}
3.1.3 getTransactionFactoryFromEnvironment
// DefaultSqlSessionFactory.java |
3.1.4 closeTransaction
// DefaultSqlSessionFactory.java |
4. SqlSession
org.apache.ibatis.session.SqlSession
,SQL Session 接口。代码如下:
// SqlSession.java |
- 大体接口上,和 Executor 接口是相似的。
4.1 DefaultSqlSession
org.apache.ibatis.session.defaults.DefaultSqlSession
,实现 SqlSession 接口,默认的 SqlSession 实现类。
4.1.1 构造方法
// DefaultSqlSession.java |
4.1.2 selectList
// DefaultSqlSession.java |
-
<1>
处,调用Configuration#getMappedStatement(String id)
方法,获得 MappedStatement 对象。代码如下:// DefaultSqlSession.java
/**
* MappedStatement 映射
*
* KEY:`${namespace}.${id}`
*/
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
// 校验,保证所有 MappedStatement 已经构造完毕
if (validateIncompleteStatements) {
buildAllStatements();
}
// 获取 MappedStatement 对象
return mappedStatements.get(id);
}
protected void buildAllStatements() {
if (!incompleteResultMaps.isEmpty()) {
synchronized (incompleteResultMaps) { // 保证 incompleteResultMaps 被解析完
// This always throws a BuilderException.
incompleteResultMaps.iterator().next().resolve();
}
}
if (!incompleteCacheRefs.isEmpty()) {
synchronized (incompleteCacheRefs) { // 保证 incompleteCacheRefs 被解析完
// This always throws a BuilderException.
incompleteCacheRefs.iterator().next().resolveCacheRef();
}
}
if (!incompleteStatements.isEmpty()) {
synchronized (incompleteStatements) { // 保证 incompleteStatements 被解析完
// This always throws a BuilderException.
incompleteStatements.iterator().next().parseStatementNode();
}
}
if (!incompleteMethods.isEmpty()) {
synchronized (incompleteMethods) { // 保证 incompleteMethods 被解析完
// This always throws a BuilderException.
incompleteMethods.iterator().next().resolve();
}
}
}- 其中,
#buildAllStatements()
方法,是用来保证所有 MappedStatement 已经构造完毕。不过艿艿,暂时没想到,什么情况下,会出现 MappedStatement 没被正确构建的情况。猜测有可能是防御性编程。
- 其中,
<2>
处,调用Executor#query(...)
方法,执行查询。
4.1.3 selectOne
// DefaultSqlSession.java |
- 内部调用
#selectList(String statement, Object parameter)
方法,进行实现。
4.1.4 selectMap
#selectMap(...)
方法,查询结果,并基于 Map 聚合结果。代码如下:
// DefaultSqlSession.java |
<1>
处,调用#selectList(String statement, Object parameter, RowBounds rowBounds)
方法,执行查询。<2>
处,创建 DefaultMapResultHandler 对象。<3>
处,创建 DefaultResultContext 对象。-
<4>
处,遍历查询结果,并调用DefaultMapResultHandler#handleResult(context)
方法,将结果的当前元素,聚合成 Map 。代码如下:// DefaultMapResultHandler.java
public class DefaultMapResultHandler<K, V> implements ResultHandler<V> {
/**
* 结果,基于 Map 聚合
*/
private final Map<K, V> mappedResults;
/**
* {@link #mappedResults} 的 KEY 属性名
*/
private final String mapKey;
private final ObjectFactory objectFactory;
private final ObjectWrapperFactory objectWrapperFactory;
private final ReflectorFactory reflectorFactory;
@SuppressWarnings("unchecked")
public DefaultMapResultHandler(String mapKey, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
// 创建 Map 对象
this.mappedResults = objectFactory.create(Map.class);
this.mapKey = mapKey;
}
@Override
public void handleResult(ResultContext<? extends V> context) {
// 获得 KEY 对应的属性
final V value = context.getResultObject();
final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
// TODO is that assignment always true?
final K key = (K) mo.getValue(mapKey);
// 添加到 mappedResults 中
mappedResults.put(key, value);
}
public Map<K, V> getMappedResults() {
return mappedResults;
}
} -
<5>
处,返回结果。
4.1.5 selectCursor
// DefaultSqlSession.java |
<1>
处,调用Configuration#getMappedStatement(String id)
方法,获得 MappedStatement 对象。<2>
处,调用Executor#queryCursor(...)
方法,执行查询。-
<3>
处,调用#registerCursor(Cursor<T> cursor)
方法,添加cursor
到cursorList
中。代码如下:// DefaultSqlSession.java
private <T> void registerCursor(Cursor<T> cursor) {
if (cursorList == null) {
cursorList = new ArrayList<>();
}
cursorList.add(cursor);
}
4.1.6 select
#select(..., ResultHandler handler)
方法,执行查询,使用传入的 handler
方法参数,对结果进行处理。代码如下:
// DefaultSqlSession.java |
4.1.7 wrapCollection
在上述的查询方法中,我们都可以看到一个 #wrapCollection(final Object object)
方法,若参数 object
是 Collection、Array、Map 参数类型的情况下,包装成 Map 返回。代码如下:
// DefaultSqlSession.java |
4.1.8 update
// DefaultSqlSession.java |
<1>
处,标记dirty
,表示执行过写操作。该参数,会在事务的提交和回滚,产生其用途。<2>
处,获得 MappedStatement 对象。<3>
处,调用Executor#update(MappedStatement ms, Object parameter)
方法,执行更新操作。
4.1.9 insert
// DefaultSqlSession.java |
- 基于
#update(...)
方法来实现。
4.1.10 delete
// DefaultSqlSession.java |
- 基于
#update(...)
方法来实现。
4.1.11 flushStatements
#flushStatements()
方法,提交批处理。代码如下:
// DefaultSqlSession.java |
4.1.12 commit
// DefaultSqlSession.java |
-
其中,
#isCommitOrRollbackRequired(boolean force)
方法,判断是否执行提交或回滚。代码如下:// DefaultSqlSession.java
private boolean isCommitOrRollbackRequired(boolean force) {
return (!autoCommit && dirty) || force;
}- 有两种情况需要触发:
- 1)未开启自动提交,并且数据发生写操作
- 2)强制提交
4.1.13 rollback
// DefaultSqlSession.java |
4.1.14 close
#close()
方法,关闭会话。代码如下:
// DefaultSqlSession.java |
<1>
处,调用Executor#close(boolean forceRollback)
方法,关闭执行器。并且,根据forceRollback
参数,是否进行事务回滚。-
<2>
处,调用#closeCursors()
方法,关闭所有游标。代码如下:// DefaultSqlSession.java
private void closeCursors() {
if (cursorList != null && cursorList.size() != 0) {
for (Cursor<?> cursor : cursorList) {
try {
cursor.close();
} catch (IOException e) {
throw ExceptionFactory.wrapException("Error closing cursor. Cause: " + e, e);
}
}
cursorList.clear();
}
}
<3>
处,重置dirty
为false
。
4.1.15 getConfiguration
// DefaultSqlSession.java |
4.1.16 getMapper
// DefaultSqlSession.java |
4.1.17 getConnection
// DefaultSqlSession.java |
4.1.18 clearCache
// DefaultSqlSession.java |
5. SqlSessionManager
org.apache.ibatis.session.SqlSessionManager
,实现 SqlSessionFactory、SqlSession 接口,SqlSession 管理器。所以,从这里已经可以看出,SqlSessionManager 是 SqlSessionFactory 和 SqlSession 的职能相加。
5.1 构造方法
// SqlSessionManager.java |
- 比较有意思的有两点,我们逐条来看。
<1>
处,localSqlSession
属性,线程变量,记录当前线程的 SqlSession 对象。<2>
处,创建 SqlSession 的代理对象,而方法的拦截器是 SqlSessionInterceptor 类。详细解析,见 。
5.2 newInstance
#newInstance(...)
静态方法,创建 SqlSessionManager 对象。代码如下:
// SqlSessionManager.java |
- 代码比较简单,胖友自己瞅瞅。
5.3 startManagedSession
#startManagedSession(...)
方法,发起一个可被管理的 SqlSession 。代码如下:
// SqlSessionManager.java |
- 可能胖友很难理解“可被管理”的 SqlSession 的意思?继续往下看。
5.4 对 SqlSessionFactory 的实现方法
// SqlSessionManager.java |
- 直接调用
sqlSessionFactory
对应的方法即可。
5.5 对 SqlSession 的实现方法
// SqlSessionManager.java |
- 调用
localSqlSession
中的 SqlSession 对象,对应的方法。
5.6 SqlSessionInterceptor
SqlSessionInterceptor ,是 SqlSessionManager 内部类,实现 InvocationHandler 接口,实现对 sqlSessionProxy
的调用的拦截。代码如下:
// SqlSessionManager.java |
- 分成两种情况,胖友直接看代码注释。hoho 。
5.7 嘿嘿嘿
貌似,SqlSessionManager 在实际项目中木有什么用。这里,胖友就是去理解,以及动态代理的使用。
6. Configuration
org.apache.ibatis.session.Configuration
,MyBatis 配置对象。在前面已经不断在介绍了,这里就不重复了。
7. MapperMethod
在前面的文章中,我们很分块的介绍了 MyBatis 的各个组件,那么一条 SQL 命令的完整流程是怎么样的呢?如下图所示:
FROM 祖大俊
那么从这个图中可以看出,MapperMethod 在里面扮演了非常重要的角色,将方法转换成对应的 SqlSession 方法,并返回执行结果。实际上,在 中,我们已经介绍了 MapperMethod 类,但是因为 #execute(SqlSession sqlSession, Object[] args)
方法,涉及内容较多,所以没有详细解析。那么此处,让我们撸起这个方法。代码如下:
// MapperMethod.java |
-
INSERT
命令类型<1.1>
处,调用MethodSignature#convertArgsToSqlCommandParam(args)
方法,转换参数。<1.2>
处,调用SqlSession#insert(String statement, Object parameter)
方法,执行 INSERT 操作。-
<1.3>
处,调用#rowCountResult(int rowCount)
方法,将返回的行变更数,转换成方法实际要返回的类型。代码如下:// MapperMethod.java
private Object rowCountResult(int rowCount) {
final Object result;
if (method.returnsVoid()) { // Void 情况,不用返回
result = null;
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) { // Int
result = rowCount;
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) { // Long
result = (long) rowCount;
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) { // Boolean
result = rowCount > 0;
} else {
throw new BindingException("Mapper method ‘" + command.getName() + "‘ has an unsupported return type: " + method.getReturnType());
}
return result;
}- 根据方法的返回类型,做相应的转化处理。
UPDATE
命令类型,同INSERT
命令类型。DELETE
命令类型,同INSERT
命令类型。SELECT
命令类型<2.1>
处,无返回,并且有 ResultHandler 方法参数,则调用#executeWithResultHandler(SqlSession sqlSession, Object[] args)
方法,将查询的结果,提交给 ResultHandler 进行处理。详细解析,见 。<2.2>
处,调用#executeForMany(SqlSession sqlSession, Object[] args)
方法,执行查询,返回列表。详细解析,见 。<2.3>
处,调用#executeForMap(SqlSession sqlSession, Object[] args)
方法,执行查询,返回 Map。详细解析,见 。<2.4>
处,调用#executeForCursor(SqlSession sqlSession, Object[] args)
方法,执行查询,返回 Map。详细解析,见 。<2.5>
处,执行查询,返回单个对象。此处是,对 SqlSession 的 方法的封装。
7.1 executeWithResultHandler
// MapperMethod.java |
- 对 SqlSession 的 方法的封装。
7.2 executeForMany
// MapperMethod.java |
- 对 SqlSession 的 方法的封装。
-
情况一,Array 。代码如下:
// MapperMethod.java
private <E> Object convertToArray(List<E> list) {
Class<?> arrayComponentType = method.getReturnType().getComponentType();
Object array = Array.newInstance(arrayComponentType, list.size());
if (arrayComponentType.isPrimitive()) {
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
return array;
} else {
return list.toArray((E[]) array);
}
}
-
情况二,Collection 。代码如下:
// MapperMethod.java
private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
Object collection = config.getObjectFactory().create(method.getReturnType());
MetaObject metaObject = config.newMetaObject(collection);
metaObject.addAll(list);
return collection;
}
7.3 executeForMap
// MapperMethod.java |
- 对 SqlSession 的 方法的封装。