For the use of mybatis cache, just read this article

The importance of caching is self-evident. Using cache, we can avoid frequent interaction with the database, especially when the more queries and the higher the cache hit rate, the use of cache can significantly improve the performance.

mybatis also provides cache support, which is divided into L1 cache and L2 cache. However, by default, only the L1 cache is enabled (L1 cache is for the same SqlSession).

The following items are in Preliminary use of mybatis (Maven project of IDEA, super detailed) On the basis of.

For the following code, you can also My GitHub Get the corresponding item in.

L1 cache

For the same SqlSession object, execute the SQL statement only once (if the cache does not expire) when the parameters are exactly the same as SQL

That is, this can only happen when the parameters are exactly the same as SQL.

1.1 the same SqlSession

@Test
public void oneSqlSession() {
    SqlSession sqlSession = null;
    try {
        sqlSession = sqlSessionFactory.openSession();
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    // Execute first query
    List<Student> students = studentMapper.selectAll();
    for (int i = 0; i < students.size(); i++) {
        System.out.println(students.get(i));
    }
    System.out.println("=============Start the same Sqlsession Second query of============");
    // Query the same sqlSession for the second time
    List<Student> stus = studentMapper.selectAll();
    Assert.assertEquals(students, stus);
    for (int i = 0; i < stus.size(); i++) {
        System.out.println("stus:" + stus.get(i));
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (sqlSession != null) {
        sqlSession.close();
    }
}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

In the above code, two queries are performed, using the same SqlSession, and the results are as follows

In logs and output:

The first query sends the SQL statement and returns the result;

The second query did not send an SQL statement, and the results were obtained directly from memory.

Moreover, the two result inputs are consistent, and the assertion that the two objects are the same also passes.

1.2 different sqlsessions

 @Test
public void differSqlSession() {
    SqlSession sqlSession = null;
    SqlSession sqlSession2 = null;
    try {
        sqlSession = sqlSessionFactory.openSession();
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    // Execute first query
    List<Student> students = studentMapper.selectAll();
    for (int i = 0; i < students.size(); i++) {
        System.out.println(students.get(i));
    }
    System.out.println("=============Start different Sqlsession Second query of============");
    // Create a new sqlSession2 for the second query
    sqlSession2 = sqlSessionFactory.openSession();
    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
    List<Student> stus = studentMapper2.selectAll();
    // Unequal
    Assert.assertNotEquals(students, stus);
    for (int i = 0; i < stus.size(); i++) {
        System.out.println("stus:" + stus.get(i));
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (sqlSession != null) {
        sqlSession.close();
    }
    if (sqlSession2 != null) {
        sqlSession2.close();
    }
}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

In the code, the same query is performed using sqlSession and sqlSession2 respectively.

The results are as follows

You can see from the log that the two queries took data from the database respectively. Although the results are the same, the two are different objects.

1.3 refresh cache

Refreshing the cache is to empty all caches of this SqlSession, not just a key.

@Test
public void sameSqlSessionNoCache() {
    SqlSession sqlSession = null;
    try {
        sqlSession = sqlSessionFactory.openSession();
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    // Execute first query
    Student student = studentMapper.selectByPrimaryKey(1);
    System.out.println("=============Start the same Sqlsession Second query of============");
    // Query the same sqlSession for the second time
    Student stu = studentMapper.selectByPrimaryKey(1);
    Assert.assertEquals(student, stu);
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (sqlSession != null) {
        sqlSession.close();
    }
}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

If it is the above, it is no different. The result is the second one without SQL statements.

Here, make some changes in studentmapper XML, add

flushCache="true"

The modified configuration file is as follows:

<select id="selectByPrimaryKey" flushCache="true" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from student
    where student_id=#{id, jdbcType=INTEGER}
</select>

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

The results are as follows:

The first time and the second time, SQL statements are sent. At the same time, there is an error asserting that the two objects are the same.

1.4 summary

  1. In the same SqlSession, Mybatis will generate cached key values for the executed methods and parameters through the algorithm, and store the key values and results in a Map. If the subsequent key values are the same, Mybatis will directly obtain data from the Map;

  2. Caches between different sqlsessions are isolated from each other;

  3. With a SqlSession, the cache can be emptied before query through configuration;

  4. Any update, insert or delete statement will empty the cache.

L2 cache

The L2 cache exists in the SqlSessionFactory lifecycle.

2.1 configuring L2 cache

2.1. 1 global switch

In mybatis, the L2 cache has a global switch and a sub switch. The global switch is in mybatis config XML is configured as follows:

<settings>
  <!--Globally turns on or off any cache that has been configured by all mappers in the configuration file. -->
  <setting name="cacheEnabled" value="true"/>
</settings>

 
  • 1
  • 2
  • 3
  • 4

The default is true, that is, the main switch is turned on by default.

2.1. 2 off switch

The opening and closing switch is in * mapper Enable or disable L2 cache in XML. It is not enabled by default.

2.1. 3. Entity implements the serialization interface

public class Student implements Serializable {
private static final long serialVersionUID = -4852658907724408209L;

...

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.2 using L2 cache

@Test
public void secendLevelCacheTest() {
// Get SqlSession object
SqlSession sqlSession = sqlSessionFactory.openSession();
//  Get Mapper object
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
// Use the corresponding method of Mapper interface to query the object with id=2
Student student = studentMapper.selectByPrimaryKey(2);
// Name of update object
student.setName("tea with milk");
// Use the same SqlSession again to query the object with id=2
Student student1 = studentMapper.selectByPrimaryKey(2);
Assert.assertEquals("tea with milk", student1.getName());
// The same SqlSession is a level-1 cache, and the two objects are the same
Assert.assertEquals(student, student1);

sqlSession.close();

SqlSession sqlSession1 = sqlSessionFactory.openSession();
StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class);
Student student2 = studentMapper1.selectByPrimaryKey(2);
Student student3 = studentMapper1.selectByPrimaryKey(2);
// Because we configured readOnly="true", the subsequent objects of the same SqlSession are different
Assert.assertEquals("tea with milk", student2.getName());
Assert.assertNotEquals(student3, student2);

sqlSession1.close();

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

The results are as follows:

2018-09-29 23:14:26,889 [main] DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Created connection 242282810.
2018-09-29 23:14:26,889 [main] DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e70f13a]
2018-09-29 23:14:26,897 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - ==>  Preparing: select student_id, name, phone, email, sex, locked, gmt_created, gmt_modified from student where student_id=? 
2018-09-29 23:14:26,999 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - ==> Parameters: 2(Integer)
2018-09-29 23:14:27,085 [main] TRACE [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - <==    Columns: student_id, name, phone, email, sex, locked, gmt_created, gmt_modified
2018-09-29 23:14:27,085 [main] TRACE [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - <==        Row: 2, Xiao Li, 13821378271, xiaoli@mybatis.cn, 0, 0, 2018-09-04 18:27:42.0, 2018-09-04 18:27:42.0
2018-09-29 23:14:27,093 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper.selectByPrimaryKey] - <==      Total: 1
2018-09-29 23:14:27,093 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper] - Cache Hit Ratio [com.homejim.mybatis.mapper.StudentMapper]: 0.0
2018-09-29 23:14:27,108 [main] DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@e70f13a]
2018-09-29 23:14:27,116 [main] DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@e70f13a]
2018-09-29 23:14:27,116 [main] DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Returned connection 242282810 to pool.
2018-09-29 23:14:27,124 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper] - Cache Hit Ratio [com.homejim.mybatis.mapper.StudentMapper]: 0.3333333333333333
2018-09-29 23:14:27,124 [main] DEBUG [com.homejim.mybatis.mapper.StudentMapper] - Cache Hit Ratio [com.homejim.mybatis.mapper.StudentMapper]: 0.5

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

The above results are explained in several processes:

Phase I:

  1. In the first SqlSession, query the student object and send the SQL statement;
  2. student changed the name attribute;
  3. SqlSession queries the student1 object again. At this time, no SQL statement is sent. The "Cache Hit Ratio" is printed in the log, which means that the L2 cache is used but missed. Because the L1 cache works first.
  4. Because it is a L1 cache, the two objects are the same at this time.
  5. Sqlsession. Was called Close(), which serializes and holds the data in the L2 cache.

Phase II:

  1. Create a new sqlsession Close() object;
  2. The student2 object is queried and the data is directly taken from the L2 cache, so the SQL statement is not sent. At this time, three objects are queried, but only one hit is found, so the hit rate is 1 / 3 = 0.333333;
  3. The student3 object is queried and the data is directly taken from the L2 cache, so the SQL statement is not sent. At this time, four objects are queried, but only one hit is found, so the hit rate is 2 / 4 = 0.5;
  4. Because readOnly = "true", student2 and student3 are obtained by deserialization and are different instances.

2.3 configuration details

Viewing the dtd file, you can see the following constraints:

<!ELEMENT cache (property*)>
<!ATTLIST cache
type CDATA #IMPLIED
eviction CDATA #IMPLIED
flushInterval CDATA #IMPLIED
size CDATA #IMPLIED
readOnly CDATA #IMPLIED
blocking CDATA #IMPLIED
>

 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

It can be seen that:

  1. Any number of property child elements can appear in the cache;
  2. cache has some optional attributes such as type, occurrence, flush interval, size, readonly, blocking

2.3.1 type

Type is used to specify the implementation type of the cache. The default is personal, which corresponds to the cache implementation class org. Of mybatis itself apache. ibatis. cache. impl. PerpetualCache.

Later, if we want to implement our own cache or use a third-party cache, we need to change here.

2.3.2 eviction

eviction corresponds to the recycling policy, which defaults to LRU.

  1. LRU: least recently used, remove objects that have not been used for the longest time.

  2. FIFO: first in first out, which removes objects in the order they enter the cache.

  3. SOFT: SOFT reference, which removes objects based on garbage collector status and SOFT reference rules.

  4. WEAK: WEAK reference, removing objects based on garbage collector status and WEAK reference rules.

2.3.3 flushInterval

The flush interval corresponds to the refresh interval, in milliseconds. The default value is not set, that is, there is no refresh interval. The cache is refreshed only when the statement is refreshed.

If it is set, the corresponding time will expire. You need to get the data from the database again.

2.3.4 size

size corresponds to the number of references, that is, the maximum cached object data. The default is 1024.

2.3.5 readOnly

readOnly is a read-only attribute, which is false by default

  1. false: read / write. When creating an object, it will get a copy of the cache object through deserialization. Therefore, the speed will be relatively slow, but the focus is on safety.

  2. true: read only. The read-only cache will return the same instance of the cache object to all callers. Therefore, the performance is very good, but if the object is modified, it may cause problems in the program.

2.3.6 blocking

Blocking is blocking, and the default value is false. When specified as true, BlockingCache will be used for encapsulation.

Using BlockingCache will lock the corresponding Key when querying the cache. If the cache hits, the corresponding lock will be released. Otherwise, the lock will be released after querying the database. This can prevent multiple threads from querying data at the same time in the case of concurrency.

2.4 precautions

  1. Since the cache will be refreshed during update, attention should be paid to the use occasions: when the query frequency is very high, it is used when the update frequency is very low, that is, select is often used, and delete, insert and update are relatively less used.

  2. The cache is in namespace, and the operations in different namespaces do not affect each other. However, refreshing the cache is to refresh the cache of the whole namespace, that is, if you update one, the whole cache will be refreshed.

  3. It is best to use the cache in the namespace of the table with "single table operation only", and all operations on the table are in this namespace. Otherwise, data inconsistency may occur.

Keywords: Mybatis

Added by LuAn on Sun, 19 Dec 2021 06:40:26 +0200