The article directories

  • One, foreword
  • MyBatis cache overview
  • Mybatis level 1 cache
    • 3.1 What is Level 1 Caching? Why level 1 caching?
    • 3.2 How is level 1 cache organized in SqlSession? (Class diagram organization +hashMap cache, completed)
    • 3.3 What is the life cycle of a Level 1 cache PerpetualCache? Three ways to end Level 1 Cache PerpetualCache (done)
    • 3.4 SqlSession Level 1 Cache workflow (build cacheKey+ query by cacheKey, completed)
    • 3.5 Bonus: Level 1 cache low-level design (very important, completed)
      • 3.5.1 Two Functions of the CacheKey
      • 3.5.2 How can I determine the CacheKey? How can I tell if two queries are exactly the same?
      • 3.5.3 CacheKey Construction Algorithm (The BaseExecutor class createCacheKey() method)
      • 3.5.4 CacheKey HashCode Generation Algorithm (update())
    • 3.6 Performance analysis of level 1 cache and matters needing attention
      • 3.6.1 Can I Use a CERTAIN SqlSession Object to Query Data All the time? If HashMap is Too Large, an OOM Error Occurs
      • 3.6.2 Level 1 cache is a Hashmap, no cache expiration, no cache updates
  • Mybatis level 2 cache
    • 4.1 The overall design of cache mechanism of MyBatis and the working mode of two-level cache
    • 4.2 Mapper level 2 Operations (two types: one Mapper, one cache object + multiple Mapper, one cache object)
    • 4.3 Conditions for Using Level 2 Cache (Sql statement level operations in Level 2 Cache Mapper)
    • 4.4 Sequence of Level-1 cache and Level-2 Cache
    • 4.5 Three implementation modes of level 2 cache
    • 4.6 The first implementation: implementation of level 2 cache provided by MyBatis itself (10 level 2 cache implementation classes)
  • How to fine-grained control your MyBatis level 2 cache (MyBatis enhanced Cache)
    • 5.1 A practical question about MyBatis level 2 cache
    • 5.2 Current working mechanism of MyBatis Level 2 Cache
    • 5.3 Design and working principle of mybatis-enhanced cache plug-in
      • 5.3.1 EnhancedCachingExecutor artifacts
      • 5.3.2 EnhancedCachingManager artifacts
    • 5.4 Example of using the Mybatis -enhanced cache plug-in
  • 6, interview goldfinger/talk about your understanding of MyBatis cache? (Interview recitation)
      • 6.1 Level-1 cache and Level-2 Cache
      • 6.2 How level 1 Cache is organized (Class diagram organization +hashMap Cache)
      • 6.3 Lifecycle of Level-1 Cache PerpetualCache/End Four ways of Level-1 cache PerpetualCache
      • 6.4 Level-1 Cache Query Process
      • 6.5 Underlying design of level 1 Cache
      • 6.6 Level-2 Cache Operations at Mapper Level
      • 6.7 Level-2 Cache SQL statement-level operations and Level-2 Cache usage conditions (three)
      • 6.8 Cache Usage Sequence
      • 6.9 Three implementation modes of level 2 Cache
      • 6.10 The first implementation: the implementation of level 2 cache provided by MyBatis itself
  • Seven, summary

One, foreword

MyBatis is a simple, compact but very powerful ORM open source framework, its powerful function is also reflected in its cache mechanism. MyBatis provides two cache mechanisms, level 1 cache and level 2 cache, which can well process and maintain the cache to improve the performance of the system.

ORM framework: Hibernate,Mybatis,Mybatis is smaller and lighter than Hibernate.

This article mainly explains the design principle of MyBatis very good cache mechanism, introduces the outline of the cache mechanism of MyBatis to the readers, and then discusses various aspects of the cache mechanism.

MyBatis cache overview

MyBatis designs the data cache into a two-level structure, which is divided into first-level cache and second-level cache:

Level 1 cache is Session Session level cache, located in the SqlSession object representing a database Session, also known as local cache. Level 1 cache is a feature of MyBatis internal implementation, users can not configure, automatically support the cache by default, users do not have the right to customize it (but this is not absolute, can be modified through the development of plug-ins);

Level 2 cache is the Application level cache, which has a long life cycle, like the Application declaration cycle, that is, it is the scope of the entire Application.

The core differences between Level-1 cache and Level-2 cache are as follows: Level-1 cache is the SqlSession level, and Level-2 cache is the Application level. Level 2 cache is the CachingExecutor. It is the decorator of Executor objects. It is disabled by default and must be manually enabled by the programmer.

The organization of level 1 cache and level 2 cache in MyBatis is shown below:

Level 1 caching works as follows:

Level 1 caching is Session Session level. Generally, a SqlSession object uses an Executor object to perform Session operations. Executor objects maintain a Cache to improve query performance.

How level 2 cache works:

As mentioned above, a SqlSession object uses an Executor object to perform session operations. MyBatis’ secondary caching mechanism is based on this Executor object. If cacheEnabled=true is configured, MyBatis will add a decorator to the Executor object when creating it for the SqlSession object: CachingExecutor, in which case the SqlSession uses the CachingExecutor object to complete the operation request. CachingExecutor checks whether the query request has a cached result in the level-2 cache of the Application level. If yes, the CachingExecutor returns the cached result directly. If not, the actual Executor object is used to perform the query. CachingExecutor then places the query result returned by the real Executor into the cache and returns it to the user.



Implementation of two level cache

MyBatis level 2 cache design is more flexible, you can use MyBatis defined level 2 cache implementation; You can also by implementing org. Apache. Ibatis. Cache. The cache interface custom caching; You can also use third-party memory caching libraries, such as Memcached, which we’ll discuss in more detail in a future article.

Mybatis level 1 cache

The purpose of this section is to introduce the level 1 cache of MyBatis to readers in detail, in-depth source code, analysis of the implementation principle of the level 1 cache of MyBatis, and for the characteristics of the level 1 cache in the actual use of matters that should be paid attention to.

3.1 What is Level 1 Caching? Why level 1 caching?

MyBatis will create a SqlSession object to represent a database session whenever we open a session with MyBatis.

