mybatis多次查询缓存问题

来源:互联网 发布:exe 端口 编辑:程序博客网 时间:2024/04/28 15:08

最近在使用mybatis的过程中,发现一个问题。如果在同一个事物中,多次同一个查询sql在mybatis的执行过程中,只会查询一次数据库,后几次所返回的对象是mybatis在在内部做了缓存。

Property property = this.findByPropertyId("123");    property.setPropertyId(null);;    property = this.findByPropertyId("123");    System.out.println(property.getPropertyId());  

以上的代码,打印的结果为 null , 但是我们所期望的可能是 123 , 我不知道这是mybatis的一个bug还是故意这样去设计的.mybatis在执行查询语句的时候,会在本地做一份缓存信息.在BaseExecutor类中:

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;    }  

可以看到在queryFromDatabase方法中,查询数据库返回结果之后,mybatis编制了一个cachekey的对象,作为key,返回结果作为value,放入了缓存当中(这个地方没有使用拷贝的函数,所以只要外部修改了值,内部缓存中的值信息也会被修改)

之后再下次查询的时候,会依据一个判断,是否需要执行缓存信息,同样是在BaseExecutor类中

@SuppressWarnings("unchecked")    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();        }        deferredLoads.clear(); // issue #601        if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {          clearLocalCache(); // issue #482        }      }      return list;    } 

看到mybatis判断了 ms.isFlushCacheRequired() 的返回数据,如果为 true 会执行 clearLocalCache 方法,清空缓存信息。如果缓存中获取不到的话,才会继续去查询数据库。
可以从 list = resultHandler == null ? (List) localCache.getObject(key) : null; 代码中看出。

所以当第一次查询放入缓存之后,在外部修改了任何一个值之后,mybatis内部缓存的值也会被修改,而且下次查询不会查询数据库,直接返回缓存中被修改过的值 。如果在外部把该值删除后,缓存值会变为空对象,此时如果在次执行该dao方法,会返回一个空的对象,不会去查询数据库。

ms.isFlushCacheRequired() 这段代码的判断是基于了一个MappedStatement 类中的flushCacheRequired 的属性做判断的。flushCacheRequired 变量可以通过注解的方式和xml的方式来配置

1.注解:注解的方式是通过 @Options 注解中 flushCache 的配置
2.配置文件:xml中每一个select 都可以设置 flushCache 的属性

flushCache 设置成true之后,本sql的每次查询都会清空缓存后在执行。

文章摘自:http://www.lai18.com/content/3151250.html

0 0
原创粉丝点击