Mybatis cache mechanism

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

mybatis-config01

Test sample – sqlmap.xml

sqlmap.xml.01

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

mybastis-config.xml.02

Test sample – sqlmap.xml

sqlmap.xml.02

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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.