In a session on the database, we may be repeatedly execute the same query, if you don’t take some measures, each new query query a database, and we are in a very short period of time to do the exact same query, so they are most likely the result of the same, because the cost of query a database is very big, This can be a huge waste of resources.

In order to solve this problem, reduce the waste of resources, MyBatis will be in session said SqlSession objects to create a simple cache, cached will each query to the results of the results, the next time the query, if the judge was exactly the same query, will take out the results directly from the cache, returned to the user. There is no need for another database query.

As shown in the following figure, MyBatis will create a local cache in a session representation —- a SqlSession object. For each query, it will try to find whether it is in the local cache according to the query conditions. If it is in the cache, it will directly fetch it from the cache. It is then returned to the user; Otherwise, the data is read from the database, the query results are cached and returned to the user.

Mybatis has a SqlSession, SqlSession has an Executor, and an Executor has a Cache.

For session-level data caching, we call it level-1 data caching, or level-1 cache for short.

3.2 How is level 1 cache organized in SqlSession? (Class diagram organization +hashMap cache, completed)

Since MyBatis uses the SqlSession object to represent a database session, level 1 caching at the session level should also be controlled in SqlSession.

MyBatis is simply an interface to MyBatis, and SqlSession assigns its work to the Executor role, which is responsible for performing operations on the database. When a SqlSession object is created, MyBatis creates a new Executor Executor for the SqlSession object. The Cache information is maintained in this Executor Executor. MyBatis encapsulates the Cache and cache-related operations into the Cache interface.

The relationship between SqlSession, Executor, and Cache is shown in the following class diagram:

As shown in the class diagram above, the Implementation class of the Executor interface, BaseExecutor, has a PerpetualCache implementation class for the Cache interface. For a BaseExecutor object, it maintains the Cache using the PerpetualCache object.

To sum up, the relationship between SqlSession objects, Executor objects, and Cache objects is shown as follows:

Goldfinger: A session corresponds to a SqlSession, an Executor object, and a PerpetualCache object. The session ends and all disappear without persistency. The service stops and all disappear.

Since a Session level 1 cache is actually maintained using PerpetualCache, how is PerpetualCache implemented?

The implementation of PerpetualCache is simple, internally implemented with a simple HashMap<k,V>, no other restrictions.

PerpetualCache A so-called cache is just a hashmap 1, in memory, not persisted, stops the program cache disappearing. Hashmap, thread unsafe. Similarly, eureka-server’s two-layer map stores the service address list: 1. In memory, no persistence is carried out, and the stop program cache disappears. ConcurrentHashMap

>, outer ConcurrentHashMap, thread-safe.
,map

Here’s the implementation code for PerpetualCache:

package org.apache.ibatis.cache.impl; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import org.apache.ibatis.cache.Cache; import org.apache.ibatis.cache.CacheException; @author Clinton Begin */ public class PerpetualCache implements Cache {private String id; private Map<Object, Object> cache = new HashMap<Object, Object>(); public PerpetualCache(String id) { this.id = id; } public StringgetId() {
    return id;
  }
 
  public int getSize() {
    return cache.size();
  }
 
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }
 
  public Object getObject(Object key) {
    return cache.get(key);
  }
 
  public Object removeObject(Object key) {
    return cache.remove(key);
  }
 
  public void clear() {
    cache.clear();
  }
 
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
 
  public boolean equals(Object o) {
    if (getId() == null) throw new CacheException("Cache instances require an ID.");
    if (this == o) return true;
    if(! (o instanceof Cache))return false;
 
    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }
 
  public int hashCode() {
    if (getId() == null) throw new CacheException("Cache instances require an ID.");
    returngetId().hashCode(); }}Copy the code

3.3 What is the life cycle of a Level 1 cache PerpetualCache? Three ways to end Level 1 Cache PerpetualCache (done)

When MyBatis starts a database session, it creates a new SqlSession object, which has a new Executor object, which holds a new PerpetualCache object.

A. session.close() If a SqlSession calls close(), the session ends, the SqlSession object, its internal Executor object, and the PerpetualCache object are also released, i.e. the tier 1 cache is not available when the Tier 1 cache is freed;

Goldfinger: One session for one SqlSession, one Executor object, and one PerpetualCache object; The session ends and all disappear without persistency. The service stops and all disappear.

B. session.clearCache() method: If clearCache() is called by SqlSession, the data in the PerpetualCache object is cleared, but the object is still usable;

Session.update (), session.delete(), session.insert() : Every UPDATE operation performed in SqlSession (update(), delete(), INSERT ()) clears data from the PerpetualCache object, but the object continues to be used;

Summary: Tier 1 cache lifecycle, three ways to end Tier 1 cache PerpetualCache: a.session.close () If a SqlSession calls close(), the session ends, the SqlSession object, its internal Executor object, and the PerpetualCache object are also released, i.e. the tier 1 cache is not available when the Tier 1 cache is freed; B. session.clearCache() method: If clearCache() is called by SqlSession, the data in the PerpetualCache object is cleared, but the object is still usable; Session.update (), session.delete(), session.insert() : Every UPDATE operation performed in SqlSession (update(), delete(), INSERT ()) clears data from the PerpetualCache object, but the object continues to be used;

3.4 SqlSession Level 1 Cache workflow (build cacheKey+ query by cacheKey, completed)

  1. Build cacheKey: for a query, on the basis of statementId rowBounds, boundSql, params to build a key value (goldfinger: BaseExecutor createCacheKey in class () method), is cacheKey values.

  2. Query by cacheKey: Cache results of fetching the corresponding key from the Cachkey Cache (Goldfinger: getObject() from BaseExecutor is invoked by the Query () method); Determine whether the data fetched from the Cache based on a specific key value is null, that is, hit (Goldfinger: query() method in BaseExecutor);

    (1) If a match is made, the cached result will be returned directly;

    (2) if lost: ① go to the database to query the data, get the query results; ② The key and the queried result are stored in the Cache as key and value pairs, respectively. ③ Return the query result.

Build cacheKey+ query from cacheKey

3.5 Bonus: Level 1 cache low-level design (very important, completed)

As shown in the figure below, MyBatis defines an org. Apache. Ibatis. Cache. The cache Interface as its cache Provider of SPI (Service Provider Interface), all of the internal cache cache MyBatis, All should implement this interface. MyBatis defines a PerpetualCache implementation class that implements the Cache interface. In fact, instances of the Cache type (interface oriented, so the Cache type is maintained) are maintained within the Executor object in the SqlSession object. That’s the PerpetualCache subclass.

