-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmybatis-cache.drawio
468 lines (468 loc) · 101 KB
/
mybatis-cache.drawio
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
<mxfile host="Electron" modified="2024-12-09T16:51:08.730Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.5 Chrome/114.0.5735.243 Electron/25.3.1 Safari/537.36" etag="nkEk_-id_wXqa7EROPYD" version="21.6.5" type="device">
<diagram name="第 1 页" id="Euxv3z7wh99awfAnfNUn">
<mxGraphModel dx="3088" dy="879" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="uPxwXOejs5hoWM0QN6wt-1" value="<h1 style="font-size: 16px;">Mybatis 两级缓存工作原理&nbsp;<span style="font-weight: normal">(3.5.9)</span></h1><p>共有两级缓存,<br>一级缓存默认开启,默认作用范围是Session;<br>二级缓存默认关闭,作用范围是相同的命名空间(namespace);</p><p>测试Demo: SpringBoot-Labs/mybatis/mybatis-cache。</p><p></p>" style="text;html=1;strokeColor=none;fillColor=none;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="1" vertex="1">
<mxGeometry x="40" y="10" width="440" height="110" as="geometry" />
</mxCell>
<mxCell id="uPxwXOejs5hoWM0QN6wt-4" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="uPxwXOejs5hoWM0QN6wt-2" target="uPxwXOejs5hoWM0QN6wt-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="uPxwXOejs5hoWM0QN6wt-2" value="<b>CachingExecutor</b>#<b>query</b>(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="40" y="160" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-11" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="uPxwXOejs5hoWM0QN6wt-3" target="yhzCpn97QrwA5I1gkQgs-10" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="730" y="240" />
<mxPoint x="730" y="400" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-16" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="yhzCpn97QrwA5I1gkQgs-11" vertex="1" connectable="0">
<mxGeometry x="0.8433" y="-2" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-15" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="uPxwXOejs5hoWM0QN6wt-3" target="yhzCpn97QrwA5I1gkQgs-14" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="730" y="240" />
<mxPoint x="730" y="860" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-17" value="2" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="yhzCpn97QrwA5I1gkQgs-15" vertex="1" connectable="0">
<mxGeometry x="0.9311" y="-2" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="uPxwXOejs5hoWM0QN6wt-3" value="<div style="font-size: 10px;">public &lt;E&gt; List&lt;E&gt; <b style="font-size: 10px;">query</b>(MappedStatement ms, Object parameterObject, <br style="font-size: 10px;">RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {</div><div style="font-size: 10px;"><span style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">&nbsp; &nbsp; // 参考MySQL整体流程图</font></span></div><div style="font-size: 10px;">&nbsp; &nbsp; BoundSql boundSql = ms.<b style="font-size: 10px;">getBoundSql</b>(parameterObject);</div><div style="font-size: 10px;"><b style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">&nbsp; &nbsp; // 1 创建二级缓存的key</font></b></div><div style="font-size: 10px;">&nbsp; &nbsp; CacheKey key = this.<b style="font-size: 10px;">createCacheKey</b>(ms, parameterObject, rowBounds, boundSql);</div><div style="font-size: 10px;"><b><font color="#007fff">&nbsp; &nbsp; // 2 内部实现两级缓存操作</font></b></div><div style="font-size: 10px;">&nbsp; &nbsp; return this.<b style="font-size: 10px;">query</b>(ms, parameterObject, rowBounds, resultHandler, key, boundSql);</div><div style="font-size: 10px;">}</div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=4;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="280" y="160" width="440" height="160" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-1" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b style="font-size: 11px;">BoundSql</b></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private final String <b style="font-size: 11px;">sql</b>;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private final List&lt;ParameterMapping&gt; parameterMappings;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private final Object parameterObject;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private final Map&lt;String, Object&gt; additionalParameters;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private final MetaObject metaParameters;</p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br style="font-size: 11px;"></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-480" y="160" width="440" height="160" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-8" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;dashed=1;endArrow=block;endFill=1;fontSize=11;" parent="1" source="yhzCpn97QrwA5I1gkQgs-2" target="yhzCpn97QrwA5I1gkQgs-7" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-2" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b style="font-size: 11px;">BaseExecutor</b><br style="font-size: 11px;"></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;">protected Transaction <b style="font-size: 11px;">transaction</b>;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">protected Executor wrapper;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">protected ConcurrentLinkedQueue&lt;DeferredLoad&gt; deferredLoads;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">protected PerpetualCache localCache;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">protected PerpetualCache localOutputParameterCache;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">protected Configuration configuration;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">protected int queryStack;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private boolean closed;</p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br style="font-size: 11px;"></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-1440" y="280" width="440" height="200" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-6" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;endArrow=open;endFill=0;fontSize=11;" parent="1" source="yhzCpn97QrwA5I1gkQgs-3" target="yhzCpn97QrwA5I1gkQgs-7" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-3" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b style="font-size: 11px;">CachingExecutor</b><br style="font-size: 11px;"></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private final Executor <b style="font-size: 11px;">delegate</b>;<br style="font-size: 11px;"></p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private final TransactionalCacheManager <b style="font-size: 11px;">tcm</b> = new TransactionalCacheManager();<br style="font-size: 11px;"></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br style="font-size: 11px;"></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-960" y="160" width="440" height="160" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-5" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;endArrow=block;endFill=1;fontSize=11;" parent="1" source="yhzCpn97QrwA5I1gkQgs-4" target="yhzCpn97QrwA5I1gkQgs-2" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-4" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b style="font-size: 11px;">SimpleExecutor</b><br style="font-size: 11px;"></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br style="font-size: 11px;"></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br style="font-size: 11px;"></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="-1440" y="520" width="440" height="80" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-7" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b style="font-size: 11px;">Executor</b></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff" style="font-size: 11px;">接口方法分为SQL查询、更新(增删改)、事务(提交、回滚)、缓存(创建缓存Key、清理本地缓存</font><span style="color: rgb(0, 127, 255); background-color: initial; font-size: 11px;">)、连接等</span></p><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br style="font-size: 11px;"></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="-1440" y="160" width="440" height="80" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-9" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b style="font-size: 11px;">CacheKey</b><br style="font-size: 11px;"></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff" style="font-size: 11px;"><b style="font-size: 11px;">参与Key生成的对象包括:MappedStatement.id(即Mapper方法)、RowBounds.offset、RowBounds.limit、 BoundSql.sql、OUT参数、环境配置ID<br style="font-size: 11px;"></b></font></p><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br style="font-size: 11px;"></p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private static final int DEFAULT_MULTIPLIER = 37;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private static final int DEFAULT_HASHCODE = 17;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private final int multiplier;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff" style="font-size: 11px;">// 由各个参与Key生成的对象hashCode计算生成</font></p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private int <b style="font-size: 11px;">hashcode</b>;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private long <b style="font-size: 11px;">checksum</b>;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff" style="font-size: 11px;">// 参与Key生成的对象数量</font></p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private int <b style="font-size: 11px;">count</b>;</p><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff" style="font-size: 11px;">// 参与Key生成的对象列表</font></p><p style="margin: 0px 0px 0px 4px; font-size: 11px;">private List&lt;Object&gt; <b style="font-size: 11px;">updateList</b>;</p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// hashcode:checksum:&lt;所有参与key生成的对象的toString()返回的字符串&gt;</font></p><p style="margin: 0px 0px 0px 4px;">public String toString()<br style="font-size: 11px;"></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="-480" y="400" width="440" height="280" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-10" value="<div style="font-size: 10px;"><b><font color="#007fff">// 参与Key生成的对象包括:MappedStatement.id(即Mapper方法)、RowBounds.offset、RowBounds.limit、 BoundSql.sql、存储过程OUT参数、环境配置ID</font></b></div><div style="font-size: 10px;">public CacheKey <b>createCacheKey</b>(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {</div><div style="font-size: 10px;">&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; if (closed) {</div><div>&nbsp; &nbsp; &nbsp; throw new ExecutorException("Executor was closed.");</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; CacheKey cacheKey = new <b>CacheKey</b>();</div><div>&nbsp; &nbsp; cacheKey.<b>update</b>(ms.getId());</div><div>&nbsp; &nbsp; cacheKey.<b>update</b>(rowBounds.getOffset());</div><div>&nbsp; &nbsp; cacheKey.<b>update</b>(rowBounds.getLimit());</div><div>&nbsp; &nbsp; cacheKey.<b>update</b>(boundSql.getSql());</div><div>&nbsp; &nbsp; List&lt;ParameterMapping&gt; <b>parameterMappings</b> = boundSql.getParameterMappings();</div><div>&nbsp; &nbsp; TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();</div><div>&nbsp; &nbsp; // mimic DefaultParameterHandler logic</div><div>&nbsp; &nbsp; for (ParameterMapping parameterMapping : <b>parameterMappings</b>) {</div><div>&nbsp; &nbsp; &nbsp; if (parameterMapping.getMode() != <b>ParameterMode.OUT)</b> {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; Object value;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; String propertyName = parameterMapping.getProperty();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (boundSql.hasAdditionalParameter(propertyName)) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value = boundSql.getAdditionalParameter(propertyName);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; } else if (parameterObject == null) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value = null;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value = parameterObject;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; } else {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MetaObject metaObject = configuration.newMetaObject(parameterObject);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value = metaObject.getValue(propertyName);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; cacheKey.<b>update</b>(value);</div><div>&nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; if (configuration.getEnvironment() != null) {</div><div>&nbsp; &nbsp; &nbsp; // issue #176</div><div>&nbsp; &nbsp; &nbsp; cacheKey.<b>update</b>(configuration.getEnvironment().getId());</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; return cacheKey;</div><div style="font-size: 10px;">}</div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=2;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="760" y="160" width="440" height="480" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-12" value="BaseExecutor" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="930" y="130" width="100" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-13" value="<font color="#007fff">举例:<br><b>hashcode:checksum:MappedStatement.id:offset:limit:sql<br></b>-1868714736:4358584015:top.kwseeker.mybatis.cache.mapper.UserMapper.selectById:0:2147483647:SELECT id, username, password, create_time FROM users WHERE id = ?</font>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1210" y="160" width="810" height="50" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-27" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-14" target="yhzCpn97QrwA5I1gkQgs-26" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1210" y="860" />
<mxPoint x="1210" y="710" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-28" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="yhzCpn97QrwA5I1gkQgs-27" vertex="1" connectable="0">
<mxGeometry x="0.1463" y="-2" relative="1" as="geometry">
<mxPoint x="8" y="-51" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-39" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="yhzCpn97QrwA5I1gkQgs-14" target="yhzCpn97QrwA5I1gkQgs-38" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1210" y="860" />
<mxPoint x="1210" y="1030" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-40" value="2" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="yhzCpn97QrwA5I1gkQgs-39" vertex="1" connectable="0">
<mxGeometry x="0.8262" y="-2" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-117" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="yhzCpn97QrwA5I1gkQgs-14" target="yhzCpn97QrwA5I1gkQgs-118" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1241" y="1150" as="targetPoint" />
<Array as="points">
<mxPoint x="1210" y="860" />
<mxPoint x="1210" y="1160" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-120" value="3" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="yhzCpn97QrwA5I1gkQgs-117" vertex="1" connectable="0">
<mxGeometry x="0.9058" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-14" value="<div style=""><div style="font-size: 10px;">public &lt;E&gt; List&lt;E&gt; <b>query</b>(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)</div><div style="font-size: 10px;">&nbsp; &nbsp; throws SQLException {</div><div style=""><span style="font-size: 10px; background-color: initial;"><font color="#007fff"><b>&nbsp; &nbsp; // 获取二级缓存对象,每个MappedStatement都有自己的二级缓存对象,Cache在MappedStatement Builder 初始化时指定,要么创建新的缓存对象(&lt;cache/&gt; 或 @CacheNamespace),要么引用其他的缓存对象</b></font></span><font color="#007fff"><b>(&lt;cache-ref/&gt; 或 @CacheNamespaceRef)</b></font></div><div style="font-size: 10px;">&nbsp; &nbsp; Cache cache = <b>ms</b>.<b>getCache</b>();</div><div style="font-size: 10px;"><font color="#007fff">&nbsp; &nbsp; // 如果二级缓存不为空,且启用了二级缓存,先查二级缓存</font></div><div style="font-size: 10px;">&nbsp; &nbsp; if (cache != null) {</div><div style="font-size: 10px;"><span style=""><span style="">&nbsp;&nbsp;&nbsp;&nbsp;<span style=""><span style="">&nbsp;&nbsp;&nbsp;&nbsp;</span></span></span></span><font color="#007fff">// 这里是通过 MappedStatement.<b>flushCacheRequired</b> 判断二级缓存是否需要清空, 是则清空缓存</font><br></div><div style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; <b>flushCacheIfRequired</b>(ms);&nbsp;</div><div style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; if (ms.<b>isUseCache</b>() &amp;&amp; resultHandler == null) {</div><div style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ensureNoOutParams(ms, boundSql);</div><div style="font-size: 10px;"><b><font color="#007fff">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //1 使用 CacheKey 从二级缓存中查询缓存的结果</font></b></div><div style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @SuppressWarnings("unchecked")</div><div style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; List&lt;E&gt; list = (List&lt;E&gt;) <b>tcm</b>.<b>getObject</b>(cache, key);</div><div style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (list == null) { <font color="#007fff">// 二级缓存中没有,执行后面逻辑,然后更新二级缓存</font></div><div style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; list = delegate.<b>query</b>(ms, parameterObject, rowBounds, resultHandler, key, boundSql);</div><div style=""><span style="font-size: 10px;"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="font-size: 10px;"><span style="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></span><b style="font-size: 10px;"><font color="#007fff">// 2&nbsp; 这一步缓存并没有将值直接放到&nbsp;</font></b><font color="#007fff"><b>PerpetualCache, 而是先临时放到了 TransactionCache 的&nbsp;</b></font><font color="#007fff"><b>entriesToAddOnCommit</b></font><br></div><div style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <b>tcm</b>.<b>putObject</b>(cache, key, list); // issue #578 and #116</div><div style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return list;</div><div style="font-size: 10px;">&nbsp; &nbsp; &nbsp; &nbsp; }</div><div style="font-size: 10px;">&nbsp; &nbsp; }</div><div style="font-size: 10px;"><b>&nbsp; &nbsp; <font color="#007fff">// 3 没有开启二级缓存继续后面的逻辑(先查一级缓存,没有再执行SQL)</font></b></div><div style="font-size: 10px;">&nbsp; &nbsp; return delegate.<b>query</b>(ms, parameterObject, rowBounds, resultHandler, key, boundSql);</div><div style="font-size: 10px;">}</div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=2;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="760" y="680" width="440" height="360" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-20" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" source="yhzCpn97QrwA5I1gkQgs-18" target="yhzCpn97QrwA5I1gkQgs-14" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1220" y="460" />
<mxPoint x="1220" y="680" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-23" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-18" target="yhzCpn97QrwA5I1gkQgs-22" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-18" value="<div style=""><div style="font-size: 10px;"><div>Cache cache = new <b>CacheBuilder</b>(currentNamespace)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; .implementation(valueOrDefault(typeClass, <b>PerpetualCache</b>.class))</div><div>&nbsp; &nbsp; &nbsp; &nbsp; .addDecorator(valueOrDefault(evictionClass, <b>LruCache</b>.class))</div><div>&nbsp; &nbsp; &nbsp; &nbsp; .clearInterval(flushInterval)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; .size(size)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; .readWrite(readWrite)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; .blocking(blocking)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; .properties(props)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; .<b>build</b>();</div></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=2;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1241" y="400" width="440" height="120" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-19" value="MapperBuilderAssistant" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="1386" y="370" width="150" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-21" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b style="font-size: 11px;">CacheBuilder</b><br style="font-size: 11px;"></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff"><b>缓存实例Builder</b></font></p><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br style="font-size: 11px;"></p><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff">// Mapper类的全限定类名</font></p><p style="margin: 0px 0px 0px 4px;">private final String <b>id</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 最终使用的缓存实例是经过多层装饰器模式装饰的,最内层实现缓存数据的存储,implementation 指定缓存的基本实现类(最内层实现类)</font></p><p style="margin: 0px 0px 0px 4px;">private Class&lt;? extends Cache&gt; <b>implementation</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">// 通过装饰器模式拓展的功能实现类</font></p><p style="margin: 0px 0px 0px 4px;">private final List&lt;Class&lt;? extends Cache&gt;&gt; <b>decorators</b>;</p><p style="margin: 0px 0px 0px 4px;">private Integer size;</p><p style="margin: 0px 0px 0px 4px;">private Long clearInterval;</p><p style="margin: 0px 0px 0px 4px;">private boolean readWrite;</p><p style="margin: 0px 0px 0px 4px;">private Properties properties;</p><p style="margin: 0px 0px 0px 4px;">private boolean blocking;</p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//&nbsp;</font></p><p style="margin: 0px 0px 0px 4px;">public Cache build()<br style="font-size: 11px;"></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-960" y="400" width="440" height="280" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-22" value="<div style=""><div style=""><div style="">public Cache build() {</div><div style="">&nbsp; &nbsp;<font color="#007fff"> // 初始化时可能没有指定缓存的基本实现,就默认使用 <b>PerpetualCache.class</b>, 如果装饰类也没有指定,则默认使用 <b>LruCache.class </b>作为装饰类</font></div><div style="">&nbsp; &nbsp; <b>setDefaultImplementations</b>();</div><div style="">&nbsp; &nbsp; Cache cache = <b>newBaseCacheInstance</b>(implementation, id);</div><div style="">&nbsp; &nbsp; setCacheProperties(cache);</div><div style="">&nbsp; &nbsp; // issue #352, do not apply decorators to custom caches</div><div style="">&nbsp; &nbsp; if (<b>PerpetualCache</b>.class.equals(cache.getClass())) { <font color="#007fff">//只有基础实现是&nbsp;PerpetualCache,才使用装饰类装饰</font></div><div style="">&nbsp; &nbsp; &nbsp; &nbsp; for (Class&lt;? extends Cache&gt; decorator : <b>decorators</b>) {</div><div style="">&nbsp; &nbsp; &nbsp; &nbsp; cache = newCacheDecoratorInstance(decorator, cache);</div><div style="">&nbsp; &nbsp; &nbsp; &nbsp; setCacheProperties(cache);</div><div style="">&nbsp; &nbsp; &nbsp; &nbsp; }</div><div style="">&nbsp; &nbsp; &nbsp; &nbsp; cache = setStandardDecorators(cache);</div><div style="">&nbsp; &nbsp; } else if (!<b>LoggingCache</b>.class.isAssignableFrom(cache.getClass())) { <font color="#007fff">// 非PerpetualCache基础实现</font></div><div style="">&nbsp; &nbsp; &nbsp; &nbsp; cache = new LoggingCache(cache);</div><div style="">&nbsp; &nbsp; }</div><div style="">&nbsp; &nbsp; return cache;</div><div style="">}</div></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=2;fontSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="1720" y="400" width="440" height="240" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-24" value="CacheBuilder" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="1890" y="370" width="100" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-25" value="<div><font color="#007fff"><b>测试Demo中默认创建的缓存实例:</b></font></div><div><font color="#007fff">如果没有对缓存配置做特殊定制,基本都是这样。</font></div><div><font color="#007fff">cache = {<b>SynchronizedCache</b>@6768}&nbsp;</font></div><div><font color="#007fff">&nbsp;delegate = {<b>LoggingCache</b>@6799}&nbsp;</font></div><div><font color="#007fff">&nbsp; log = {Slf4jImpl@6800}&nbsp;</font></div><div><font color="#007fff">&nbsp; delegate = {<b>SerializedCache</b>@6801}&nbsp;</font></div><div><font color="#007fff">&nbsp; &nbsp;delegate = {<b>LruCache</b>@6802}&nbsp;</font></div><div><font color="#007fff">&nbsp; &nbsp; delegate = {<b>PerpetualCache</b>@6803}&nbsp;</font></div><div><font color="#007fff">&nbsp; &nbsp; &nbsp;id = "top.kwseeker.mybatis.cache.mapper.UserMapper"</font></div><div><font color="#007fff">&nbsp; &nbsp; &nbsp;cache = {HashMap@6806}&nbsp; size = 0</font></div><div><font color="#007fff">&nbsp; &nbsp; keyMap = {LruCache$1@6804}&nbsp; size = 0</font></div><div><font color="#007fff">&nbsp; &nbsp; eldestKey = null</font></div><div><font color="#007fff">&nbsp; requests = 0</font></div><div><font color="#007fff">&nbsp; hits = 0</font></div>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="2170" y="400" width="280" height="180" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-33" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-26" target="yhzCpn97QrwA5I1gkQgs-32" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-26" value="<div style=""><div style=""><font color="#007fff">// getTransactionalCache 先查看是否已经为 cache 创建过 TransactionalCache,没有就创建&nbsp;(就是在外面再装饰一层),然后一层层查缓存</font></div><div style="">return <b>getTransactionalCache</b>(cache).<b>getObject</b>(key);<br></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=10;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1241" y="680" width="440" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-29" value="<p style="margin: 4px 0px 0px; text-align: center;"><b>TransactionalCache</b><br></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px;"><b><font color="#007fff">功能:为了实现在commit后刷新到缓存,以及在rollback时清除对应的缓存,以避免出现脏读问题</font></b></p><p style="margin: 0px 0px 0px 4px;"><br></p><p style="margin: 0px 0px 0px 4px;">private final Cache <b>delegate</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">//执行clear()后设置为true(clear()中只是清理临时缓存 entriesToAddOnCommit),用于标记commit时是否需要清理 delegate 的缓存</font></p><p style="margin: 0px 0px 0px 4px;">private boolean <b>clearOnCommit</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff"><b>// 存储在commit后需要放到真正的缓存中的数据,此类的putObject其实是将值临时缓存到这里,并没有直接放到 PerpetualCache</b></font></p><p style="margin: 0px 0px 0px 4px;">private final Map&lt;Object, Object&gt; <b>entriesToAddOnCommit</b>;</p><p style="margin: 0px 0px 0px 4px;"><font color="#007fff"><b>// 未命中的缓存,在提交阶段也会存储到缓存中,值设置为空</b></font></p><p style="margin: 0px 0px 0px 4px;">private final Set&lt;Object&gt; <b>entriesMissedInCache</b>;</p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br style="font-size: 11px;"></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="-960" y="1080" width="440" height="240" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-83" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;endArrow=open;endFill=0;" parent="1" source="yhzCpn97QrwA5I1gkQgs-30" target="yhzCpn97QrwA5I1gkQgs-29" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-30" value="<p style="margin: 4px 0px 0px; text-align: center;"><b>TransactionalCacheManager</b><br></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px;"><font color="#007fff">事务缓存的容器,会为每个Cache实例创建一个对应的 <b>TransactionalCache</b>,TransactionalCache 是在Cache最外边又装饰了一层</font></p><p style="margin: 0px 0px 0px 4px;"><br></p><p style="margin: 0px 0px 0px 4px;">private final Map&lt;Cache, TransactionalCache&gt; <b>transactionalCaches</b> = new HashMap&lt;&gt;();<br></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br style="font-size: 11px;"></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="-480" y="1080" width="440" height="160" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-31" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b style="font-size: 11px;">Cache</b></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br style="font-size: 11px;"></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="-960" y="720" width="440" height="80" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-35" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-32" target="yhzCpn97QrwA5I1gkQgs-34" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-37" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-32" target="yhzCpn97QrwA5I1gkQgs-36" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-32" value="<div style=""><div style=""><b>TransactionalCache</b>#getObject(key)</div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=center;arcSize=10;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1720" y="680" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-34" value="<div style=""><div style=""><b>SynchronziedCache</b>#getObject(key).<br><font color="#007fff">...&nbsp; 逐级查询 ...</font></div><div style=""><b>PerpetualCache</b>#getObject(key)<br></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=center;arcSize=10;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1720" y="860" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-36" value="<div style=""><div style=""><div>public Object getObject(Object key) {</div><div><font color="#007fff">&nbsp; &nbsp; // issue #116</font></div><div>&nbsp; &nbsp; Object object = <b>delegate</b>.<b>getObject</b>(key);</div><div>&nbsp; &nbsp; if (object == null) {&nbsp; <font color="#007fff">//缓存未命中</font></div><div>&nbsp; &nbsp; &nbsp; &nbsp; entriesMissedInCache.add(key);</div><div>&nbsp; &nbsp; }</div><div><font color="#007fff">&nbsp; &nbsp; // issue #146</font></div><div>&nbsp; &nbsp; if (<b>clearOnCommit</b>) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return null;</div><div>&nbsp; &nbsp; } else {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return object;</div><div>&nbsp; &nbsp; }</div><div>}</div></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=4;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1960" y="680" width="440" height="160" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-42" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-43" target="yhzCpn97QrwA5I1gkQgs-41" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-38" value="<div style=""><div style=""><font color="#007fff">// getTransactionalCache 先查看是否已经为 cache 创建过 TransactionalCache,没有就创建&nbsp;(就是在外面再装饰一层),然后将结果缓存</font></div><div style=""><b>getTransactionalCache</b>(cache).<b>putObject</b>(key, value);<br></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=10;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1241" y="1000" width="440" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-41" value="<div style=""><div style=""><font color="#007fff">没有将值直接放到 PerpetualCache, 而是先临时放到了 TransactionCache 的 entriesToAddOnCommit<br></font></div><div style=""><b>entriesToAddOnCommit</b>.<b>put</b>(key, object);<br></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=10;fontSize=10;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1">
<mxGeometry x="1960" y="1000" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-44" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-38" target="yhzCpn97QrwA5I1gkQgs-43" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1681" y="1030" as="sourcePoint" />
<mxPoint x="1960" y="1030" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-43" value="<div style=""><div style=""><b>TransactionalCache</b>#putObject(key, value)</div><div style=""><font color="#007fff"><b>将数据暂存到 TransactionCache 本地</b></font></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=center;arcSize=10;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1720" y="1000" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-47" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-45" target="yhzCpn97QrwA5I1gkQgs-46" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-45" value="<div style=""><div style=""><b>TransactionalCache</b>#<b>commit</b>()</div><div style=""><b><font color="#007fff">真正执行数据缓存, 将 TransactionCache 暂存的数据最终保存到&nbsp;</font></b><font color="#007fff"><b>PerpetualCache</b></font></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=center;arcSize=10;fontSize=10;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="1" vertex="1">
<mxGeometry x="1720" y="1640" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-49" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-46" target="yhzCpn97QrwA5I1gkQgs-48" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-46" value="<div style=""><div style=""><div>public void commit() {</div><div>&nbsp; &nbsp;<font color="#007fff"> //update操作处理中会设置 clearOnCommit = true</font></div><div>&nbsp; &nbsp; if (clearOnCommit) {&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; delegate.clear();</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; <b>flushPendingEntries</b>();</div><div>&nbsp; &nbsp; reset();</div><div>}</div></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=10;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1960" y="1610" width="200" height="120" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-59" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="yhzCpn97QrwA5I1gkQgs-48" target="yhzCpn97QrwA5I1gkQgs-50" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-48" value="<div style=""><div style=""><div>private void flushPendingEntries() {</div><div>&nbsp; &nbsp; for (Map.Entry&lt;Object, Object&gt; entry : <b>entriesToAddOnCommit</b>.entrySet()) {</div><div><font color="#007fff"><b>&nbsp; &nbsp; &nbsp; &nbsp; // 这里才真正的将缓存数据写到 PerpetualCache 中</b></font></div><div>&nbsp; &nbsp; &nbsp; &nbsp; <b>delegate</b>.<b>putObject</b>(entry.getKey(), entry.getValue());</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; for (Object entry : <b>entriesMissedInCache</b>) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (!entriesToAddOnCommit.containsKey(entry)) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delegate.putObject(entry, null);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; }</div><div>}</div></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=3;fontSize=10;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1">
<mxGeometry x="2200" y="1600" width="440" height="140" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-55" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-50" target="yhzCpn97QrwA5I1gkQgs-51" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-50" value="<div style=""><div style=""><b style="border-color: var(--border-color);">SynchronziedCache</b>#putObject(key, value)<br></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=center;arcSize=10;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1720" y="1780" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-56" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-51" target="yhzCpn97QrwA5I1gkQgs-52" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-51" value="<div style=""><div style=""><b style="border-color: var(--border-color);">LoggingCache</b>#putObject(key, value)<br></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=center;arcSize=10;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1720" y="1880" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-57" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-52" target="yhzCpn97QrwA5I1gkQgs-53" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-52" value="<div style=""><div style=""><b style="border-color: var(--border-color);">SerializedCache</b>#putObject(key, value)<br></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=center;arcSize=10;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1720" y="1980" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-58" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-53" target="yhzCpn97QrwA5I1gkQgs-54" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-53" value="<div style=""><div style=""><b style="border-color: var(--border-color);">LruCache</b>#putObject(key, value)<br></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=center;arcSize=10;fontSize=10;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="1720" y="2080" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-54" value="<div style=""><div style=""><b style="border-color: var(--border-color);">PerpetualCache</b>#putObject(key, value)<br></div><div style=""><font color="#007fff">真正的缓存数据容器</font></div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=center;arcSize=10;fontSize=10;fillColor=#e3c800;strokeColor=#B09500;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="1720" y="2180" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-62" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-60" target="yhzCpn97QrwA5I1gkQgs-61" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-60" value="<b>事务提交</b><br><font style="" color="#007fff">非自动事务提交是在 CachingExecutor#query() 外面且是之后执行的,最终是怎么触发执行 TransactionalCache#commit()的</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="40" y="1620" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-66" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-61" target="yhzCpn97QrwA5I1gkQgs-65" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-61" value="commitTransactionAfterReturning(txInfo);" style="rounded=1;whiteSpace=wrap;html=1;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="280" y="1620" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-63" value="<font color="#007fff" style="font-size: 10px;">在 commit() 中加断点查调用栈<br style="font-size: 10px;"><div style="font-size: 10px;"><b style="font-size: 10px;">commit</b>:96, <b style="font-size: 10px;">TransactionalCache</b> (org.apache.ibatis.cache.decorators)</div><div style="font-size: 10px;">commit:45, TransactionalCacheManager (org.apache.ibatis.cache)</div><div style="font-size: 10px;">commit:120, CachingExecutor (org.apache.ibatis.executor)</div><div style="font-size: 10px;">commit:220, DefaultSqlSession (org.apache.ibatis.session.defaults)</div><div style="font-size: 10px;">commit:214, DefaultSqlSession (org.apache.ibatis.session.defaults)</div><div style="font-size: 10px;">beforeCommit:283, SqlSessionUtils$SqlSessionSynchronization (org.mybatis.spring)</div><div style="font-size: 10px;">triggerBeforeCommit:97, TransactionSynchronizationUtils (org.springframework.transaction.support)</div><div style="font-size: 10px;"><b style="font-size: 10px;">triggerBeforeCommit</b>:916, <b style="font-size: 10px;">AbstractPlatformTransactionManager</b> (org.springframework.transaction.support)</div><div style="font-size: 10px;">processCommit:727, AbstractPlatformTransactionManager (org.springframework.transaction.support)</div><div style="font-size: 10px;">commit:711, AbstractPlatformTransactionManager (org.springframework.transaction.support)</div><div style="font-size: 10px;"><b style="font-size: 10px;">commitTransactionAfterReturning</b>:654, <b style="font-size: 10px;">TransactionAspectSupport</b> (org.springframework.transaction.interceptor)</div><div style="font-size: 10px;">invokeWithinTransaction:407, TransactionAspectSupport (org.springframework.transaction.interceptor)</div><div style="font-size: 10px;">invoke:119, <b style="font-size: 10px;">TransactionInterceptor</b> (org.springframework.transaction.interceptor)</div><div style="font-size: 10px;">proceed:186, ReflectiveMethodInvocation (org.springframework.aop.framework)</div><div style="font-size: 10px;">proceed:763, CglibAopProxy$CglibMethodInvocation (org.springframework.aop.framework)</div><div style="font-size: 10px;">intercept:708, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)</div><div style="font-size: 10px;">updateById:-1, UserService$$EnhancerBySpringCGLIB$$21c4c479 (top.kwseeker.mybatis.cache.service)</div><div style="font-size: 10px;">lambda$testCache$1:33, <b style="font-size: 10px;">UserServiceTest</b> (top.kwseeker.mybatis.cache.service)</div></font>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="40" y="1820" width="540" height="240" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-64" value="spring-tx:TransactionAspectSupport" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="275" y="1590" width="210" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-69" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-65" target="yhzCpn97QrwA5I1gkQgs-68" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-65" value="<div style=""><b style="background-color: initial;"><font color="#007fff">//Mybatis 事务缓存处理是在提交前一刻</font></b></div><b>triggerBeforeCommit</b>(status);<br>...<br><b>doCommit</b>(status);<br>..." style="rounded=1;whiteSpace=wrap;html=1;fontSize=10;align=left;" parent="1" vertex="1">
<mxGeometry x="520" y="1620" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-67" value="spring-tx:<br>AbstractPlatformTransactionManager" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="510" y="1580" width="220" height="40" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-72" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-68" target="yhzCpn97QrwA5I1gkQgs-71" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="980" y="1650" />
<mxPoint x="980" y="1700" />
<mxPoint x="260" y="1700" />
<mxPoint x="260" y="1770" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-79" value="<font color="#007fff">回归到Mybatis的处理</font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="yhzCpn97QrwA5I1gkQgs-72" vertex="1" connectable="0">
<mxGeometry x="-0.0376" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-68" value="<font color="#007fff"><b>// Mybatis 事务缓存处理通过实现 SpringTx</b><br><b>的 TransactionSynchronization 拓展接口实现</b></font><br><b>synchronization.beforeCommit(</b><br><b>readOnly);</b><div style=""></div>" style="rounded=1;whiteSpace=wrap;html=1;fontSize=10;align=left;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="760" y="1620" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-70" value="spring-tx:TransactionSynchronizationUtils" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="740" y="1590" width="240" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-75" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-71" target="yhzCpn97QrwA5I1gkQgs-74" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-71" value="<b style="">this.holder.getSqlSession().commit();</b><br><div style=""></div>" style="rounded=1;whiteSpace=wrap;html=1;fontSize=10;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="280" y="1740" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-73" value="SqlSessionUtils" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="325" y="1710" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-78" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-74" target="yhzCpn97QrwA5I1gkQgs-77" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-74" value="executor.commit(<br>isCommitOrRollbackRequired(force));<br><b><font color="#007fff">executor 即 CachingExecutor 实例<br></font></b><font color="#007fff"><b>所以Mybatis DefaultSqlSession 中的提交并不是真正的事务提交只是提交前置处理</b></font><br><div style=""></div>" style="rounded=1;whiteSpace=wrap;html=1;fontSize=10;align=center;" parent="1" vertex="1">
<mxGeometry x="521" y="1740" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-76" value="DefaultSqlSession" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="561" y="1710" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-81" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="yhzCpn97QrwA5I1gkQgs-77" target="yhzCpn97QrwA5I1gkQgs-45" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1241" y="1770" as="targetPoint" />
<Array as="points">
<mxPoint x="1220" y="1770" />
<mxPoint x="1220" y="1670" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-77" value="<div>public void commit() {</div><div>&nbsp; &nbsp; for (TransactionalCache txCache : <b>transactionalCaches</b>.values()) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; txCache.commit();</div><div>&nbsp; &nbsp; }</div><div>}</div><div style=""></div>" style="rounded=1;whiteSpace=wrap;html=1;fontSize=10;align=left;arcSize=6;" parent="1" vertex="1">
<mxGeometry x="760" y="1740" width="440" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-82" value="TransactionalCacheManager" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="890" y="1710" width="180" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-93" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=block;endFill=1;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-84" target="yhzCpn97QrwA5I1gkQgs-31" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-84" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b>SynchronizedCache</b></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff">为了保证缓存访问的线程安全,在方法前面加了synchronized</font></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-280" y="840" width="240" height="80" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-92" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;endArrow=block;endFill=1;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-85" target="yhzCpn97QrwA5I1gkQgs-31" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="-740" y="800" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-85" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b>LoggingCache</b></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff">用于统计缓存命中率</font></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-560" y="840" width="240" height="80" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-91" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;endArrow=block;endFill=1;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-86" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="-740" y="800" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-86" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b>SerializedCache</b></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><br></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-840" y="840" width="240" height="80" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-90" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=block;endFill=1;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-87" target="yhzCpn97QrwA5I1gkQgs-31" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-87" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b>LruCache</b></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff">使用LRU算法防止缓存内存溢出</font></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-1120" y="840" width="240" height="80" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-89" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;endArrow=block;endFill=1;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-88" target="yhzCpn97QrwA5I1gkQgs-31" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-88" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b>PerpetualCache</b></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff"><b>真正存储缓存数据的实现,perpetual: 持久的</b></font></p><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff"><b><br></b></font></p><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff"><b>// Mapper 类的全限定名,所以每个Mapper有一个Cache实例</b></font></p><p style="margin: 0px 0px 0px 4px;">private final String <b>id</b>;</p><p style="margin: 0px 0px 0px 4px;"><b><font color="#007fff">// Key: CacheKey实例, Value: 查询结果</font></b></p><p style="margin: 0px 0px 0px 4px;"></p><p style="margin: 0px 0px 0px 4px;">private final Map&lt;Object, Object&gt; <b>cache</b> = new HashMap&lt;&gt;();</p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="-1600" y="840" width="440" height="160" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-94" value="<b>问题总结:<br><br></b>1.&nbsp;为何两个事务中同时执行同一条查询语句使用二级缓存不会出现脏读?<br><font color="#007fff">从源码可知事务中查询语句的结果在事务提交前会被临时存储在 TransactionCache.entriesToAddOnCommit 中,<br>只有在提交前一刻才会真正迁移到真正的缓存对象(比如PerpetualCache)中;<br>且事务中查询二级缓存不会查 TransactionCache 中的临时存储;<br>所以事务提交前其他事务是不会看到这个事务未提交的数据。</font><br><br>2.&nbsp;二级缓存联表查询数据不一致问题产生的原因?<br><font color="#007fff"><b>因为一个命名空间中对某个表某条记录的写操作不会刷新另一个命名空间对此表同一条记录的查询缓存;<br></b>跨命名空间查询修改同一个表,当然是因为发生了联表操作(不过这里的联表不是说一定是join操作,<br>还可能是子查询、UNION)。</font><br><br>3. 为什么说二级缓存是基于 namespace 的?<br><font color="#007fff">从PerpetualCache源码看二级缓存是基于 </font><b style="color: rgb(0, 127, 255);">Mapper 类的全限定名</b><font color="#007fff">,</font><b style="color: rgb(0, 127, 255);">每个 Mapper 类有一个自己的缓存实例</b><font color="#007fff">; </font><br><font color="#007fff">而Mapper类的全限定名在Mapper xml 文件中被称为 namespace;&nbsp;</font><br><font color="#007fff">比如:</font><br><font color="#007fff">&lt;mapper </font><b style="color: rgb(0, 127, 255);">namespace</b><font color="#007fff">="org.apache.ibatis.submitted.rounding.Mapper"&gt;&nbsp;</font><br><br>4. 注意写操作清除两级缓存是清除的<b>整个 Cache 实例的缓存</b>,并不是只清除受到影响的某些 Statement 的查询缓存;<br>可能后者实现太复杂了,需要评估写操作到底会影响哪些语句的结果不是容易的事。<br><br>5. 如何理解一级缓存默认是 Session 共享的<br><font color="#007fff">看 Mybatis 创建 Session 的源码可以看到每次创建 Session 都会新建 Executor 实例,而一级缓存是保存在 BaseExecutor</font><br><font color="#007fff">中的(一个字段),所以说每个 Session 都有自己的一级缓存实例,同一个 Session 对缓存的操作是操作的同一个一级缓存实例。</font><br><br>6. SpringBoot 集成 Mybatis 为何非事务方式连续执行同一条查询两次第二次却不会命中一级缓存<br><font color="#007fff">一级缓存默认是同 Session 共享的,但是上面情况非事务方式连续执行两条查询,每条查询都创建了不同的Session,<br>即每条语句使用的不同的一级缓存。<br>参考Spring事务工作原理流程图,查看通过事务管理器中获取 SqlSession 的代码。<br></font>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="40" y="335" width="710" height="450" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-96" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-95" target="yhzCpn97QrwA5I1gkQgs-31" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-580" y="820" />
<mxPoint x="-740" y="820" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-95" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b>ScheduledCache</b></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff">为了保证缓存访问的线程安全,在方法前面加了synchronized</font></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-700" y="960" width="240" height="80" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-98" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-97" target="yhzCpn97QrwA5I1gkQgs-31" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-1000" y="940" />
<mxPoint x="-860" y="940" />
<mxPoint x="-860" y="820" />
<mxPoint x="-740" y="820" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-97" value="<p style="margin: 4px 0px 0px; text-align: center; font-size: 11px;"><b>FifoCache</b></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px; font-size: 11px;"><font color="#007fff">使用FIFO算法防止缓存内存溢出</font></p><hr style="font-size: 11px;"><p style="margin: 0px 0px 0px 4px;"><br></p>" style="verticalAlign=top;align=left;overflow=fill;fontSize=11;fontFamily=Helvetica;html=1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="-1120" y="960" width="240" height="80" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-100" value="<font color="#007fff">CacheKey举例:<br><b>hashcode:checksum:MappedStatement.id:offset:limit:sql<br></b>-1868714736:4358584015:top.kwseeker.mybatis.cache.mapper.UserMapper.selectById:0:2147483647<br>:SELECT id, username, password, create_time FROM users WHERE id = ?</font>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="-480" y="685" width="480" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-103" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-101" target="yhzCpn97QrwA5I1gkQgs-102" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-101" value="<b>CachingExecutor</b>#<b>update</b>(<br>MappedStatement ms, Object parameterObject)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="40" y="2138" width="200" height="60" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-105" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-102" target="yhzCpn97QrwA5I1gkQgs-104" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-107" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="yhzCpn97QrwA5I1gkQgs-102" target="yhzCpn97QrwA5I1gkQgs-106" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-102" value="<div>public int update(MappedStatement ms, Object parameterObject) throws SQLException {</div><div>&nbsp; &nbsp; <b>flushCacheIfRequired</b>(ms);</div><div>&nbsp; &nbsp; return delegate.<b>update</b>(ms, parameterObject);</div><div>}</div>" style="rounded=1;whiteSpace=wrap;html=1;fontSize=10;align=left;" parent="1" vertex="1">
<mxGeometry x="280" y="2128" width="440" height="80" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-114" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.75;entryDx=0;entryDy=0;dashed=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-104" target="yhzCpn97QrwA5I1gkQgs-45" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1240.5" y="2240" as="targetPoint" />
<Array as="points">
<mxPoint x="1240" y="2240" />
<mxPoint x="1240" y="1685" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-115" value="<font color="#007fff">提交时执行PerpetualCache的清理</font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="yhzCpn97QrwA5I1gkQgs-114" vertex="1" connectable="0">
<mxGeometry x="-0.8987" y="-1" relative="1" as="geometry">
<mxPoint x="59" y="-5" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-104" value="<div><font color="#007fff"><b>// 清理二级缓存,而且是清理的整个缓存,并不是只清理这个Statement的缓存</b></font></div><div>private void flushCacheIfRequired(MappedStatement ms) {</div><div>&nbsp; &nbsp; Cache cache = ms.<b>getCache</b>();</div><div>&nbsp; &nbsp; if (cache != null &amp;&amp; ms.isFlushCacheRequired()) {</div><div><font color="#007fff"><b><span style=""><span style="white-space: pre;"> &nbsp;&nbsp;&nbsp;&nbsp;</span></span>// TransactionCache#clear() 仅仅是清理本地的临时缓存不会清理 PerpetualCache 中的缓存<br><span style=""><span style="white-space: pre;">&nbsp;&nbsp;&nbsp;&nbsp; </span></span>// PerpetualCache 中的缓存真正清除是在当前事务提交的时候</b></font></div><div>&nbsp; &nbsp; &nbsp; &nbsp; tcm.<b>clear</b>(cache);</div><div>&nbsp; &nbsp; }</div><div>}</div>" style="rounded=1;whiteSpace=wrap;html=1;fontSize=10;align=left;arcSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="760" y="2128" width="440" height="120" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-109" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-106" target="yhzCpn97QrwA5I1gkQgs-108" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-106" value="<div>public int update(MappedStatement ms, Object parameter) throws SQLException {</div><div>&nbsp; &nbsp; ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());</div><div>&nbsp; &nbsp; if (closed) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; throw new ExecutorException("Executor was closed.");</div><div>&nbsp; &nbsp; }</div><div><font color="#007fff"><b>&nbsp; &nbsp; // 清理一级缓存</b></font></div><div>&nbsp; &nbsp; <b>clearLocalCache</b>();</div><div>&nbsp; &nbsp; return doUpdate(ms, parameter);</div><div>}</div>" style="rounded=1;whiteSpace=wrap;html=1;fontSize=10;align=left;arcSize=10;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="760" y="2288" width="440" height="140" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-108" value="<div><font color="#007fff">// 后面操作不涉及缓存</font></div><div>public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {</div><div>&nbsp; &nbsp; Statement stmt = null;</div><div>&nbsp; &nbsp; try {</div><div>&nbsp; &nbsp; &nbsp; Configuration configuration = ms.getConfiguration();</div><div>&nbsp; &nbsp; &nbsp; StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);</div><div>&nbsp; &nbsp; &nbsp; stmt = prepareStatement(handler, ms.getStatementLog());</div><div>&nbsp; &nbsp; &nbsp; return handler.update(stmt);</div><div>&nbsp; &nbsp; } finally {</div><div>&nbsp; &nbsp; &nbsp; closeStatement(stmt);</div><div>&nbsp; &nbsp; }</div><div>&nbsp; }</div>" style="rounded=1;whiteSpace=wrap;html=1;fontSize=10;align=left;arcSize=10;" parent="1" vertex="1">
<mxGeometry x="1241" y="2278" width="440" height="160" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-110" value="BaseExecutor" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="930" y="2258" width="100" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-111" value="SimpleExecutor" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="1406" y="2248" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-112" value="<font color="#007fff">从后面流程可以看到,写操作只会清理自己这条语句所在命名空间对应的缓存实例;&nbsp;<br></font><div style=""><font color="#007fff">即便其他命名空间也可能查询这里修改的表,也不会刷新它们的缓存,这时就可能查询到过期的数据</font></div>" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="40" y="2218" width="550" height="40" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-123" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="yhzCpn97QrwA5I1gkQgs-118" target="yhzCpn97QrwA5I1gkQgs-122" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-118" value="<div style="font-size: 10px;">public &lt;E&gt; List&lt;E&gt; <b style="font-size: 10px;">query</b>(MappedStatement ms, Object parameterObject, <br style="font-size: 10px;">RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {</div><div style="font-size: 10px;"><span style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">&nbsp; &nbsp; // 参考MySQL整体流程图</font></span></div><div style="font-size: 10px;">&nbsp; &nbsp; BoundSql boundSql = ms.<b style="font-size: 10px;">getBoundSql</b>(parameterObject);</div><div style="font-size: 10px;"><b style="font-size: 10px;"><font color="#007fff" style="font-size: 10px;">&nbsp; &nbsp; // 1 这次是创建一级缓存的key,两级缓存用的 CacheKey 是相同的</font></b></div><div style="font-size: 10px;">&nbsp; &nbsp; CacheKey key = this.<b style="font-size: 10px;">createCacheKey</b>(ms, parameterObject, rowBounds, boundSql);</div><div style="font-size: 10px;"><b><font color="#007fff">&nbsp; &nbsp; // 2 内部实现第一级缓存操作</font></b></div><div style="font-size: 10px;">&nbsp; &nbsp; return this.<b style="font-size: 10px;">query</b>(ms, parameterObject, rowBounds, resultHandler, key, boundSql);</div><div style="font-size: 10px;">}</div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=4;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1241" y="1100" width="439" height="120" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-119" value="BaseExecutor" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="450" y="130" width="100" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-121" value="CachingExecutor" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="925" y="650" width="110" height="30" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-125" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;fontColor=#007FFF;" parent="1" source="yhzCpn97QrwA5I1gkQgs-122" target="yhzCpn97QrwA5I1gkQgs-124" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-122" value="<div style="font-size: 10px;"><div>public &lt;E&gt; List&lt;E&gt; <b>query</b>(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {</div><div>&nbsp; &nbsp; ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());</div><div>&nbsp; &nbsp; if (closed) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; throw new ExecutorException("Executor was closed.");</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; if (queryStack == 0 &amp;&amp; ms.isFlushCacheRequired()) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; <b>clearLocalCache</b>();</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; List&lt;E&gt; list;</div><div>&nbsp; &nbsp; try {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; queryStack++;</div><div><font color="#007fff"><b>&nbsp; &nbsp; &nbsp; &nbsp; // 先查询一级缓存</b></font></div><div><font color="#007fff"><b><span style="white-space: pre;">	</span>// 一级缓存直接通过&nbsp;PerpetualCache 直接实现,即本质就只是一个HashMap</b></font></div><div>&nbsp; &nbsp; &nbsp; &nbsp; list = resultHandler == null ? (List&lt;E&gt;) <b>localCache.getObject</b>(key) : null;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (list != null) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; } else {</div><div><b><font color="#007fff"><span style="white-space: pre;">	</span>&nbsp; &nbsp; // 1 内部更新一级缓存</font><br></b></div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; list = <b>queryFromDatabase</b>(ms, parameter, rowBounds, resultHandler, key, boundSql);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; } finally {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; queryStack--;</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; if (queryStack == 0) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; for (DeferredLoad deferredLoad : deferredLoads) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; deferredLoad.load();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; // issue #601</div><div>&nbsp; &nbsp; &nbsp; &nbsp; deferredLoads.clear();</div><div><span style=""><span style="">&nbsp;&nbsp;&nbsp; <font color="#007fff">&nbsp; &nbsp; </font></span></span><font color="#007fff">// LocalCacheScope.STATEMENT SQL执行完毕立即清理,即这类型的缓存只用查询内部</font><br></div><div>&nbsp; &nbsp; &nbsp; &nbsp; if (configuration.<b>getLocalCacheScope</b>() == LocalCacheScope.<b>STATEMENT</b>) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // issue #482</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <b>clearLocalCache</b>();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; }</div><div>&nbsp; &nbsp; return list;</div><div>}</div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=2;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="1720" y="1100" width="440" height="480" as="geometry" />
</mxCell>
<mxCell id="yhzCpn97QrwA5I1gkQgs-124" value="<div style=""><div style="">private &lt;E&gt; List&lt;E&gt; queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {</div><div style="">&nbsp; &nbsp; this.<b>localCache</b>.<b>putObject</b>(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);</div><div style=""><br></div><div style="">&nbsp; &nbsp; List list;</div><div style="">&nbsp; &nbsp; try {</div><div style="">&nbsp; &nbsp; &nbsp; &nbsp; list = this.<b>doQuery</b>(ms, parameter, rowBounds, resultHandler, boundSql);</div><div style="">&nbsp; &nbsp; } finally {</div><div style=""><font color="#007fff"><span style="white-space: pre;">	</span>// ???为何查询前先缓存占位符,查询完再清除</font><br></div><div style="">&nbsp; &nbsp; &nbsp; &nbsp; this.localCache.<b>removeObject</b>(key);</div><div style="">&nbsp; &nbsp; }</div><div style=""><font color="#007fff">&nbsp; &nbsp; // 一级缓存写</font></div><div style="">&nbsp; &nbsp; this.localCache.<b>putObject</b>(key, list);</div><div style="">&nbsp; &nbsp; if (ms.getStatementType() == StatementType.CALLABLE) {<font color="#007fff"> //如果是存储过程调用</font></div><div style="">&nbsp; &nbsp; &nbsp; &nbsp; this.<b>localOutputParameterCache</b>.putObject(key, parameter);</div><div style="">&nbsp; &nbsp; }</div><div style=""><br></div><div style="">&nbsp; &nbsp; return list;</div><div style="">}</div></div>" style="rounded=1;whiteSpace=wrap;html=1;align=left;arcSize=3;fontSize=10;" parent="1" vertex="1">
<mxGeometry x="2200" y="1100" width="439" height="240" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>