MyBatis caching mechanism

Posted by lajollabob on Fri, 25 Feb 2022 01:37:11 +0100

13.MyBatis caching mechanism

1. Why cache?

When users frequently query some fixed data, they query these data from the database for the first time and save them in the cache. When users query these data again, they do not need to query through the database, but query in the cache. Reduce the loss caused by network connection and database query, so as to improve our query efficiency and reduce the system performance problems caused by high concurrent access.

One sentence summary: frequently query some data that does not change frequently, and use cache to improve query efficiency.

Like most persistence frameworks, Mybatis also provides caching strategies to reduce the number of queries in the database and improve performance. The cache in Mybatis is divided into level 1 cache and level 2 cache.

2. L1 cache

L1 cache is a SqlSession level cache, which is enabled by default.

  • Therefore, when the parameters are exactly the same as SQL, we use the same SqlSession object to call a Mapper method, and often execute SQL only once, because MyBatis will put it in the cache after the first query using SelSession. When querying later, if there is no statement to refresh and the cache does not timeout, SqlSession will fetch the currently cached data and will not send SQL to the database again.

2.1 verification

 /**
     * Query user by id
     * @throws IOException
     */
    @Test
    public void test8() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.findUserById(1);
        System.out.println(user);
        User user1 = userMapper.findUserById(1);
        System.out.println(user);

        sqlSession.close();
    }

We can find that although we query twice in the above code, we only perform the database operation once in the end. This is why the first level cache provided by Mybatis is is working. Because of the existence of the first level cache, when querying the record with id 1 for the second time, the sql statement is not issued to query the data from the database, but from the first level cache. The results are shown in the figure:

2.2 analysis

  • The L1 cache is a cache in the SqlSession range. Executing the C (add) U (update) D (delete) operation of SqlSession or calling clearCache(), commit(), close() methods will empty the cache.

  1. For the first time to query the user information with user id 1, first search the user information with user id 1 in the cache. If not, query the user information from the database.
  2. Get the user information and store the user information in the first level cache (sqlSession cache area)
  3. If you execute the C (add) U (update) D (delete) operation of sqlsession, or call clearCache(), commit(), close() methods, the cache will be cleared. If the cache is not cleared, calling the same Mapper method in the same sqlsession object will find the previously found data in the first level cache instead of searching the database again.

2.3 clear cache

sqlSession.clearCache();

 @Test
    public void test8() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.findUserById(1);
        System.out.println(user);
        //Manually clear cache
        sqlSession.clearCache();
        User user1 = userMapper.findUserById(1);
        System.out.println(user);

        sqlSession.close();
    }

At this time, execute the sql twice, and the results are as follows:

In addition, you can also set that the cache will be cleared every time it is executed in the xml Mapping file.

<!-- The cache is cleared each time a query is made -->
< select flushCache="true"></select>

3. L2 cache

3.1 introduction to L2 cache

  1. L2 cache is a namespace level (cross SQL session) cache, which is not enabled by default.
  2. The opening of L2 cache needs to be configured. When implementing L2 cache, MyBatis requires that the returned POJO must be Serializable, that is, to implement the Serializable interface.
  3. You only need to map the XML file configuration to enable the L2 cache.

3.2 verification

(1) Configure core profile

<settings>
  <!--
because cacheEnabled The default value of is true,Therefore, this step can be omitted without configuration.
by true On behalf of opening L2 cache; by false Indicates that L2 cache is not enabled.
  -->
  <setting name="cacheEnabled" value="true"/>
</settings>

(2) Configure usermapper XML Mapping

<mapper namespace="com.lagou.dao.UserMapper">
  <!--Enable L2 cache for current mapping file-->
  <cache></cache>
  <!--
<select>Set in label useCache="true"Represents the current statement To use L2 cache.
If L2 cache is not used, it can be set to false
    be careful:
      The latest data is required for each query sql,To set to useCache="false",Disable L2 caching.
-->
  <select id="findById" parameterType="int" resultType="user" useCache="true"
>
   SELECT * FROM `user` where id = #{id}
  </select>
</mapper>

(3) Modify entity class

public class User implements Serializable {
  private Integer id;
  private String username;
  private Date birthday;
  private String sex;
  private String address;
 
  private List<Role> roleList;
  private List<Order> orderList;
}

(4) Test results

@Test
public void testTwoCache() throws Exception {
  SqlSession sqlSession = MyBatisUtils.openSession();
  UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  User user = userMapper.findById(41);
  System.out.println("Users queried for the first time:" + user);
  sqlSession.close();
  SqlSession sqlSession1 = MyBatisUtils.openSession();
  UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
  User user1 = userMapper1.findById(41);
  System.out.println("Users of the second query:"+user1);
  sqlSession1.close();
}

3.3 L2 cache analysis

  • L2 cache is a cross level cache. Multiple sqlsessions operate sql statements mapped by the same Mapper. Multiple sqlsessions can share L2 cache (cross sqlsessions).

Problems caused by introducing L2 cache:

  • Because the second level cache of mybatis is at the namespace level, dirty reading will occur during multi table queries

Demand: query user information and the amount of order information associated with the user (associate two tables user and order)

analysis:

  1. Amount of user information queried for the first time: 199 yuan
  2. The second query will obtain data in the secondary cache area, and the amount is still 199 yuan
  3. Now update the order amount corresponding to the user in the order table and modify it to 299 yuan
  4. Now there is a very serious dirty reading problem!!!
    1. Because to modify the data of the order table is to operate ordermapper XML, so only orderMapper.xml is cleared Cache of XML. UserMapper. The cache of XML still exists, so the dirty data still exists.
  • The actual development will use a third-party cache, such as rides

4. Summary

  1. The cache of mybatis does not require us to store and obtain data manually. Mybatis is automatically maintained.
  2. After mybatis has enabled the L2 cache, the query order is: L2 cache – L1 cache – database
  3. Note: the second level cache of mybatis will have dirty reading problems, which need to be solved by using third-party cache technology.

Topics: Java MySQL Mybatis Cache