There are a number of implementations of the Cache interface in MyBatis, level 1 Cache is only addressed by the PerpetualCache subclass, other implementations of the Cache are covered by level 2 caches.

3.5.1 Two Functions of the CacheKey

A CacheKey does two things:

  1. Cachekey is used as the key of a HashMap to find the Cache result (that is, a HashMap);
  2. If the Cache fails to match, the cacheKey is removed from the database and written to the Cache to ensure that the Cache is hit next time. If the Cache fails to match, the cacheKey is used as the key and the result queried from the database is used as a value. The value pair is stored in the Cache.

3.5.2 How can I determine the CacheKey? How can I tell if two queries are exactly the same?

As we know, the core implementation of Cache is actually a Map, in which the eigenvalues used in the query are taken as keys and the query results are stored as values in the Map.

Now comes the core question: how do you determine the eigenvalues of a query?

In other words: How do you tell if two queries are exactly the same query?

How do I determine the key in the Cache?

MyBatis considers two queries to be exactly the same if the following four conditions are exactly the same:

  1. StatementId: Indicates the incoming statementId
  2. Paging filtering: The range of results in the result set required when querying (the range of results is represented by rowbounds. offset and rowbounds. limit);
  3. Eventually produced by dynamic SQL, the query to be passed to the JDBC Java. The SQL statements. SQL Preparedstatement words (* * boundSql getSql () * * dynamic SQL)
  4. Parameter value: The parameter value passed to java.sql.Statement to be set

Now explain each of the four conditions:

  1. The statementId passed in, for MyBatis, you need a statementId to use it, it represents what Sql you are going to execute;
  2. The paging function provided by MyBatis itself is realized by RowBounds, which filters the query result set through RowBounds. Offset and RowBounds. Limit.

MyBatis needs to ensure that two identical queries are identical to the underlying JDBC query because MyBatis relies on JDBC. For JDBC, two queries are considered to be consistent as long as the SQL statements and parameters passed to JDBC are identical.

The third condition above requires that the SQL statements passed to JDBC be exactly the same; The fourth rule is to ensure that the parameters passed to JDBC are also exactly the same;

3, 4 May be vague, take an example:

  <select id="selectByCritiera" parameterType="java.util.Map" resultMap="BaseResultMap">
        select employee_id,first_name,last_name,email,salary
        from louis.employees
        where  employee_id = #{employeeId}
        and first_name= #{firstName}
        and last_name = #{lastName}
        and email = #{email}
  </select>
Copy the code

MyBatis will replace all #{} in SQL with? As follows:

select employee_id,first_name,last_name,email,salary
from louis.employees
where  employee_id = ?
and first_name= ?
and last_name = ?
and email = ?
Copy the code

MyBatis will eventually use the SQL string to create the JDBC Java. The. SQL PreparedStatement object, for this PreparedStatement object, you also need to set parameters for it, call setXXX () to complete the set value, the conditions of article 4, The parameter values in the PreparedStatement for JDBC must be the same.

Namely, the most essential requirements of 3 and 4 MyBatis are:

When calling JDBC, the SQL statements passed in must be identical, and the parameter values passed to JDBC must be identical.

To sum up, a CacheKey is determined by:

StatementId + rowBounds + SQL passed to JDBC + parameter values passed to JDBC

3.5.3 CacheKey Construction Algorithm (The BaseExecutor class createCacheKey() method)

For each query request, Executor creates a Corresponding CacheKey object based on the parameters passed and dynamically generated SQL statements using the above conditions, which are the cacheKey construction algorithm and the unique signature of the CacheKey.

The CacheKey build is placed in the Executor interface implementation class BaseExecutor, defined as follows:

/ * * * belongs to class: org. Apache. Ibatis. Executor. BaseExecutor * function: CacheKey */ public CacheKey createCacheKey(MappedStatement MS, Object parameterObject, RowBounds RowBounds, BoundSql boundSql) {if (closed) throw new ExecutorException("Executor was closed."); Cachekey cachekey = new cachekey (); Cachekey.update (Ms. GetId ())) is provided in the MappedStatement object. //2. Rowbounds. offset and rowbounds. limit, arguments rowBounds object provides cacheKey.update(rowbounds.getoffset ()); cacheKey.update(rowBounds.getLimit()); BoundSql object provides cacheKey.update(boundSQL.getsQL ()); / / 4 parameters: Update each parameter value to be passed to JDBC to the CacheKey as well, Parameter parameterObject object provides a List < ParameterMapping > parameterMappings = boundSql. GetParameterMappings (); TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();for (int i = 0; i < parameterMappings.size(); i++) { // mimic DefaultParameterHandler logic
      ParameterMapping parameterMapping = parameterMappings.get(i);
      if(parameterMapping.getMode() ! = ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) {
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else{ MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } // Update each parameter value to be passed to JDBC to cachekey.update (value) in CacheKey; }} // After setting the cacheKey's unique signature, done, returnsreturn cacheKey;
  }    
Copy the code

The createCacheKey() method determines the unique signature of the CacheKey. The four cachekey parameters are provided by the four parameters of createCacheKey(). The MappedStatement provides the statementId. ParameterObject provides SQL parameters, RowBounds provides paging filtering conditions, and BoundSql provides dynamic SQL.

3.5.4 CacheKey HashCode Generation Algorithm (update())

An implementation of the Cache interface is essentially stored using a HashMap

, and a CacheKey is built to act as a key in a HashMap

