This is the 8th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
MyBatis cache introduction
Like most persistence frameworks, MyBatis also provides support for level 1 and level 2 caching
- Level 1 Cache A HashMap local Cache based on PerpetualCache, which is stored at Session scope, and when a Session is flushed or closed, all caches in that Session are emptied.
- Level 2 caches have the same mechanism as Level 1 caches, with PerpetualCache and HashMap storage by default, but with Mapper(Namespace) storage scope and customizable storage source, such as Ehcache.
- For cache data Update mechanism, after Create/Update/Delete operations are performed on a certain scope (Session level 1 / Namespaces level 2), all caches in the select area are cleared by default.
Mybatis level 1 cache
The mapping file
<mapper namespace="com.shxt.dao.UserDao">
<resultMap type="com.shxt.model.User" id="BaseResultMapper">
<id column="user_id" property="user_id"/>
<result column="account" property="account"/>
<result column="password" property="password"/>
<result column="user_name" property="user_name"/>
<result column="status" property="status"/>
<result column="login_time" property="login_time"/>
<result column="ip" property="ip"/>
<result column="fk_role_id" property="fk_role_id"/>
</resultMap>
<sql id="sys_user_columns">
user_id,account,password,user_name,status,login_time,ip,fk_role_id
</sql>
<select id="load" parameterType="int" resultMap="BaseResultMapper">
SELECT
<include refid="sys_user_columns"/>
FROM
sys_user
WHERE user_id=#{user_id}
</select>
</mapper>
Copy the code
Queries | l1 test
@Test
public voidQuery _ Level-1 Cache Test (){SqlSession SqlSession =null;
try {
sqlSession = MyBatisUtils.getSqlSession();
User u1 = sqlSession.selectOne(UserDao.class.getName()+".load", -999);
System.out.println("First query :"+u1);
User u2 = sqlSession.selectOne(UserDao.class.getName()+".load", -999);
System.out.println("Second query :"+u2);
} finally{ MyBatisUtils.closeSqlSession(sqlSession); }}Copy the code
Console running result description
DEBUG [main] - ==> Preparing: SELECT user_id,account,password,user_name,status,login_time,ip,fk_role_id FROM sys_user WHERE user_id=?
DEBUG [main] - ==> Parameters: - 999.(Integer)
TRACE [main] - < == Columns: user_id, account, password, user_name, status, login_time, ip, fk_role_id
TRACE [main] - < == Row: - 999., Super, super, Liu Wenming,1.201707 -- 30 09:50:18.0.- 100.
DEBUG [main] - < == Total: 1First query:User [user_id=- 999., account=super, password=super, user_name=Wenming Liu, Status=1, login_time=Sun Jul 30 09:50:18 CST 2017, ip=, fk_role_id=- 100.] Second query:User [user_id=- 999., account=super, password=super, user_name=Wenming Liu, Status=1, login_time=Sun Jul 30 09:50:18 CST 2017, ip=, fk_role_id=- 100.]
Copy the code
It can be seen from the above results that the load method is called twice, but the database is queried only once. The reason for this phenomenon is the level 1 cache of MyBatis, which is enabled by default.
Query – change – queries | l1 test
@Test
public voidQuery _ Change _ Level-1 Cache Test (){SqlSession SqlSession =null;
try {
sqlSession = MyBatisUtils.getSqlSession();
User u1 = sqlSession.selectOne(UserDao.class.getName()+".load", -999);
System.out.println("First query :"+u1);
User u2 = new User();
u2.setUser_id(-999);
u2.setStatus(2);
// Change the database
sqlSession.update(UserDao.class.getName()+".update", u2);
User u3 = sqlSession.selectOne(UserDao.class.getName()+".load", -999);
System.out.println("Second query :"+u3);
// The data will not enter the database
sqlSession.commit();
}catch (Exception ex) {
ex.printStackTrace();
}finally{ MyBatisUtils.closeSqlSession(sqlSession); }}Copy the code
If a CUD operation is involved, the cache disappears automatically and the query is requeried
Console running result description
DEBUG [main] - ==> Preparing: SELECT user_id,account,password,user_name,status,login_time,ip,fk_role_id FROM sys_user WHERE user_id=?
DEBUG [main] - ==> Parameters: - 999.(Integer)
TRACE [main] - < == Columns: user_id, account, password, user_name, status, login_time, ip, fk_role_id
TRACE [main] - < == Row: - 999., Super, super, Liu Wenming,1.201707 -- 30 09:50:18.0.- 100.
DEBUG [main] - < == Total: 1First query:User [user_id=- 999., account=super, password=super, user_name=Wenming Liu, Status=1, login_time=Sun Jul 30 09:50:18 CST 2017, ip=, fk_role_id=- 100.]
DEBUG [main] - ==> Preparing: UPDATE sys_user SET status = ? WHERE user_id=?
DEBUG [main] - ==> Parameters: 2(Integer), - 999.(Integer)
DEBUG [main] - < == Updates: 1
DEBUG [main] - ==> Preparing: SELECT user_id,account,password,user_name,status,login_time,ip,fk_role_id FROM sys_user WHERE user_id=?
DEBUG [main] - ==> Parameters: - 999.(Integer)
TRACE [main] - < == Columns: user_id, account, password, user_name, status, login_time, ip, fk_role_id
TRACE [main] - < == Row: - 999., Super, super, Liu Wenming,2.201707 -- 30 09:50:18.0.- 100.
DEBUG [main] - < == Total: 1Second query:User [user_id=- 999., account=super, password=super, user_name=Wenming Liu, Status=2, login_time=Sun Jul 30 09:50:18 CST 2017, ip=, fk_role_id=- 100.]
Copy the code
Mybatis level 2 cache
Mybatis level 2 cache is not enabled by default, so I test the following code to see how it works
@Test
public voidQuery _ No level 2 cache tests (){// The first SqlSession
SqlSession sqlSession1 = null;
// The second SqlSession
SqlSession sqlSession2 = null;
try {
sqlSession1 = MyBatisUtils.getSqlSession();
sqlSession2 = MyBatisUtils.getSqlSession();
User u1 = sqlSession1.selectOne(UserDao.class.getName()+".load", -999);
System.out.println("SqlSession1 query."+u1);
User u2 = sqlSession2.selectOne(UserDao.class.getName()+".load", -999);
System.out.println("SqlSession2 query."+u2);
} finally{ MyBatisUtils.closeSqlSession(sqlSession1); MyBatisUtils.closeSqlSession(sqlSession2); }}Copy the code
Console running result description
DEBUG [main] - ==> Preparing: SELECT user_id,account,password,user_name,status,login_time,ip,fk_role_id FROM sys_user WHERE user_id=?
DEBUG [main] - ==> Parameters: - 999.(Integer)
TRACE [main] - < == Columns: user_id, account, password, user_name, status, login_time, ip, fk_role_id
TRACE [main] - < == Row: - 999., Super, super, Liu Wenming,2.201707 -- 30 09:50:18.0.- 100.
DEBUG [main] - < == Total: 1SqlSession1 query:User [user_id=- 999., account=super, password=super, user_name=Wenming Liu, Status=2, login_time=Sun Jul 30 09:50:18 CST 2017, ip=, fk_role_id=- 100.]
Tue Sep 05 16:20:42 CST 2017 WARN: Establishing SSL connection without server'identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL Connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false.or set useSSL=true and provide truststore for server certificate verification.
DEBUG [main] - ==> Preparing: SELECT user_id,account,password,user_name,status,login_time,ip,fk_role_id FROM sys_user WHERE user_id=?
DEBUG [main] - ==> Parameters: - 999.(Integer)
TRACE [main] - < == Columns: user_id, account, password, user_name, status, login_time, ip, fk_role_id
TRACE [main] - < == Row: - 999., Super, super, Liu Wenming,2.201707 -- 30 09:50:18.0.- 100.
DEBUG [main] - < == Total: 1SqlSession2 query:User [user_id=- 999., account=super, password=super, user_name=Wenming Liu, Status=2, login_time=Sun Jul 30 09:50:18 CST 2017, ip=, fk_role_id=- 100.]
Copy the code
Two sessions are used to query the User with id -999 respectively, so Mybatis interacts with the database twice, which indicates that mybatis has not enabled the level 2 cache now, so we need to manually enable it.
Step 1: Serialize persistent classes
Java class to implement Serializable interface, otherwise MyBatis level 2 cache is not available
public class User implements java.io.Serializable{
private static final long serialVersionUID = 1L;
}
Copy the code
Step 2: Manually enable level 2 cache
The principle here is that if level 2 caching is enabled, data from the SQLSession level 1 cache will be added to the level 2 cache of namespace after SQLSession is disabled.
Mybatis -config. XML configuration information
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
Copy the code
CacheEnabled defaults to true, so it can be ignored
Usermapper. XML starts the second level cache label
<mapper namespace="com.shxt.dao.UserDao">
<! -- Enable level 2 cache -->
<cache></cache>
<! -- Omitted part of code -->
</mapper>
Copy the code
Step 3: Test the code
@Test
public voidQuery _ Level 2 Cache test () {// The first SqlSession
SqlSession sqlSession1 = null;
// The second SqlSession
SqlSession sqlSession2 = null;
sqlSession1 = MyBatisUtils.getSqlSession();
sqlSession2 = MyBatisUtils.getSqlSession();
User u1 = sqlSession1.selectOne(UserDao.class.getName() + ".load", -999);
System.out.println("SqlSession1 query." + u1);
MyBatisUtils.closeSqlSession(sqlSession1);/ / close
User u2 = sqlSession2.selectOne(UserDao.class.getName() + ".load", -999);
System.out.println("SqlSession2 query." + u2);
MyBatisUtils.closeSqlSession(sqlSession2);
}
Copy the code
Console running result description
DEBUG [main] - Cache Hit Ratio [com.shxt.dao.UserDao]: 0.0
DEBUG [main] - ==> Preparing: SELECT user_id,account,password,user_name,status,login_time,ip,fk_role_id FROM sys_user WHERE user_id=?
DEBUG [main] - ==> Parameters: - 999.(Integer)
TRACE [main] - < == Columns: user_id, account, password, user_name, status, login_time, ip, fk_role_id
TRACE [main] - < == Row: - 999., Super, super, Liu Wenming,2.201707 -- 30 09:50:18.0.- 100.
DEBUG [main] - < == Total: 1SqlSession1 query:User [user_id=- 999., account=super, password=super, user_name=Wenming Liu, Status=2, login_time=Sun Jul 30 09:50:18 CST 2017, ip=, fk_role_id=- 100.]
DEBUG [main] - Cache Hit Ratio [com.shxt.dao.UserDao]: 0.5SqlSession2 query:User [user_id=- 999., account=super, password=super, user_name=Wenming Liu, Status=2, login_time=Sun Jul 30 09:50:18 CST 2017, ip=, fk_role_id=- 100.]
Copy the code
By default, the cache is flushed when sqlSession commits
@Test
public voidQuery _ Level 2 Cache test (){// The first SqlSession
SqlSession sqlSession1 = null;
// The second SqlSession
SqlSession sqlSession2 = null;
try {
sqlSession1 = MyBatisUtils.getSqlSession();
sqlSession2 = MyBatisUtils.getSqlSession();
User u1 = sqlSession1.selectOne(UserDao.class.getName()+".load", -999);
System.out.println("SqlSession1 query."+u1);
sqlSession1.commit();// Force refresh
User u2 = sqlSession2.selectOne(UserDao.class.getName()+".load", -999);
System.out.println("SqlSession2 query."+u2);
} finally{ MyBatisUtils.closeSqlSession(sqlSession1); MyBatisUtils.closeSqlSession(sqlSession2); }}Copy the code
In the select statement:
- The default value for flushCache is false, which means that the local cache and secondary cache will not be flushed whenever a statement is called.
- The default value of useCache is true, indicating that the result of this statement will be cached at level 2.
Modify the mapping file as follows
<mapper namespace="com.shxt.dao.UserDao">
<! -- Enable level 2 cache -->
<cache></cache>
<resultMap type="com.shxt.model.User" id="BaseResultMapper">
<id column="user_id" property="user_id"/>
<result column="account" property="account"/>
<result column="password" property="password"/>
<result column="user_name" property="user_name"/>
<result column="status" property="status"/>
<result column="login_time" property="login_time"/>
<result column="ip" property="ip"/>
<result column="fk_role_id" property="fk_role_id"/>
</resultMap>
<sql id="sys_user_columns">
user_id,account,password,user_name,status,login_time,ip,fk_role_id
</sql>
<! -- flushCache="true" -->
<select id="load" parameterType="int" resultMap="BaseResultMapper" flushCache="true">
SELECT
<include refid="sys_user_columns"/>
FROM
sys_user
WHERE user_id=#{user_id}
</select>
</mapper>
Copy the code
Run the test method that was committed before the commit again with the result:
DEBUG [main] - Cache Hit Ratio [com.shxt.dao.UserDao]: 0.0
DEBUG [main] - ==> Preparing: SELECT user_id,account,password,user_name,status,login_time,ip,fk_role_id FROM sys_user WHERE user_id=?
DEBUG [main] - ==> Parameters: - 999.(Integer)
TRACE [main] - < == Columns: user_id, account, password, user_name, status, login_time, ip, fk_role_id
TRACE [main] - < == Row: - 999., Super, super, Liu Wenming,2.201707 -- 30 09:50:18.0.- 100.
DEBUG [main] - < == Total: 1SqlSession1 query:User [user_id=- 999., account=super, password=super, user_name=Wenming Liu, Status=2, login_time=Sun Jul 30 09:50:18 CST 2017, ip=, fk_role_id=- 100.]
DEBUG [main] - Cache Hit Ratio [com.shxt.dao.UserDao]: 0.5
DEBUG [main] - ==> Preparing: SELECT user_id,account,password,user_name,status,login_time,ip,fk_role_id FROM sys_user WHERE user_id=?
DEBUG [main] - ==> Parameters: - 999.(Integer)
TRACE [main] - < == Columns: user_id, account, password, user_name, status, login_time, ip, fk_role_id
TRACE [main] - < == Row: - 999., Super, super, Liu Wenming,2.201707 -- 30 09:50:18.0.- 100.
DEBUG [main] - < == Total: 1SqlSession2 query:User [user_id=- 999., account=super, password=super, user_name=Wenming Liu, Status=2, login_time=Sun Jul 30 09:50:18 CST 2017, ip=, fk_role_id=- 100.]
Copy the code
For INSERT, UPDATE, delete statements:
- FlushCache defaults to true, which means that any time a statement is called, the local cache and the second-level cache will be flushed.
- The useCache property is not present in this case
conclusion
Level 1 cache
- The default open
- It must be the same session. If the session object is already closed (), it will not be used
- The query conditions must be consistent
- Session.cleancache () was not executed; Clear the cache
- No add, delete, or change operations have been performed (these operations clean up the cache)
The second level cache
1. The default value is set in mybatis-config. XML
<settings>
<setting name="cacheEnabled" value="true" />
</settings>
Copy the code
2. Manually enable adding in mapper. XML
<mapper namespace="com.shxt.dao.UserDao">
<! -- Enable level 2 cache -->
<cache></cache>
<resultMap type="com.shxt.model.User" id="BaseResultMapper">
<id column="user_id" property="user_id"/>
<result column="account" property="account"/>
<result column="password" property="password"/>
<result column="user_name" property="user_name"/>
<result column="status" property="status"/>
<result column="login_time" property="login_time"/>
<result column="ip" property="ip"/>
<result column="fk_role_id" property="fk_role_id"/>
</resultMap>
<sql id="sys_user_columns">
user_id,account,password,user_name,status,login_time,ip,fk_role_id
</sql>
<! -- flushCache="true" -->
<select id="load" parameterType="int" resultMap="BaseResultMapper">
SELECT
<include refid="sys_user_columns"/>
FROM
sys_user
WHERE user_id=#{user_id}
</select>
</mapper>
Copy the code
3. All select statements in the mapping statement file will be cached. 4. Map all insert, UPDATE, and DELETE statements in the statement file to flush the cache. The Least Recently Used (LRU) algorithm is Used to retrieve the cache. 6. The cache is refreshed at specified intervals. 7. Cache stores 1024 objects
<cache
eviction="FIFO"// The collection policy is first-in, first-outflushInterval="60000"// Automatic refresh time60s
size="512"// Maximum cache512Reference objectsreadOnly="true"/>/ / read-onlyCopy the code
MyBatis cache doesn’t make much sense.
A. In the face of A certain amount of data, the built-in cache is useless; B. MyBatis framework is not good at caching query result sets, it should concentrate on SQL Mapper. It makes more sense to use the framework’s Application to build caches, such as OSCache, Memcached, etc.