The importance of caching is self-evident. With caching, we can avoid frequent interactions with the database, especially in the case of more queries and higher cache hit ratio, the performance improvement with caching is more obvious

Similarly, Mybatis, as an ORM framework, will definitely support caching

It supports level 1 and level 2 caching, respectively. The level 1 cache is the sqlSession level cache, and the level 2 cache can realize the cache between multiple SQLSessions

What do you mean? Read on

First-level cache

01. What is Level 1 cache

Mybatis level 1 cache is sqlSession level, because it only supports the cache under the same sqlSession, that is, the cache is only shared between the same sqlSession

02. How to implement level 1 cache

Level 1 caches are divided into two categories: Statement and session

Statement: An SQL statement

Session: a connection generated by the database, that is, a SQLsession

Mybatis supports level 1 caching by default, no special configuration is required, and it supports session range level 1 caching.

In view of the cached attributes, mybatis org. Using the class apache. Ibatis. SessionConfiuration configuration, we can see the default level for the SESSION of localCacheScope (and the second level cache is also the default open)

Note \color{#FF0000}{note} Note: The cacheEnabled attribute in the Configuration class is for on-off control of level 2 cache, not level 1 cache. Level 1 cache does not need to be configured at all. It has no switch and is supported by Mybatis by default

So, in other words, I run the service directly, and level 1 caching takes effect?

Let’s try the level 1 cache test

03. Level 1 cache test

1) Call the same method twice in the same method

public void testCache(){
    User user = userMapperWithAnnotation.findById(33L);
    log.info("Find User: {}", user);
    User user2 = userMapperWithAnnotation.findById(33L);
    log.info("Find User2: {}", user2);
}
Copy the code

2) Call directly from the run method

@SpringBootApplication
@Slf4j
@MapperScan("com.shumile.springbootmybatis.mapper")
public class SpringBootMybatisApplication

implements ApplicationRunner {


    @Autowired
    private UserMapperWithAnnotation userMapperWithAnnotation;
    
    public static void main(String[] args) {
        SpringApplication.run(SpringBootMybatisApplication.class, args);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
          testCache();
    }
Copy the code

Since mybatis already supports level 1 caching by default, I’m sure I only need to query the database once and then fetch the result directly from the cache the second time

3) Run the code, as shown below

Huh? What’s going on? A lie? Print two SQL statements, still query twice?

To be honest, this question has puzzled me for several hours, I thought, is there a problem with what the Internet says? Is there any special configuration for level 1 caching?

Finally, through the source tracking, finally suddenly enlightened ~

04. Source code analysis

We trace the execution of the findById() method as follows

1) The selectOne() method of DefaultSqlSession is first entered

2) Next, the selectList() method is entered

We know that the Confifuration class already supports level 1 caching by default. When executing a query, we get the corresponding configuration object that supports session caching by default (again, regardless of the cacheEnabled attribute).

So, there’s no problem here

3) Go down to the Query () method of CachingExecutor

After obtaining the corresponding executable SQL statement based on the parameters, the createCacheKey() method creates the cache key and passes the obtained key as a parameter to the following query() method

Here, we pause and go inside the createCacheKey() method to see how this Key is determined

4) Explore the implementation of createCacheKey()

It places statement. id, Offset, Limmit, Sql, and Params into the updateList in the cacheKey by executing the cacheKey.update() method, respectively. The update() method is as follows

Look again at the cachekey.equals () method

Obviously, except for hashcode, checksum, and count comparisons, we can consider CacheKey equality as long as the updatelist elements are equal. Id, Offset, Limmit, Sql, and Params are the same as each other

Look at the key assembly process, then continue to go ~

5) Find the key in the cache

In the query() method, the key value is first fetched from the cache. If the list is not in the cache, the obtained list is empty, and the database needs to be queried

6) Execute specific database query methods

7) Store the results in cache

After execution, the result of the current execution is placed in the localCache via localcache.putobject (key,list)

8) Submit results

Once this is done, the commit() method is entered