. Whereas hashmaps are organized and stored by the key’s Hashcode, the process of building a CacheKey is really the process of constructing its Hashcode. The following code is the CacheKey’s core HashCode generation algorithm, if you’re interested:
,v>
,v>

  public void update(Object object) {
    if(object ! = null && object.getClass().isArray()) { int length = Array.getLength(object);for(int i = 0; i < length; i++) { Object element = Array.get(object, i); doUpdate(element); }}else{ doUpdate(object); } } private void doUpdate(Object object) { //1. Get the hashcode of the object; int baseHashCode = object == null ? 1 : object.hashCode(); //2. The object count is incremented, and the hashcode of the object is expanded by count times count++; checksum += baseHashCode; baseHashCode *= count; HashCode = multiplier * hashCode + baseHashCode; hashCode = multiplier * hashCode + baseHashCode; updateList.add(object); }Copy the code

1. The CacheKey serves two purposes: (1) a cache hit is removed from a HashMap based on the CacheKey; (2) If the cache is not hit, the cacheKey is removed from the database and written to the cache to ensure that the cache is hit again. 2. Cachekey determination: StatementId + rowBounds + SQL passed to JDBC + parameter values passed to JDBC The four cacheKey parameters are provided by the four parameters of createCacheKey(), the MappedStatement provides the statementId, ParameterObject provides SQL parameters, RowBounds provides paging filtering criteria, and BoundSql provides dynamic SQL. Get the hashcode of the object; int baseHashCode = object == null ? 1 : object.hashCode(); //2. The object count is incremented, and the hashcode of the object is expanded by count times count++; checksum += baseHashCode; baseHashCode = count; HashCode = multiplier * hashCode + baseHashCode; hashCode = multiplier * hashCode + baseHashCode;

3.6 Performance analysis of level 1 cache and matters needing attention

I will discuss SqlSession level 1 cache performance in terms of two level 1 cache features:

3.6.1 Can I Use a CERTAIN SqlSession Object to Query Data All the time? If HashMap is Too Large, an OOM Error Occurs

1.MyBatis has a relatively simple design for the Session level cache. It simply uses HashMap to maintain the cache and does not limit the capacity and size of HashMap.

Readers may feel wrong: if I have been using a SqlSession object query data, this will not lead to a HashMap is too big, lead to Java. Lang. An OutOfMemoryError? Readers might be right to think so, but that’s exactly how MyBatis is designed.

MyBatis has its own reasons for doing so:

A. Release at the end of session: Generally speaking, the lifetime of SqlSession is short. In general, a SqlSession object does not perform many operations and then dies.

B. Update operation release: For a CERTAIN SqlSession object, as long as the execution of update operations (UPDATE, INSERT, delete), the corresponding level of the SqlSession object will be cleared, so in general, the cache is not too large, affecting the JVM memory space problem;

C. Manual release: You can manually release the cache in the SqlSession object.

3.6.2 Level 1 cache is a Hashmap, no cache expiration, no cache updates

  1. Level 1 cache is a coarse-grained cache with no concepts of cache update and cache expiration

MyBatis uses a simple HashMap for level 1 cache. MyBatis is only responsible for storing the results of the query database in the cache. It does not determine whether the cache is too long or expired, so there is no update of the cache results.

According to the characteristics of level 1 cache, I think we should pay attention to: 1, for the data change frequency is very large, and the need for high accuracy of the data requirements, we use SqlSession query, to control the SqlSession survival time, SqlSession survival time is longer, it is possible that the cache of data is older, resulting in and real database error; In this case, the user can also manually clear the CACHE in the SqlSession.

2. For SqlSession objects that only perform and frequently perform large select operations, the LIFETIME of the SqlSession object should not be too long.

For example:

SQL > select * from SqlSession; SQL > select * from SqlSession; SQL > select * from SqlSession

package com.louis.mybatis.test; import java.io.InputStream; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ibatis.executor.BaseExecutor; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.log4j.Logger; import com.louis.mybatis.model.Employee; @author louluan */ public class SelectDemo1 {private static final Logger loger = Logger.getLogger(SelectDemo1.class); public static void main(String[] args) throws Exception { InputStream inputStream = Resources.getResourceAsStream("mybatisConfig.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(inputStream); SqlSession sqlSession = factory.openSession(); Params = new HashMap<String,Object>(); params.put("min_salary", 10000); Date first = new Date(); List<Employee> result = sqlsession.selectList ("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params);
		loger.info("first quest costs:"+ (new Date().getTime()-first.getTime()) +" ms");
		Date second = new Date();
		result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params);
		loger.info("second quest costs:"+ (new Date().getTime()-second.getTime()) +" ms"); }}Copy the code

Running results:

As you can see from the above results, the first query takes 464ms, while the second query takes less than 1ms, this is because after the first query, MyBatis will store the query result in the cache of SqlSession object, when the same query later, directly from the cache.

Select params (HashMap), params (HashMap), params (HashMap), params (HashMap);

As a result, the cache of the first query was pulled from the level 1 cache, even though the params parameters passed in the second query were inconsistent.

At this point, let the reader know this question:

MyBatis does not mean that all parameters passed to the Session are exactly the same. You just need to ensure that the SQL statement generated by statementId, rowBounds, And the parameters required by the SQL statement are exactly the same.

Not all parameters are the same, but all dynamically generated SQL requires the same parameters.

Mybatis level 2 cache

MyBatis level 2 cache is the Application level cache, it can improve the efficiency of database query, to improve the performance of the Application. This paper will fully analyze the design principle of MyBatis secondary cache.

4.1 The overall design of cache mechanism of MyBatis and the working mode of two-level cache

MyBatis’ second-level caching mechanism is based on an Executor object that is used by a SqlSession object to perform session operations when opening a session, as shown above. If cacheEnabled=true is configured, MyBatis will add a decorator to the Executor object when creating it for the SqlSession object: CachingExecutor, in which case the SqlSession uses the CachingExecutor object to complete the operation request. CachingExecutor checks whether the query request has a cached result in the level-2 cache of the Application level. If yes, the CachingExecutor returns the cached result directly. If not, the actual Executor object is used to perform the query. CachingExecutor then places the query result returned by the real Executor into the cache and returns it to the user.

CachingExecutor is an Executor decorator to enhance the Executor by caching queries, using the decorator pattern in the design pattern,

The relationship between CachingExecutor and Executor’s interface is shown in the following class diagram:

4.2 Mapper level 2 Operations (two types: one Mapper, one cache object + multiple Mapper, one cache object)

MyBatis does not simply have a Cache object for the whole Application, it divides the Cache into more details, namely Mapper level, that is, each Mapper can have a Cache object, there are two types:

A. Assign a Cache object to each Mapper (using node configuration);

B. Multiple mappers share a Cache object (using node configuration);

