如何创建 SqlSession

org.apache.ibatis.session.defaults.DefaultSqlSessionFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 在应用程序中通过sqlSessionFactory获取一个SqlSession对象执行CRUD操作
SqlSession sqlSession = sqlSessionFactory.openSession(true);

// 在DefaultSqlSessionFactory中获取SqlSession对象
@Override
public SqlSession openSession(boolean autoCommit) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}

// 通过MyBatis配置参数构建SqlSession对象
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
// 注意这个设计模式
Transaction tx = null;
try {
// 注意这个逐一抽象的设计模式
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根据配置的Executor类型装配具体的实现类
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();
}
}

缓存问题

参考《深入浅出mybatis之缓存机制》

xml 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- 有效值: true|false,默认值为true -->
<setting name="cacheEnabled" value="true" />
<!-- 有效值:SESSION|STATEMENT,默认值为SESSION -->
<setting name="localCacheScope" value="SESSION" />

## 如何创建执行器

`org.apache.ibatis.session.Configuration`
```java
// 在Configuration中根据不同的defaultExecutorType参数值装配具体的Executor实现
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// 这里的双行 executorType 赋值很有意思
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
// 这里使用了简单工厂方法模式,值得注意的是 Executor 本身知道自己的 wrapper 和 delegate 对象之间的相互引用
// 根据不同的defaultExecutorType参数值装配具体的Executor实现
Executor executor;
if (ExecutorType.BATCH == executorType) {
// 当defaultExecutorType值为BATCH时,使用BatchExecutor
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
// 当defaultExecutorType值为REUSE时,使用ReuseExecutor
executor = new ReuseExecutor(this, transaction);
} else {
// 默认情况下使用SimpleExecutor
executor = new SimpleExecutor(this, transaction);
}
// 如果设置cacheEnabled参数值为true,将使用CachingExecutor,这是第一个参数的重要特性!
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}

org.apache.ibatis.executor.BaseExecutor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
// 先从缓存中获取数据,这里引用了一个 PerpetualCache
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 未从缓存中获取到数据时直接从数据库中查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (BaseExecutor.DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
// 如果参数localCacheScope值为STATEMENT,则每次查询之后都清空缓存-这是这个配置真正的用意!非常重要!反之,LocalCacheScope.SESSION 什么都不做(不再清空缓存)
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}

// 直接从数据库查询
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
// 真正执行数据库查询操作的是BaseExecutor子类中实现的doQuery()方法
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}

// BaseExecutor中的doQuery()方法是一个抽放方法
// 所以真正执行数据库查询的操作都是委托给了BaseExecutor子类:BatchExecutor,ReuseExecutor和SimpleExecutor
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
  1. 参数cacheEnabled控制MyBatis使用的执行器类型,cacheEnabled 为 false 使用 BaseExecutor,里面一样有一个 localCache。
  2. 参数localCacheScope控制的是BaseExecutor内部的缓存策略,确定 localCache 是否真的要被使用-它可以优化同一个 session 内的重复查询。

org.apache.ibatis.cache.CacheKey

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
public class CacheKey implements Cloneable, Serializable {
private final int multiplier;
private int hashcode;
private long checksum;
private int count;
private transient List<Object> updateList;

@Override
public int hashCode() {
// hashCode值为内部保存的hashcode属性
return hashcode;
}

@Override
public boolean equals(Object object) {
// 判断两个CacheKey实例通过equals()方法比较时返回true必须同时满足如下条件:
// 1.hashcode属性值相等
// 2.checksum属性值相等
// 3.count属性值相等
// 4.updateList列表属性中存放的每一个对象通过equals()方法比较时返回true
if (this == object) {
return true;
}
if (!(object instanceof CacheKey)) {
return false;
}

final CacheKey cacheKey = (CacheKey) object;

if (hashcode != cacheKey.hashcode) {
return false;
}
if (checksum != cacheKey.checksum) {
return false;
}
if (count != cacheKey.count) {
return false;
}

for (int i = 0; i < updateList.size(); i++) {
Object thisObject = updateList.get(i);
Object thatObject = cacheKey.updateList.get(i);
if (!ArrayUtil.equals(thisObject, thatObject)) {
return false;
}
}
return true;
}
}


@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
// 在查询之前构造CacheKey缓存Key对象
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}


// cache key 的构造算法,把所有的关联值对象化,再列表化,再组装起来
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
// 调用CacheKey实例的update()方法将一个对象放在其内部的updateList列表中
// 只要是查询同一条数据的相同SQL语句,可以保证如下参数的相同的:
// ms.getId(),rowBounds.getOffset(),rowBounds.getLimit(),boundSql.getSql(),boundSql.getParameterMappings()
// 也就是说,只要是在相同的SqlSession中查询同一条数据时都会命中BaseExecutor的本地缓存
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}

org.apache.ibatis.executor.CachingExecutor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 被包装的执行器对象
private final Executor delegate;

// CachingExecutor内部的缓存管理器
private final TransactionalCacheManager tcm = new TransactionalCacheManager();

// 在构造函数中传入一个执行器对象
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}

// CachingExecutor实现的查询方法,在这里实现缓存
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 从MappedStatement中获取一个缓存实例对象,注意看,这里的 cache 其实是存在于执行对象里的
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
  1. localCacheScope 控制的是一级缓存的配置,这是一个经常忘记被关闭的缓存的配置。
  2. cacheEnabled 控制的是二级缓存的配置,人们往往记得关闭它。
  3. 这两个缓存可以被同时激活。