之前写了一个Mybatis的读写分离插件,今天使用过程中发现1个奇怪的问题
就是如果在一个线程里同时调用某个函数2次,比如这样的
// Select
role = userMapper.getRole0(13);
LOGGER.info("---------------------------------------------------");
role = userMapper.getRole0(13);
发现第2次没有走我之前的插件机制,然后我 debug了一下,发现问题在这里
Step completed: "thread=main", org.apache.ibatis.executor.BaseExecutor.query(), line=152 bci=68
152 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
main[1] !!
next
>
Step completed: "thread=main", org.apache.ibatis.executor.BaseExecutor.query(), line=153 bci=91
153 if (list != null) {
main[1] !!
next
>
Step completed: "thread=main", org.apache.ibatis.executor.BaseExecutor.query(), line=154 bci=96
154 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
貌似走了本地缓存,打上断点看看
stop in org.apache.ibatis.executor.BaseExecutor.query
观察2次有什么区别,现象如下:
第1次执行
Step completed: "thread=main", org.apache.ibatis.executor.BaseExecutor.query(), line=152 bci=68
152 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
main[1] print localCache
localCache = "org.apache.ibatis.cache.impl.PerpetualCache@c2f3a6f7"
main[1] next
>
Step completed: "thread=main", org.apache.ibatis.executor.BaseExecutor.query(), line=153 bci=91
153 if (list != null) {
main[1] print list
list = null
第一次,肯定是找不到的
果然走了本地缓存,所以问题也找到了,根本原因就是
@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++;
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;
}
问题找到了,那么
onfiguration.getLocalCacheScope()怎么控制呢?
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.session;
/**
* @author Eduardo Macarron
*/
public enum LocalCacheScope {
SESSION,STATEMENT
}
默认是session级别的,所以这里我要改成statement级别的!!!
=============怎么改?
所以,只要在配置文件里增加
<setting name="localCacheScope" value="STATEMENT" />
测试通过!不会再用本地缓存了。