Level 1 cache granularity: coarse-grained, available to all requests, cacheKey is statementId + rowBounds + SQL passed to JDBC + parameter value passed to JDBC. Level 1 cache life cycle: short life cycle, three kinds of release: end of session release, update operation release, manual release. Level 1 cache read operation: if hit, get out; if not hit, get out of the database and update the cache; Level 1 cache write operation: Direct release. Level 1 buffer application: 1, for the data change frequency is very large, and the need for high accuracy of the data requirements, we use SqlSession query, to control the SqlSession survival time, SqlSession survival time is longer, it is possible that the cache of data is older, resulting in and real database error; In this case, the user can also manually clear the CACHE in the SqlSession. 2. For SqlSession objects that only perform and frequently perform large select operations, the LIFETIME of the SqlSession object should not be too long.

Level 2 cache granularity: Fine granularity, each mapper can only use its level 2 cache. The second level cache life cycle: long life cycle, the whole application/end release configuration. Level 2 cache read operation: if hit, get out; if not hit, get out of database and update cache; Level 2 cache write operations: Level 2 Cache application:

Add: Redis read operation: hit it out, not hit it out from the database and update the cache; Redis write operations: write direct and write back (redis is written on the next read request)

If you want multiple mappers to share a Cache, you can use nodes to specify which Mapper’s Cache is used by your Mapper.

Summary: Mapper level 2 operations (two types: one Mapper, one cache object + multiple Mapper, one cache object)

4.3 Conditions for Using Level 2 Cache (Sql statement level operations in Level 2 Cache Mapper)

MyBatis support for level 2 caching is fine-grained, specifying whether a particular query statement in xxxmapper.xml uses level 2 caching.

Although Mapper is configured and Cache objects are allocated to the Mapper, this does not mean that the results of the query defined in Mapper will be placed in the Cache object. We must specify whether a select statement in Mapper supports caching, as shown below. When useCache is set to true on the node, Mapper supports caching for the Select query. Otherwise, the Select query does not pass the Cache. If the Select statement is set to useCache= “true” as shown below, the Select statement will use level 2 caching.

 <select id="selectByMinSalary" resultMap="BaseResultMap" parameterType="java.util.Map" useCache="true">
Copy the code

Level 2 cache support is fine-grained and specifies whether a particular query statement in xxxmapper.xml uses level 2 cache

In summary, to enable a Select query to support level 2 caching, you need to ensure three things:

  1. MyBatis supports the master switch for level 2 caching: the global configuration variable parameter cacheEnabled=true
  2. Mapper. XML level: The Mapper of the select statement is configured with or nodes and is valid
  3. SQL level in mapper.xml: The parameter of the select statement useCache=true

Summary: Conditions for using level 2 cache (three) :

  1. MyBatis supports the master switch for level 2 caching: cacheEnabled=true;
  2. Mapper. XML level: The Mapper where the select statement resides is configured with or nodes and valid.
  3. SQL level in mapper.xml: The parameter of the select statement useCache=true.

4.4 Sequence of Level-1 cache and Level-2 Cache

If MyBatis is using level 2 cache and Mapper and SELECT statements are configured to use level 2 cache, MyBatis will retrieve input from level 2 cache first and then from Level 1 cache.

Level 2 cache — > Level 1 cache — > database

Summary: Cache use order: level 2 cache – > Level 1 cache – > database

4.5 Three implementation modes of level 2 cache

MyBatis is very flexible in the design of level 2 cache,

First, it internally implements a series of Cache implementation classes and provides various Cache refresh strategies such as LRU, FIFO, etc.

Second, MyBatis also allows users to customize the Cache implementations of the interface, the user is the need to implement org. Apache. Ibatis. Cache. The Cache interface, then the Cache implementation class configuration on the type attribute of the node;

MyBatis also supports integration with third-party memory caching libraries such as Memecached. In short, there are three options for using MyBatis level 2 cache:

(1) Cache implementation provided by MyBatis itself;

(2) User-defined Cache interface implementation;

(3) Integration with third-party memory cache libraries;

Summary: Three implementation methods of level 2 cache: (1) Cache implementation provided by MyBatis; (2) User-defined Cache interface implementation; (3) Integration with third-party memory cache libraries;

4.6 The first implementation: implementation of level 2 cache provided by MyBatis itself (10 level 2 cache implementation classes)

MyBatis itself provides a rich and powerful implementation of level 2 Cache, it has a series of Cache interface decorators, can meet a variety of Cache operations and update strategies.

MyBatis defines a large number of Cache decorators to enhance the Cache function, as shown in the following class diagram.

Level 2 cache has 10 implementation classes and lacks BlockingCache.

Level 1 cache and level 2 cache are different for Mybatis

Similarities: level 1 cache PerpetualCache and 10 second level cache implementation classes are org. Apache. Ibatis. Cache. The cache interface,

Difference:

Level 1 cache PerpetualCache in org. Apache. Ibatis. Cache. The impl package,

10 second level cache implementation class in org. Apache. Ibatis. Cache. The decorators in the package.

Different implementation classes of level 2 cache are essentially different cache strategies

For each Cache, there is a capacity limit, MyBatis provides a variety of strategies to control the size of the Cache, and to refresh and replace the data in the Cache.

MyBatis provides the following refresh and replacement strategies:

LRU :(Least Recently Used) if the cache capacity is full, it will delete the Recently Used cache records and add new records, such as the LruCache class.

FIFO :(First in First out), First in First out algorithm, if the cache capacity is full, then the First data into the cache will be cleared, such as FifoCache class;

Scheduled: Scheduled an algorithm that deletes data from a Cache at a specified time interval, such as the ScheduledCache class.

Summary: The first implementation method: the implementation of level 2 cache provided by MyBatis. First, the level 1 cache and level 2 cache of MyBatis are different. Level 1 cache PerpetualCache and 10 second level cache implementation classes are org. Apache. Ibatis. Cache. The cache interface, difference: Level 1 cache PerpetualCache in org. Apache. Ibatis. Cache. The impl package, 10 second level cache implementation class in org. Apache. Ibatis. Cache. The decorators in the package. Second, the different implementation classes of level 2 cache are essentially different cache policies, such as LRU, FIFO, Scheduled.

