轉自: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的每次查詢都會清空緩存后在執行。