Talk about the primary cache and secondary cache of Mybatis

Level 1 cache

Mybatis supports caching. By default, the first level cache is turned on. The first level cache is at the SqlSession level , and the cache is the SqlSession object. In the case of the same SQL statement and parameters, when we use the same SqlSession object to call the same Mapper method, we only need to execute SQL once, because after the first execution of the Mapper method, Mybatis will cache it and query it later If the parameters and SQL statements are the same, the cached data will be fetched directly instead of querying the database, which greatly improves the query efficiency.

Here are some other people's cache diagrams, which is clear and clear!

 The life cycle of the first level cache

  • MyBatis first level cache is based on SQLSession. When MyBatis opens a database session, it will create a new SqlSession object, and there will be a new Executor object in the SqlSession object. The Executor object holds a new PerpetualCache object; when the session ends, the SqlSession object and its internal Executor objects and PerpetualCache objects are also released.
  • If SqlSession calls the close() method, the PerpetualCache object of the first level cache will be released, and the first level cache will be unavailable.
  • If SqlSession calls clearCache(), the data in the PerpetualCache object will be cleared, but the object can still be used.
  • Any update operation (update(), delete(), insert()) performed in SqlSession will clear the data of the PerpetualCache object, but the object can continue to be used

Level 1 cache trigger condition

  1. The incoming statementId is the same
  2. The range of results in the result set required for query is the same
  3. The SqlPreparedstatement statement string (boundSql.getSql()) that is finally passed to JDBC java.sql. produced by this query is the same
  4. The parameter values ​​passed to java.sql.Statement to be set are the same

First-level cache use cases

First of all, the entity class ( note: need to be serializable, I implemented the Serializable interface here )

public class TtUser implements Serializable{
    private Long id;
    private String loginName;
    private String password;

    // 这里省略get、set方法
}

 dao method

public interface TtUserMapper {
    TtUser selectByPrimaryKey(Long id);
    int updateByPrimaryKeySelective(TtUser record);
}

mapper.xml method

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xiateng.dao.user.TtUserMapper">

  <!--<cache eviction="LRU" flushInterval="10000" size="1024"  />-->

  <resultMap id="BaseResultMap" type="com.xiateng.entity.TtUser">
    <id column="id" jdbcType="BIGINT" property="id" />
    <result column="login_name" jdbcType="VARCHAR" property="loginName" />
    <result column="password" jdbcType="VARCHAR" property="password" />
  </resultMap>

  <sql id="Base_Column_List">
    id, login_name, password
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from t_user
    where id = #{id,jdbcType=BIGINT}
  </select>
  
  <update id="updateByPrimaryKeySelective" parameterType="com.xiateng.entity.TtUser">
    update t_user
    <set>
      <if test="loginName != null">
        login_name = #{loginName,jdbcType=VARCHAR},
      </if>
      <if test="password != null">
        password = #{password,jdbcType=VARCHAR},
      </if>
      <if test="blong != null">
        blong = #{blong,jdbcType=SMALLINT},
      </if>
    </set>
    where id = #{id,jdbcType=BIGINT}
  </update>
  
</mapper>

Controller call

@RequestMapping(value = "/a")
    @ResponseBody
    public String a(){
        // 开启一个SqlSession(会话)
        SqlSession sqlSession = sqlSessionFactory.openSession();
        TtUserMapper mapper = sqlSession.getMapper(TtUserMapper.class);
        TtUser ttUser = mapper.selectByPrimaryKey(1L);
        System.out.println("-----------------------------------------"+ttUser);
        System.out.println("-----------------------------------------第二次执行");
        TtUser ttUser2 = mapper.selectByPrimaryKey(1L);
        System.out.println("-----------------------------------------"+ttUser2);
        // 关闭会话
        sqlSession.close();
        return "成功";
    }

 Console printing

We will find that sql is only printed once, indicating that the database was not queried the second time 

 Secondary cache

The second-level cache is the cache of the SqlSessionFactory object in Mybatis. The SqlSession created by the same SqlSessionFactory object shares its cache, but the cached data is not the object, so the result of the second query from the second-level cache is the same as the
first one. The objects of entry are not the same. The second level cache is the namespace level cache ,