How to fine-grained control your MyBatis level 2 cache (MyBatis enhanced Cache)

This part analyzes the deficiencies of the existing MyBatis level 2 cache step by step, discusses some improvements, and develops a plug-in to make up for the deficiencies.

5.1 A practical question about MyBatis level 2 cache

Amapper. XML defines CRUD operations on database table ATable, and BMapper defines CRUD operations on database table BTable. Assume that level 2 cache of MyBatis is enabled, and level 2 cache is used in AMapper. Level 2 cache of AMapper is ACache. In addition, AMapper also defines a BTable related query statement, similar to the following:

<select id="selectATableWithJoin" resultMap="BaseResultMap" useCache="true">
      select * from ATable left join BTable on ....
</select>
Copy the code

Perform the following operations:

  1. Execute the “selectATableWithJoin” operation in AMapper, at this time, the query result will be placed in the secondary cache ACache corresponding to AMapper;
  2. The BTable data is updated after update, delete, and INSERT operations on the BMapper are performed.
  3. Then perform 1 exactly the same query, at this time the value will be directly from the AMapper level cache ACache, the ACache value directly returned;

Well, here’s the problem with step 3:

Since the SQL statement corresponding to AMapper’s “selectATableWithJoin” needs to perform join lookup with BTable, and the data of BTable in step 2 has been updated, but the value queried in step 3 is the cached value of step 1, There is a high possibility that the data cached in ACache is out of date.

To sum up, it is:

For some queries that use JOIN join, if the associated table data is updated, the query result of join join is not synchronized with the real data due to the previous cache.

From MyBatis’ point of view, the question can be phrased as follows:

For some tables to perform update (UPDATE, delete, INSERT) operations, how to clear the table associated with the query statement caused by the cache (level 1 cache data update cache release, but the level 2 cache will still be retained, to think about how to deal with, from which we can see the elegance of level 1 cache);

The current caching mechanism of MyBatis can not deal with this problem well, we will start from the current caching mechanism of MyBatis to analyze this problem:

5.2 Current working mechanism of MyBatis Level 2 Cache

Current MyBatis level 2 cache working mechanism:

An important feature of MyBatis level 2 Cache: loose Cache management and maintenance.

Add, delete, modify, and query operations defined in a Mapper affect only the Cache objects associated with it. For several CRUD statements defined in Mapper Namespace1, as shown above, the resulting cache is only placed in the corresponding associated Cache1. The Mapper namespace2 namespace3, the CRUD statements will not affect the Cache1 namespace4.

It can be seen that the cache relationship between Mapper is loose and the degree of correlation is weak.

If the AMapper and BMapper share the same Cache object, then when the BMapper performs the update operation, it can clear all the cached data in the corresponding Cache. In this way, the data can also be kept up to date.

Yes, this is a solution, but it makes caching very inefficient! Any update operation of AMapper and BMapper will empty the shared Cache. The Cache will be emptied frequently, resulting in low Cache hit ratio and usage. Therefore, this strategy is not recommended in practice.

The ideal solution is:

How to clear the cache caused by the query statements associated with some tables after performing update (UPDATE, delete, INSERT) operations;

In this way, the internal cache of MyBatis is managed with very fine granularity, which greatly improves the utilization rate and accuracy of cache.

Based on this idea, I wrote a corresponding Mybatis -enhanced cache plug-in, which can well support the above functions.

For the example above, the plug-in can specify that the cache generated in ACache by the selectATableWithJoin query associated with the BTable is cleared when the BMapper performs an update operation on the BTable.

Let’s take a look at the design principles of mybatis-enhanced cache.

5.3 Design and working principle of mybatis-enhanced cache plug-in

The plug-in consists mainly of two artifacts: EnhancedCachingExecutor and EnhancedCachingManager.

5.3.1 EnhancedCachingExecutor artifacts

EnhancedCachingExecutor is an Executor interceptor that intercepts several key Executor methods.

EnhancedCachingExecutor does the following things:

  1. Whenever an Executor executes a Query operation,

(1) record the query StatementId and CacheKey, and add them to the EnhancedCachingManager;

(2) record the query StatementId and the Cache object reference in the Mapper to which this StatementId belongs, and add it to EnhancedCachingManager;

  1. Each time the Executor performs an update, it passes the StatementId of the update to the EnhancedCachingManager, and has the EnhancedCachingManager, based on the StatementId configuration of the update, To clear the cache generated by the specified query statement;

5.3.2 EnhancedCachingManager artifacts

Another artifact, EnhancedCachingManager, which is also the core of this plug-in, maintains the following things:

  1. The CacheKey set (classified by statementId) generated by all queries across MyBatis;
  2. References to all used query Statementids and their corresponding Cache objects;
  3. A mapping between the StatementId of the update type and the query StatementId collection, used to determine which query statements should be cleared from the cache when an UPDATE statement is executed.

As shown below:

Working principle:

When an UPDATE operation is performed, the Cache data generated by the specified query statement is cleared based on the configuration information.

How to obtain mybatis-enhanced cache plugin source code

  1. Source code and JAR package 2 in one compression package
  2. Github address: github.com/LuanLouis/m…

5.4 Example of using the Mybatis -enhanced cache plug-in

  1. Download the mybatis-enhanced Cache. rar package, decompress it, and add mybatis-enhanced Cache-0.0.1 – snapshot. jar to the classpath of the project.
  2. Configure the MyBatis profile as follows:
  <plugins>
       <plugin interceptor="org.luanlouis.mybatis.plugin.cache.EnhancedCachingExecutor">
          <property name="dependency" value="dependencys.xml"/>
          <property name="cacheEnabled" value="true"/>
       </plugin>
  </plugins>
Copy the code

Where, the value attribute in is the configuration file path of the dependency between StatementId.

  1. Configure the dependency relationship between StatementId
<? xml version="1.0" encoding="UTF-8"? > <dependencies> <statements> <statement id="com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey">
          <observer id="com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments" />
       </statement>
   </statements>
</dependencies>
Copy the code

The node is configured with the statementId of the update statement, and its children are configured with the statementId of the query statement that should be cleared from the cache when the update statement is executed. There can be multiple child nodes.