We see that the local cache is cleared when the commit() method is executed. In the future, the cache always cannot find the corresponding key value, and the SQL statement will be executed again every time to query the database

So it’s easy to see why level 1 caching is not supported

Commit () {color{#FF0000}{commit() {color{#FF0000}{commit() {color{#FF0000}{commit() {color{#FF0000}{ Our methods must be in the same transaction to support level 1 caching

Let’s verify that the transaction is started

1) Start the transaction

Next, we have method to open the transaction, in starting the class add @ EnableTransactionManagement annotations, and on the run () method to add @ Transactional annotation

Then it’s time for validation

2) Verification

The execution process of the first query is basically the same as the previous one. Again, the results are retrieved from the database and stored in the cache

Second Query

Notice, the key is the second query

We will continue to judge the value of the corresponding key from the cache. This time we can get the value of the key, namely its query result, and directly return the result set

Until the two statements have been executed, the commit() method is entered for the transaction commit operation

Take a look at the execution result graph

If the SQL statement is executed only once, the verification is successful ~


summary \ color # FF0000} {{subtotal}

Why does level-1 cache not take effect when transactions are not enabled? Enabled transaction, level 1 cache takes effect?


Apparently, that’s because it executes the commit method after each statement, and the commit method clears the local cache each time. With transactions enabled, methods are committed only after all operations are completed, so level 1 caching is supported Color {#FF0000}{apparently, that’s because it executes the commit method at the end of each statement, and the commit method clears the local cache each time. With transactions enabled, methods are committed only after all operations are completed, so level 1 caching is supported.

Second level cache

01. What is level 2 cache

In level 1 cache, a sqlSession uses a cache, while Mybatis level 2 cache supports multiple SQLsessions sharing cache. Mybatis enables level 2 caching by default

02. How to level 2 cache

From the source analysis of level 1 caching in section 1, we also mentioned that level 2 caching is enabled by default in the Configuration class (cacheEnabled=true).

In addition to this parameter, you need to add a cache or cache-ref tag to the mapper. XML file for cache configuration

As shown below.

The cache tag is used to declare that a namespace uses level 2 caching and can be customized using the following attributes

  • Type: Indicates the type of the cache. The default value is PerpetualCache

  • Eviction: A design designed to define recycling strategies, FIFO, LRU, etc

  • FlushInterval: flushes the cache automatically at certain intervals

  • Size: indicates the maximum number of cached objects

  • ReadOnly: Indicates whether it is read-only. If it is configured as read-write, the corresponding entity class must be serialized

  • Blocking: Configures whether the blocking is enabled when the corresponding key cannot be found in the cache until the corresponding data enters the cache

  • Cache-ref indicates a cache configuration that references another namespace. Operations on both namespaces use the same cache

To implement a shared cache between two namespaces, we can import the namespace attribute of the cache-ref tag into another namespace, for example:

<cache-ref namespace="mapper.OrderMapper"/>
Copy the code

— Label instructions refer to:

Tech.meituan.com/2018/01/19/…

03. Level 2 cache test

To verify the level 2 cache of Mybatis, you need to construct multiple operations of different SQLsessions to verify that cache sharing can be realized between these SQLsessions

1) Create the mybatis-configuration. XML file

<? The XML version = "1.0" encoding = "utf-8"? > <! DOCTYPE configuration PUBLIC "- / / mybatis.org//DTD Config / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" > <configuration> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> <! Setting name="cacheEnabled" value="true" /> <! -- <setting name="localCacheScope" value="false"/>--> </settings> <! <environment default="mysql"> <environment id="mysql"> <! <transactionManager type="JDBC"></transactionManager> <! --dataSource indicates the connection source configuration, POOLED --> <dataSource type="POOLED"> <property name="driver" Value = "com. Mysql. JDBC Driver" > < / property > < property name = "url" value = "JDBC: mysql: / / 127.0.0.1:3306 / test" > < / property > <property name="username" value="root"></property> <property name="password" value="123456"></property> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"></mapper> </mappers> </configuration>Copy the code

2) We use this configuration file to define two SQLSessions for the same query operation

