【mybatis】多次查询缓存的问题

转自:http://cheng-xinwei.iteye.com/blog/2021700?utm_source=tuicool&utm_medium=referral

     最近在使用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<E>) localCache.getObject(key) : null; 代码中看出。 

   所以当第一次查询放入缓存之后,在外部修改了任何一个值之后,mybatis内部缓存的值也会被修改,而且下次查询不会查询数据库,直接返回缓存中被修改过的值 


    ms.isFlushCacheRequired() 这段代码的判断是基于了一个MappedStatement 类中的flushCacheRequired 的属性做判断的。flushCacheRequired  变量可以通过注解的方式和xml的方式来配置 
    
    1.注解:注解的方式是通过 @Options 注解中 flushCache 的配置 
    2.配置文件:xml中每一个select 都可以设置 flushCache 的属性 

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

posted @ 2016-08-05 18:03  horizon~~~  阅读(4553)  评论(1编辑  收藏  举报