As the configuration of the show, if “com. Louis. Mybatis. Dao. DepartmentsMapper. UpdateByPrimaryKey” update statement execution, By “com. Louis. Mybatis. Dao. EmployeesMapper. SelectWithDepartments” statements generated by the data will be placed in the Cache Cache is cleared.

  1. Configure departmentSmapper. XML and employeesmapper.xml

DepartmentsMapper.xml

<? 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.louis.mybatis.dao.DepartmentsMapper" >
   
   <cache></cache>
 
  <resultMap id="BaseResultMap" type="com.louis.mybatis.model.Department" >
    <id column="DEPARTMENT_ID" property="departmentId" jdbcType="DECIMAL" />
    <result column="DEPARTMENT_NAME" property="departmentName" jdbcType="VARCHAR" />
    <result column="MANAGER_ID" property="managerId" jdbcType="DECIMAL" />
    <result column="LOCATION_ID" property="locationId" jdbcType="DECIMAL" />
  </resultMap>
  
  
  <sql id="Base_Column_List" >
    DEPARTMENT_ID, DEPARTMENT_NAME, MANAGER_ID, LOCATION_ID
  </sql>
  
  <update id="updateByPrimaryKey" parameterType="com.louis.mybatis.model.Department" >
    update HR.DEPARTMENTS
    set DEPARTMENT_NAME = #{departmentName,jdbcType=VARCHAR},
      MANAGER_ID = #{managerId,jdbcType=DECIMAL},
      LOCATION_ID = #{locationId,jdbcType=DECIMAL}
    where DEPARTMENT_ID = #{departmentId,jdbcType=DECIMAL}
  </update>
    <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from HR.DEPARTMENTS
    where DEPARTMENT_ID = #{departmentId,jdbcType=DECIMAL}
  </select>
</mapper>
Copy the code

EmployeesMapper.xml

<? 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.louis.mybatis.dao.EmployeesMapper">
 
  <cache eviction="LRU" flushInterval="100000" size="10000"/>
 
  <resultMap id="BaseResultMap" type="com.louis.mybatis.model.Employee">
    <id column="EMPLOYEE_ID" jdbcType="DECIMAL" property="employeeId" />
    <result column="FIRST_NAME" jdbcType="VARCHAR" property="firstName" />
    <result column="LAST_NAME" jdbcType="VARCHAR" property="lastName" />
    <result column="EMAIL" jdbcType="VARCHAR" property="email" />
    <result column="PHONE_NUMBER" jdbcType="VARCHAR" property="phoneNumber" />
    <result column="HIRE_DATE" jdbcType="DATE" property="hireDate" />
    <result column="JOB_ID" jdbcType="VARCHAR" property="jobId" />
    <result column="SALARY" jdbcType="DECIMAL" property="salary" />
    <result column="COMMISSION_PCT" jdbcType="DECIMAL" property="commissionPct" />
    <result column="MANAGER_ID" jdbcType="DECIMAL" property="managerId" />
    <result column="DEPARTMENT_ID" jdbcType="DECIMAL" property="departmentId" />
  </resultMap>
 
  <sql id="Base_Column_List">
    EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB_ID, SALARY, 
    COMMISSION_PCT, MANAGER_ID, DEPARTMENT_ID
  </sql>
  
  <select id="selectWithDepartments" parameterType="java.lang.Integer" resultMap="BaseResultMap" useCache="true" >
    select 
    *
    from HR.EMPLOYEES t left join HR.DEPARTMENTS S ON T.DEPARTMENT_ID = S.DEPARTMENT_ID
    where EMPLOYEE_ID = #{employeeId,jdbcType=DECIMAL}
  </select>
  
</mapper>
Copy the code

Level 2 cache uses three steps:

  1. Test code:
package com.louis.mybatis.test; import java.io.InputStream; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.log4j.Logger; import com.louis.mybatis.model.Department; import com.louis.mybatis.model.Employee; @author louluan */ public class SelectDemo3 {private static final Logger loger = Logger.getLogger(SelectDemo3.class); public static void main(String[] args) throws Exception { InputStream inputStream = Resources.getResourceAsStream("mybatisConfig.xml");
		SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
		SqlSessionFactory factory = builder.build(inputStream);
		
		SqlSession sqlSession = factory.openSession(true);
		SqlSession sqlSession2 = factory.openSession(true); Params = new HashMap<String,Object>(); params.put("employeeId", 10); Date first = new Date(); List<Employee> result = sqlsession.selectList ("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); sqlSession.commit(); checkCacheStatus(sqlSession); // first check // second query params.put("employeeId"11); result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); sqlSession.commit(); checkCacheStatus(sqlSession); // query params.put(params."employeeId", 12);
        result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); sqlSession.commit(); checkCacheStatus(sqlSession); Params. Put (params."employeeId", 13);
        result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectWithDepartments",params); sqlSession.commit(); checkCacheStatus(sqlSession); Department Department = sqlsession.selectOne ()"com.louis.mybatis.dao.DepartmentsMapper.selectByPrimaryKey", 10); department.setDepartmentName("updated");
		sqlSession2.update("com.louis.mybatis.dao.DepartmentsMapper.updateByPrimaryKey", department); sqlSession.commit(); checkCacheStatus(sqlSession); } public static void checkCacheStatus(SqlSession SqlSession) {loger.info("------------Cache Status------------");
		Iterator<String> iter = sqlSession.getConfiguration().getCacheNames().iterator();
		while(iter.hasNext())
		{
			String it = iter.next();
			loger.info(it+":"+sqlSession.getConfiguration().getCache(it).getSize());
		}
		loger.info("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -"); }}Copy the code

Result output:

Results analysis:

Can be seen from the above results, the first four times performed “com. Louis. Mybatis. Dao. EmployeesMapper. SelectWithDepartments” statement, The number of result caches stored in the EmployeesMapper Cache increases from 1 to 4.

When performing the “com. Louis. Mybatis. Dao. DepartmentsMapper. UpdateByPrimaryKey” after EmployeeMapper corresponding buffer Cache results are cleared, Or “com. Louis. Mybatis. Dao. DepartmentsMapper. UpdateByPrimaryKey” in the update statement caused EmployeeMapper “. Com. Louis. Mybatis. Dao EmployeesMapp Er. selectWithDepartments” cache cleanup.