Look at the picture

Pay attention to the use of secondary cache

  • All select statements in the mapping statement file will be cached.
  • All insert, update, and delete statements in the mapping statement file will refresh the cache.
  • The cache will be reclaimed using the default Least Recently Used (LRU) algorithm.
  • According to the schedule, such as No Flush Interval, (CNFI has no refresh interval), the cache will not be flushed in any chronological order.
  • The cache will store 1024 references to list collections or objects (no matter what the query method returns)
  • The cache will be regarded as a read/write (read/write) cache, which means that object retrieval is not shared and can be safely modified by the caller without interfering with potential modifications made by other callers or threads.

Mybatis does not enable the second-level cache by default. If you want to enable it, you need to configure it, and the returned POJO must be serializable (the entity class implements Serializable). Let's give an example:

The entities and methods are still the above, but add the configuration <cache/> in mapper.xml as follows

<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>

Property meaning:

  • eviction: Represents the cache recovery strategy. Currently MyBatis provides the following strategies.
  1. LRU, the least recently used, an object that has not been used for the longest time
  2. FIFO, first in first out, remove objects in the order they enter the cache
  3. SOFT, soft reference, remove objects based on garbage collector status and soft reference rules
  4. WEAK, weak reference, more actively remove objects based on garbage collector status and weak reference rules. LRU is used here to remove the pair image that is not used for the longest time. flushInterval: refresh interval time, in milliseconds, here is configured to refresh in 100 seconds, if you do not configure it, then it will be refreshed when SQL is executed Cache.
  • size: The number of references, a positive integer, which represents the maximum number of objects that the cache can store, and should not be set too large. Too much setting will cause memory overflow. 1024 objects are configured here
  • readOnly: read-only, which means that the cached data can only be read but not modified. The advantage of this setting is that we can quickly read the cache. The disadvantage is that we have no way to modify the cache. Its default value is false, which does not allow us to modify it.

Next, configure Mybatis to turn on the secondary cache, because I am a SpringBoot project, so I added the following configuration to the application.yml file:

Finally test the Controller code

    @RequestMapping(value = "/b")
    @ResponseBody
    public Map<String, Object> b(){
        Map map = new HashMap();
        TtUser ttUser = ttUserMapper.selectByPrimaryKey(1L);
        map.put("ttUser",ttUser);
        System.out.println("-----------------------------------------"+ttUser);
        System.out.println("-----------------------------------------第二次执行");
        TtUser ttUser2 = ttUserMapper.selectByPrimaryKey(1L);
        map.put("ttUser2",ttUser2);
        System.out.println("-----------------------------------------"+ttUser2);
        System.out.println("-----------------------------------------第三次执行");
        TtUser ttUser3 = ttUserMapper.selectByPrimaryKey(2L);
        System.out.println("-----------------------------------------"+ttUser3);
        map.put("ttUser3",ttUser3);
        return map;
    }

Print result

From the print results, we will find that the first query printed the sql statement, it directly queried the database, the second query did not print the sql statement, because the second time parameters, statementId, sql are the same, it directly hits the secondary cache. There is no database query, and the third time the parameters are different, the sql statement is printed, and the database is directly queried

The difference between the first level cache and the second level cache

Level 1 cache : enabled by default, based on SQLSession, SQLSession object needs to be constructed when operating the database, there is a HashMap in the object to store cached data, and different SQLSessions are directly unaffected. Adds, deletes, and changes are executed in a SQLSession and submitted to the database. The first level cache will be emptied to avoid dirty reads.

When a sqlsession is closed, the first level cache also disappears.

Second-level cache : Not enabled by default. Based on namespace, multiple SQLSessions under the same namespace will share a cache. Second-level cache also uses HashMap for data storage. Compared with the first-level cache, the second-level cache has a larger scope and can be spanned. Multiple SQLSessions.

Guess you like

Origin blog.csdn.net/qq_43037478/article/details/112311351