• java
  • go
  • 数据库
  • linux
  • 中间件
  • 书
  • 源码
  • 夕拾

  • java
  • go
  • 数据库
  • linux
  • 中间件
  • 书
  • 源码
  • 夕拾

mybatis-cache

目录

  • 目录
  • 引言
  • 一级缓存
    • 作用域
    • 存储位置
    • 执行流程
  • 二级缓存
    • cache执行器的执行流程
    • 事务与二级缓存

引言

配置文件内容

1
2
3
4
5
6
7
<!-- 设置缓存器,不设置缓存器,设置useCache也没用 -->
<cache/>
<select id="queryById" parameterType="Map" resultType="Map" useCache="true">
select *
from app
WHERE id = #{id}
</select>

上一篇文章sql-session的执行器部分禁用了缓存

这篇文章打开缓存.看下执行器的流程…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
// 开启二级缓存,会包装成CachingExecutor.
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}

一级缓存

mybatis的一级缓存无法关闭,只能修改作用域.默认为SqlSession.

作用域

默认作用域为sqlSession级别,可以理解为事务内共享.

改为Statement

存储位置

PrepetualCache

1
2
3
4
5
6
7
8
9
10
11
12

public abstract class BaseExecutor implements Executor {
...
// 调用sqlSession.clearCache()时会通过CacheExecutor去执行下面俩个对象的clear方法
protected PerpetualCache localCache;

protected PerpetualCache localOutputParameterCache;
...
}



执行流程

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

@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++;
// 查询一级缓存
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 (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
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 {
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;
}

二级缓存

二级缓存是事务共享的,所以,同一事务内的查询是不会走二级缓存的,因为事务可以回滚,而缓存不可以.

cache执行器的执行流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {

Cache cache = ms.getCache();
// 开启缓存
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
// 从缓存取到直接返回,没取到使用SimpleExector去执行.(同事务中,不会走二级缓存)
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}


事务与二级缓存

TransactionalCacheManager代码不多,也很干净.

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
public class TransactionalCacheManager {
// 存储了所有的Cache与事务Cache
private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();
public void clear(Cache cache) {
getTransactionalCache(cache).clear();
}
public Object getObject(Cache cache, CacheKey key) {
return getTransactionalCache(cache).getObject(key);
}
public void putObject(Cache cache, CacheKey key, Object value) {
getTransactionalCache(cache).putObject(key, value);
}
public void commit() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.commit();
}
}
public void rollback() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.rollback();
}
}
private TransactionalCache getTransactionalCache(Cache cache) {
return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
}
}


public class TransactionalCache implements Cache {

private static final Log log = LogFactory.getLog(TransactionalCache.class);
// 真实缓存对象
private final Cache delegate;
// 清空提交
private boolean clearOnCommit;
// 待提交的缓存
private final Map<Object, Object> entriesToAddOnCommit;
// 未命中的缓存.
private final Set<Object> entriesMissedInCache;

public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap<>();
this.entriesMissedInCache = new HashSet<>();
}

@Override
public String getId() {
return delegate.getId();
}

@Override
public int getSize() {
return delegate.getSize();
}

@Override
public Object getObject(Object key) {
// issue #116
Object object = delegate.getObject(key);
if (object == null) {
// 不再缓存的,放到未命中缓存中
entriesMissedInCache.add(key);
}
// issue #146
//
if (clearOnCommit) {
return null;
} else {
// 不为空,返回
return object;
}
}

// 缓存中未查到时,从db中查询,查到之后会调用此方法
// 即将查询的结果放入到待提交的map里.
@Override
public void putObject(Object key, Object object) {
entriesToAddOnCommit.put(key, object);
}

@Override
public Object removeObject(Object key) {
return null;
}

@Override
public void clear() {
// 事务提交了,情况缓存.
// 情况待提交缓存
clearOnCommit = true;
entriesToAddOnCommit.clear();
}

public void commit() {
// 事务提交时,可以设置一下这个值,弃用缓存.
// 没看具体的缓存实现类.应该就是在这个地方设置个钩子支持情况缓存
if (clearOnCommit) {
// 清空缓存
delegate.clear();
}
// 把待提交缓存放到真实缓存.
flushPendingEntries();
// 复位,clearOnCommit设置为true
// 清里两个缓存
reset();
}

public void rollback() {
//
unlockMissedEntries();
reset();
}

private void reset() {
clearOnCommit = false;
entriesToAddOnCommit.clear();
entriesMissedInCache.clear();
}

private void flushPendingEntries() {
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
delegate.putObject(entry.getKey(), entry.getValue());
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}

private void unlockMissedEntries() {
for (Object entry : entriesMissedInCache) {
try {

delegate.removeObject(entry);
} catch (Exception e) {
log.warn("Unexpected exception while notifiying a rollback to the cache adapter. "
+ "Consider upgrading your cache adapter to the latest version. Cause: " + e);
}
}
}

}

【源码阅读】CountDownLatch
mybatis-configuration
  1. 1. 目录
  2. 2. 引言
  3. 3. 一级缓存
    1. 3.1. 作用域
    2. 3.2. 存储位置
    3. 3.3. 执行流程
  4. 4. 二级缓存
    1. 4.1. cache执行器的执行流程
    2. 4.2. 事务与二级缓存
© 2023 haoxp
Hexo theme