6, interview goldfinger/talk about your understanding of MyBatis cache? (Interview recitation)

6.1 Level-1 cache and Level-2 Cache

MyBatis designs the data cache into a two-level structure, which is divided into first-level cache and second-level cache:

Level 1 cache granularity: coarse-grained, available to all requests, cacheKey is statementId + rowBounds + SQL passed to JDBC + parameter value passed to JDBC. Level 1 cache life cycle: short life cycle, end of session release, update release, manual release. Level 1 cache read operation: if hit, get out; if not hit, get out of the database and update the cache; Level 1 cache write operation: Direct release. Level 1 buffer application: 1, for the data change frequency is very large, and the need for high accuracy of the data requirements, we use SqlSession query, to control the SqlSession survival time, SqlSession survival time is longer, it is possible that the cache of data is older, resulting in and real database error; In this case, the user can also manually clear the CACHE in the SqlSession. 2. For SqlSession objects that only perform and frequently perform large select operations, the LIFETIME of the SqlSession object should not be too long.

Granularity of level 2 cache: Fine granularity. Each Mapper can only use its own level 2 cache. Each SQL statement determines whether to enable level 2 cache. The second level cache life cycle: long life cycle, the whole application/end release configuration. Level 2 cache read operation: if hit, get out; if not hit, get out of database and update cache; Level 2 cache write operations: Level 2 Cache application:

Add: Redis read operation: hit it out, not hit it out from the database and update the cache; Redis write operations: write direct and write back (redis is written on the next read request)

6.2 How level 1 Cache is organized (Class diagram organization +hashMap Cache)

SqlSession, Executor, Cache

The Implementation of the Executor interface class, BaseExecutor, has a PerpetualCache implementation class for the Cache interface, so the BaseExecutor object maintains the Cache using the PerpetualCache object.



Second, because a Session level 1 cache is actually maintained using PerpetualCache

PerpetualCache The so-called cache is a hashmap

1, in memory, not persistent, stop the program cache disappeared.

Hashmap, thread unsafe.

Similarly, eureka-server’s two-layer map stores a list of service addresses:

1, in memory, not persistent, stop the program cache disappeared.

ConcurrentHashMap<String,Map<String,Instance>>, outer ConcurrentHashMap, thread-safe.

6.3 Lifecycle of Level-1 Cache PerpetualCache/End Four ways of Level-1 cache PerpetualCache

Summary: Tier 1 cache lifecycle, three ways to end Tier 1 cache PerpetualCache: a.session.close () If a SqlSession calls close(), the session ends, the SqlSession object, its internal Executor object, and the PerpetualCache object are also released, i.e. the tier 1 cache is not available when the Tier 1 cache is freed; B. session.clearCache() method: If clearCache() is called by SqlSession, the data in the PerpetualCache object is cleared, but the object is still usable; Session.update (), session.delete(), session.insert() : Every UPDATE operation performed in SqlSession (update(), delete(), INSERT ()) clears data from the PerpetualCache object, but the object continues to be used;

6.4 Level-1 Cache Query Process

  1. Build cacheKey: for a query, on the basis of statementId rowBounds, boundSql, params to build a key value (goldfinger: BaseExecutor createCacheKey in class () method), is cacheKey values.
  2. Query by cacheKey: Cache results of fetching the corresponding key from the Cachkey Cache (Goldfinger: getObject() from BaseExecutor is invoked by the Query () method); Determine whether the data fetched from the Cache based on a specific key value is null, that is, hit (Goldfinger: query() method in BaseExecutor);

(1) If a match is made, the cached result will be returned directly;

(2) if lost: ① go to the database to query the data, get the query results; ② The key and the queried result are stored in the Cache as key and value pairs, respectively. ③ Return the query result.

6.5 Underlying design of level 1 Cache

The CacheKey does two things: (1) a cache hit is pulled from a HashMap based on the CacheKey; (2) If the cache is not hit, the cacheKey is removed from the database and written to the cache to ensure that the cache is hit again. 2. Cachekey determination: StatementId + rowBounds + SQL passed to JDBC + parameter values passed to JDBC The four cacheKey parameters are provided by the four parameters of createCacheKey(), the MappedStatement provides the statementId, ParameterObject provides SQL parameters, RowBounds provides paging filtering criteria, and BoundSql provides dynamic SQL. Get the hashcode of the object; int baseHashCode = object == null ? 1 : object.hashCode(); //2. The object count is incremented, and the hashcode of the object is expanded by count times count++; checksum += baseHashCode; baseHashCode = count; HashCode = multiplier * hashCode + baseHashCode; hashCode = multiplier * hashCode + baseHashCode;

6.6 Level-2 Cache Operations at Mapper Level

Summary: Mapper level 2 operations (two types: one Mapper, one cache object + multiple Mapper, one cache object)

6.7 Level-2 Cache SQL statement-level operations and Level-2 Cache usage conditions (three)

Summary: Conditions for using level 2 cache (three) :

  1. MyBatis supports the master switch for level 2 caching: cacheEnabled=true;
  2. Mapper. XML level: The Mapper where the select statement resides is configured with or nodes and valid.
  3. SQL level in mapper.xml: The parameter of the select statement useCache=true.

6.8 Cache Usage Sequence

Summary: Cache use order: level 2 cache – > Level 1 cache – > database

6.9 Three implementation modes of level 2 Cache

Summary: Three implementation methods of level 2 cache: (1) Cache implementation provided by MyBatis; (2) User-defined Cache interface implementation; (3) Integration with third-party memory cache libraries;

6.10 The first implementation: the implementation of level 2 cache provided by MyBatis itself

Summary: The first implementation method: the implementation of level 2 cache provided by MyBatis. First, the level 1 cache and level 2 cache of MyBatis are different. Level 1 cache PerpetualCache and 10 second level cache implementation classes are org. Apache. Ibatis. Cache. The cache interface, difference: Level 1 cache PerpetualCache in org. Apache. Ibatis. Cache. The impl package, 10 second level cache implementation class in org. Apache. Ibatis. Cache. The decorators in the package. Second, the different implementation classes of level 2 cache are essentially different cache policies, such as LRU, FIFO, Scheduled.

Seven, summary

Mybatis cache understanding, done.

Play code every day, progress every day!!