問題描述
在MyBatis中,不同SqlSession作用域中開啓了兩個相同的查詢操作。但是在控制檯的輸出中一直顯示沒有命中緩存,持續進行SQL查詢操作。
代碼如下:
public class DaoTest {
private static final Logger LOGGER = Logger.getLogger(DaoTest.class);
private SqlSessionFactory getFactory() throws IOException {
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
return factory;
}
@Test
public void testGlobalCache() throws IOException {
SqlSession sess1 = getFactory().openSession();
SqlSession sess2 = getFactory().openSession();
SqlSession sess3 = getFactory().openSession();
try {
EmployeeMapper mapper1 = sess1.getMapper(EmployeeMapper.class);
Employee emp1 = mapper1.getEmpByID(1);
sess1.close();
EmployeeMapper mapper2 = sess2.getMapper(EmployeeMapper.class);
Employee emp2 = mapper2.getEmpByID(1);
sess2.close();
EmployeeMapper mapper3 = sess3.getMapper(EmployeeMapper.class);
mapper3.insertEmp(new Employee(null, "XX", "F", "XXX"));
Employee emp3 = mapper3.getEmpByID(1);
sess3.close();
LOGGER.info("emp1 == emp2: " + String.valueOf(emp1 == emp2));
LOGGER.info("emp1 == emp3: " + String.valueOf(emp1 == emp3));
LOGGER.info("emp2 == emp3: " + String.valueOf(emp2 == emp3));
} finally {
}
}
}
控制檯輸出:
[DEBUG] 2020-03-22 00:57:23,381 method:org.apache.ibatis.cache.decorators.LoggingCache.getObject(LoggingCache.java:60)
Cache Hit Ratio [xx.xxx.xxx.dao.EmployeeMapper]: 0.0
[DEBUG] 2020-03-22 00:57:23,837 method:org.apache.ibatis.cache.decorators.LoggingCache.getObject(LoggingCache.java:60)
Cache Hit Ratio [xx.xxx.xxx.dao.EmployeeMapper]: 0.0
[DEBUG] 2020-03-22 00:57:23,932 method:org.apache.ibatis.cache.decorators.LoggingCache.getObject(LoggingCache.java:60)
Cache Hit Ratio [xx.xxx.xxx.dao.EmployeeMapper]: 0.0
[INFO ] 2020-03-22 00:57:23,961 method:xx.xxx.xxx.DaoTest.testGlobalCache(DaoTest.java:86)
emp1 == emp2: false
[INFO ] 2020-03-22 00:57:23,961 method:xx.xxx.xxx.DaoTest.testGlobalCache(DaoTest.java:87)
emp1 == emp3: false
[INFO ] 2020-03-22 00:57:23,961 method:xx.xxx.xxx.DaoTest.testGlobalCache(DaoTest.java:88)
emp2 == emp3: false
問題排查
- 是否在mybatis-conifg.xml中開啓了全局緩存的設置。
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--全局緩存:開啓-->
<setting name="cacheEnabled" value="true"/>
</settings>
- 是否在mapper.xml文件中打開了cache設置。
<cache eviction="LRU" flushInterval="600000" size="1024" readOnly="true"/>
<!--<cache/>-->
<!--
eviction: 緩存的回收策略,包括FIFO/LRU/SOFT/WEAK,默認是LRU
flushInterval: 緩存清空間隔,以毫秒爲單位
readOnly: 是否只讀
true 只讀,mybatis從緩存中獲取的數據不會修改
不安全,速度快
false 非只讀,mybatis通過序列化&反序列化的方式克隆進行數據獲取
安全,速度慢
size: 緩存多少元素
type: 自定義緩存全類名(org.apache.ibatis.cache.Cache)
-->
- Bean對象是否可以序列化
public class Employee implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
private String gender;
private String email;
}
上述步驟纔可以打開二級緩存,現在在控制檯中已經有Cache Hit Ratio 0的輸出,證明二級緩存已經打開,但是由於SQL的相關問題,導致的緩存沒有命中。繼續排查……
4. SQL 語句是否一致(一致)
5. 是否在新的SQL查詢之前沒有關閉上一個SqlSession,導致命中了一級緩存而不需要查詢二級緩存(已關閉)。
最終結論
對上述問題都進行了排查,但是依舊沒有找出問題,上網上看了半天,發現了問題:
二級緩存存在於 SqlSessionFactory 生命週期中。
我一直以爲二級緩存針對的對象是一個Mapper對象,只要是針對同一個Mapper的操作,都可以實現二級緩存。但是前提是操作必須在同一個SqlSessionFactory 中進行。
在一開始的代碼中,通過調用三次getFactory()並打開session,實例化了三個不同的SqlSessionFactory 對象,這樣在後續的SQL操作中,是不可能命中二級緩存的。
修改如下
public class DaoTest {
private static final Logger LOGGER = Logger.getLogger(DaoTest.class);
private SqlSessionFactory getFactory() throws IOException {
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
return factory;
}
@Test
public void testGlobalCache() throws IOException {
SqlSessionFactory factory = getFactory();
SqlSession sess1 = factory.openSession();
SqlSession sess2 = factory.openSession();
SqlSession sess3 = factory.openSession();
try {
EmployeeMapper mapper1 = sess1.getMapper(EmployeeMapper.class);
Employee emp1 = mapper1.getEmpByID(1);
sess1.close();
EmployeeMapper mapper2 = sess2.getMapper(EmployeeMapper.class);
Employee emp2 = mapper2.getEmpByID(1);
sess2.close();
EmployeeMapper mapper3 = sess3.getMapper(EmployeeMapper.class);
mapper3.insertEmp(new Employee(null, "XX", "F", "XXX"));
Employee emp3 = mapper3.getEmpByID(1);
sess3.close();
LOGGER.info("emp1 == emp2: " + String.valueOf(emp1 == emp2));
LOGGER.info("emp1 == emp3: " + String.valueOf(emp1 == emp3));
LOGGER.info("emp2 == emp3: " + String.valueOf(emp2 == emp3));
} finally {
}
}
}
問題解決!