Mybatis (9) ------ Level 1 cache, level 2 cache, mybatis ------
In the previous chapter, we explained how to improve query efficiency through mybatis lazy loading. In addition to lazy loading, what other methods can be used to improve query efficiency? This is the cache in this chapter.
Mybatis provides a level-1 cache and level-2 cache for us to understand:
① The first-level cache is a SqlSession-level cache. When operating the database, you need to construct a sqlSession object. The object has a data structure (HashMap) used to store cached data. The cache data regions (HashMap) between different sqlsessions do not affect each other.
② The second-level cache is a mapper-level cache. Multiple sqlsessions operate on the same Mapper SQL statement. Multiple sqlsessions can share the second-level cache, and the second-level cache is cross-SqlSession.
1. Level 1 Cache
① In A sqlSession, we perform two queries on the User table based on the id to view the SQL statement they issued.
@ Testpublic void testSelectOrderAndUserByOrderId () {// generate sessionSqlSession sqlSession = sessionFactory Based on sqlSessionFactory. openSession (); String statement = "one. to. one. mapper. ordersMapper. selectOrderAndUserByOrderID "; UserMapper userMapper = sqlSession. getMapper (UserMapper. class); // For the first query, issue an SQL statement and put the query result into the cache. User u1 = userMapper. selectUserByUserId (1); System. out. println (u1); // The second query. Because it is the same sqlSession, the query results will be searched in the cache. // If yes, the query results will be obtained directly from the cache, do not interact with the database User u2 = userMapper. selectUserByUserId (1); System. out. println (u2); sqlSession. close ();}
View the console printing information:
② The user table is also queried twice, but an update operation is performed between the two queries.
@ Testpublic void testSelectOrderAndUserByOrderId () {// generate sessionSqlSession sqlSession = sessionFactory Based on sqlSessionFactory. openSession (); String statement = "one. to. one. mapper. ordersMapper. selectOrderAndUserByOrderID "; UserMapper userMapper = sqlSession. getMapper (UserMapper. class); // For the first query, issue an SQL statement and put the query result into the cache. User u1 = userMapper. selectUserByUserId (1); System. out. println (u1); // The second step performs an update operation, sqlSession. commit () u1.setSex ("female"); userMapper. updateUserByUserId (u1); sqlSession. commit (); // The second query, because it is the same sqlSession. commit (), the cache information will be cleared // then this query will also issue the SQL statement User u2 = userMapper. selectUserByUserId (1); System. out. println (u2); sqlSession. close ();}
Console printing:
③ Summary
1. For the first time, query user information with user id 1. First, check whether the cache contains user information with id 1. If not, query user information from the database. Obtain the user information and store the user information in the level-1 cache.
2. If the intermediate sqlSession executes the commit operation (insert, update, delete), the first-level cache in the SqlSession will be cleared, this aims to make the cache store the latest information and avoid dirty reads.
3. Initiate the second query of user information with a user id of 1. First, check whether there is user information with id 1 in the cache. If there is user information in the cache, directly obtain user information from the cache.
2. Secondary Cache
The principle of level-2 cache is the same as that of level-1 cache. During the first query, data is stored in the cache, and then the second query is directly retrieved from the cache. However, the first-level cache is based on sqlSession, while the second-level cache is based on the namespace Of The mapper file. That is to say, multiple sqlsessions can share the second-level cache region in one mapper. If the two mapper namespaces are the same, even if there are two mappers, the data queried by executing SQL statements in the two mappers will also exist in the same second-level cache area.
How is the second-level cache used?
① Enable Level 2 Cache
Unlike the default enabling of level-1 cache, level-2 Cache needs to be manually enabled.
First Add the following code to the global configuration file mybatis-configuration.xml file:
<! -- Enable Level 2 Cache --> <settings> <setting name = "cacheEnabled" value = "true"/> </settings>
Enable caching in the UserMapper. xml file.
<! -- Enable Level 2 cache --> <cache> </cache>
We can see mapper. the xml file contains such an empty label <cache/>. In fact, you can configure <cache type = "org. apache. ibatis. cache. impl. perpetualCache "/>. The PerpetualCache class is the default cache function class of mybatis. We can use the default mybatis Cache without writing the type. We can also implement the Cache interface to customize the Cache.
We can see that the underlying layer of the second-level cache is still in the HashMap architecture.
② Po implementationSerializableSerialization Interface
After the second-level cache is enabled, you also need to implement the Serializable interface for the pojo to be cached. In order to retrieve the cached data and perform deserialization, because the second-level cache has various data storage media, it may not exist only in the memory. It may exist in the hard disk. If we want to obtain the cache again, We Need To deserialize it. Therefore, pojo in mybatis implements the Serializable interface.
③ Test
I. Test the second-level cache and sqlSession
@ Testpublic void testTwoCache () {// generate sessionSqlSession sqlSession1 = sessionFactory according to sqlSessionFactory. openSession (); SqlSession sqlSession2 = sessionFactory. openSession (); String statement = "com. ys. twocache. userMapper. selectUserByUserId "; UserMapper userMapper1 = sqlSession1.getMapper (UserMapper. class); UserMapper userMapper2 = sqlSession2.getMapper (UserMapper. class); // For the first query, issue an SQL statement and put the query result into the cache. User u1 = userMapper1.selectUserByUserId (1); System. out. println (u1); sqlSession1.close (); // close sqlSession after the first query. // If sqlSession1 is disabled, this query still does not issue the SQL statement User u2 = userMapper2.selectUserByUserId (1); System. out. println (u2); sqlSession2.close ();}
We can see that the first two different sqlsessions are closed, and the second query still does not issue an SQL query statement.
2. Test and execute the commit () operation, and clear the second-level cache data
@ Testpublic void testTwoCache () {// generate sessionSqlSession sqlSession1 = sessionFactory according to sqlSessionFactory. openSession (); SqlSession sqlSession2 = sessionFactory. openSession (); SqlSession sqlSession3 = sessionFactory. openSession (); String statement = "com. ys. twocache. userMapper. selectUserByUserId "; UserMapper userMapper1 = sqlSession1.getMapper (UserMapper. class); UserMapper userMapper2 = sqlSession2.getMapper (UserMapper. class); UserMapper userMapper3 = sqlSession2.getMapper (UserMapper. class); // For the first query, issue an SQL statement and put the query result into the cache. User u1 = userMapper1.selectUserByUserId (1); System. out. println (u1); sqlSession1.close (); // after the first query, close sqlSession // execute the update operation, commit () u1.setUsername ("aaa"); userMapper3.updateUserByUserId (u1 ); sqlSession3.commit (); // For the second query, the cached data has been cleared due to the last update operation (to prevent dirty Data Reading). Here, the SQL statement User u2 = userMapper2.selectUserByUserId (1) must be issued again ); system. out. println (u2); sqlSession2.close ();}
View the console information:
④ UseCache and flushCache
You can also configure userCache, flushCache, and other configuration items in mybatis. userCache is used to set whether to disable the second-level cache. In statement, setting useCache = false can disable the second-level cache of the current select statement, that is, each query sends an SQL statement to query. The default value is true, indicating that the SQL statement uses the second-level cache.
<select id="selectUserByUserId" useCache="false" resultType="com.ys.twocache.User" parameterType="int">select * from user where id=#{id}</select>
In this case, the latest data SQL is required for each query. You must set useCache to false, disable the second-level cache, and obtain it directly from the database.
In the same namespace of mapper, if there are other insert, update, and delete operations, the cache needs to be refreshed. If no refresh cache is executed, dirty reads will occur.
Set the flushCache = "true" attribute in the statement configuration. The default value is true, indicating that the cache is refreshed. If it is set to false, the cache is not refreshed. When cache is used, dirty read occurs if you manually modify the query data in the database table.
<select id="selectUserByUserId" flushCache="true" useCache="false" resultType="com.ys.twocache.User" parameterType="int">select * from user where id=#{id}</select>
Generally, the cache needs to be refreshed after the commit operation is completed. flushCache = true indicates that the cache is refreshed to avoid dirty database reads. So we do not need to set the default value.