Mybatis supports internal cache. There are 2 kinds of cache. The first is session cache. (First level cache) The second is global cache. (Second level cache)
Session cache (First level cache)
Session cache is managed within a SqlSession. (SqlSession is the wrapper of a database connection) It can not be accessed from another SqlSession. And it is enabled by default without specific setting.
Test table
CREATE TABLE CACHE_TEST( COL1 VARCHAR2(10) PRIMARY KEY, COL2 VARCHAR2(10) );
Test sample – mybastis-config.xml
Test sample – sqlmap.xml
Test sample – Test dao
public class TestDao { private SqlSession sqlSession; public void setSqlSession(SqlSession sqlSession) { this.sqlSession = sqlSession; } public void insertData(String col1, String col2) { Map paramMap = new HashMap(); paramMap.put("COL1", col1); paramMap.put("COL2", col2); this.sqlSession.insert("test.insertData", paramMap); } public void updateData(String col1, String col2) { Map paramMap = new HashMap(); paramMap.put("COL1", col1); paramMap.put("COL2", col2); this.sqlSession.update("test.updateData", paramMap); } public String selectData(String col1) { return (String)this.sqlSession.selectOne("test.selectData", col1); } }
Test sample – Test app
private void testSessionCache() throws Exception{ String keyValue = "111"; SqlSessionFactory sessionFactory = getSqlSessionFactory(); SqlSession sqlSession1 = sessionFactory.openSession(true); TestDao testDao1 = new TestDao(); testDao1.setSqlSession(sqlSession1); testDao1.insertData(keyValue, "2222"); // First query this.logger.info("First query"); String col2Data = testDao1.selectData(keyValue); this.logger.info("First query result : " + col2Data); // Second query this.logger.info("Second query"); col2Data = testDao1.selectData(keyValue); this.logger.info("Second query result : " + col2Data); sqlSession1.close(); }
Above sample queries the same record twice. Then, what happens ?
Test result
DEBUG: org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection DEBUG: org.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 359991257. DEBUG: test.insertData - ==> Preparing: INSERT INTO CACHE_TEST(COL1, COL2) VALUES(?, ?) DEBUG: test.insertData - ==> Parameters: 111(String), 2222(String) DEBUG: test.insertData - Preparing: SELECT COL2 FROM CACHE_TEST WHERE COL1 = ? DEBUG: test.selectData - ==> Parameters: 111(String) DEBUG: test.selectData - <== Total: 1 INFO : test.Test1 - First query result : 2222 INFO : test.Test1 - Second query INFO : test.Test1 - Second query result : 2222 DEBUG: org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@157507d9] DEBUG: org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 359991257 to pool.
You can check that the second query result is printed without sql execution, which shows that the second query result is from cache (Session cache)
How to disable session cache
When a table record is modified by multi sessions concurrently, cache can show outdated image. In that case, session cache needs to be disabled. There are 2 ways.
1) sql map config
select tag’s useCache=”false”, flushCache=”true” disables session cache. (Both attribute must be set)
2) SqlSession.clearCache()
Global cache (Second level cache)
Global cache is shared among SqlSessions.
How to enable global cache
Global cache is enabled by 1) setting “cacheEnabled” as “true” inside mybatis config and 2) declare “cache” tag inside sqlmap.xml. (Both must be set)
Test sample – mybatis-config.xml
Test sample – sqlmap.xml
If there are several mappers, each sql mapper must be declared cache tag.
Test sample – Test app
private void testGlobalCache() throws Exception{ String keyValue = "111"; SqlSessionFactory sessionFactory = getSqlSessionFactory(); // Session#1 SqlSession sqlSession1 = sessionFactory.openSession(true); TestDao testDao1 = new TestDao(); testDao1.setSqlSession(sqlSession1); testDao1.insertData(keyValue, "2222"); String col2Data = testDao1.selectData(keyValue); this.logger.info("Session#1 query result : " + col2Data); sqlSession1.close(); // Session#2 SqlSession sqlSession2 = sessionFactory.openSession(true); TestDao testDao2 = new TestDao(); testDao2.setSqlSession(sqlSession2); col2Data = testDao2.selectData(keyValue); this.logger.info("Session#2 query result : " + col2Data); sqlSession2.close(); }
Above sample makes 2 SqlSession and checks what happens on the second SqlSession.
Test result
DEBUG: org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection DEBUG: org.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 407964435. DEBUG: test.insertData - ==> Preparing: INSERT INTO CACHE_TEST(COL1, COL2) VALUES(?, ?) DEBUG: test.insertData - ==> Parameters: 111(String), 2222(String) DEBUG: test.insertData - Preparing: SELECT COL2 FROM CACHE_TEST WHERE COL1 = ? DEBUG: test.selectData - ==> Parameters: 111(String) DEBUG: test.selectData - <== Total: 1 INFO : test.Test1 - Session#1 query result : 2222 DEBUG: org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@18510b13] DEBUG: org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 407964435 to pool. DEBUG: test - Cache Hit Ratio [test]: 0.5 INFO : test.Test1 - Session#2 query result : 2222
You can verify that Session#2 runs without running sql, which shows that global cache is working.
One thought on “Mybatis cache mechanism”