The result of calling the method is as follows

We see that the cache is not shared, that is, the secondary cache is invalidated

3) The second test

We perform the commit of sqlSession1 before executing the second SQLSession operation

Execute the code as shown below

We see that level 2 caching has been successfully implemented

Why is that?

This is because only if the first sqlSession performs a commit can the second sqlSession sense it and then fetch the cache

So, if our data changes, it certainly can’t fetch the data from the cache, otherwise the data we see on the page won’t be up to date

Mybatis has also taken this into account for us. Let’s see what happens if we update the table in the middle of the operation

4) The third test

As shown below, in between the two query operations, we execute the update() method of another sqlSession

The command output is as follows

On the last query operation, the SQL statement was also executed, so in this case, the second-level cache is invalidated

In addition, if multiple table operations are performed during execution, that is, if table A is associated with table B, if an update operation is performed on table A, table B will not be aware of it and will get dirty data, affecting normal business logic

Level 2 cache implementation principle, we can refer to level 1 cache tracking source code, their own code to further explore, there are any questions can also communicate with me at any time oh ~


summary \ color # FF0000} {{subtotal}

Mybatis uses cacheEnabled to enable and disable level 2 caching, which is enabled by default

To use mapper. XML, you also need to enable the cache within the namespace through the cache tag. Of course, you can also add other namespaces through cache-ref to share the level 2 cache

Mybatis has limitations for multi-table queries, and it is easy to read dirty data. A third-party cache implementation is recommended

To sum up

Color {#FF0000}{level 1 cache} level 2 cache \color{#FF0000}{level 2 cache} level 2 cache

Mybatis supports level 1 cache by default. It does not need to be set on or off, but it will only take effect when the transaction is started. The specific reason is understood by tracking the execution process of the query statement

And the level 2 cache, must be in front of the sqlSession after the transaction, can support, and the use of limitations, each sqlSession execution, must be submitted operation, other sqlSession wipe can sense the change, for multiple table operation easy to get dirty data and other defects.

For example, redis is used for data storage. For distributed scenarios, we can also ensure that the cache does not fail and dirty data will not be read, so as to ensure business consistency

Skills Summary

1. Implementation method and principle of Mybatis level 1 cache

2. Implementation method and invalidation scenario of MyBatis level 2 cache

3. Summary and suggestions on the use of two-level cache

Github address of the code: github.com/helemile/he…


Warm prompt \color{#FF0000}{

If there are some non-cognitive code logic problems in the project, in addition to searching around, you might as well try to track the source code, step by step analysis to solve. This will not only help you understand how it works, but also improve your problem-solving skills. Try it. Trust me, the code will smell good after a while

If you find the article helpful at all, give it a thumbs up

Well, that’s it. Learn a little every day, and time will show you how strong you are.

Next period:

Spring Boot(8) : Monitoring feature of Spring Boot

This project code has been uploaded to Github ~ if necessary, please refer to github.com/wangjie0919…


Highlights from the past \color{#FF0000}{

Docker chapter

How do containers communicate with each other?

Docker chapter 3: Dockerfile combat open

Docker (ii) : Docker combat, command parsing

.

SpringCloud chapter

Spring Cloud (13) : How powerful is Feign?

Spring Cloud (10) : Information Center -Kafka Classic interview questions, do you know?

Spring Cloud (9) : Registry Selection – A complete summary of four registry features

Spring Cloud (iv) : Internal debate between Eureka and ZooKeeper

.

Spring Boot chapter

Spring Boot (7) : You can not not know Mybatis caching mechanism!

Spring Boot (6) : Those nice database connection pools

Spring Boot (4) : People love to hate JPA

SpringBoot (1) : Features overview

.

translation

How to Become a Google Development Expert (GDE) – A practical Guide

Use CSS to speed up page rendering

Will WebTransport replace WebRTC in the near future?

.

Occupation, life perception

Have you ever wondered, what is the meaning of travel?

Career planning for programmers

Ten Moments that Make programmers Crash! The sixth one was unbearable!

Talk about changing jobs