Hello everyone, I am Cola, a dedicated original, willing to share the program ape.

This series of tutorials will continue to be updated, you can search “IT Cola” on wechat to read the first time. Ebooks has a huge selection of free books that I’ve selected for you

In the last chapter, we explained the lazy loading of Mybatis to improve the query efficiency, so in addition to lazy loading, what other methods can improve the query efficiency? This is what caching is about in this chapter.

Mybatis provides level 1 cache and level 2 cache for us, which can be understood by the following figure:

1. Level 1 cache is SqlSession level cache. The sqlSession object is constructed when operating on the database, and there is a data structure (HashMap) in the object to store cached data. The cache data area (HashMap) between different SQLsessions does not affect each other.

2, level 2 cache is mapper level cache, multiple SQLSessions to operate the same MAPper SQL statement, multiple SQLsessions can share level 2 cache, level 2 cache is cross-SQLSESSION.

1. Level 1 cache

SQL > select * from User; select * from User; select * from User;

@ Test public void testSelectOrderAndUserByOrderId () {/ / session sqlSessionFactory produced SqlSession SqlSession = sessionFactory.openSession(); String statement = "one.to.one.mapper.OrdersMapper.selectOrderAndUserByOrderID"; UserMapper userMapper = sqlSession.getMapper(UserMapper.class); / / the first query, SQL statements, and the results of a query into the cache User u1 = userMapper. SelectUserByUserId (1); System.out.println(u1); / / the second query, because it is the same sqlSession, will be in the cache lookup query results / / if you have, directly from the cache out and interact with the database User u2 = userMapper. SelectUserByUserId (1); System.out.println(u2); sqlSession.close(); }Copy the code

View the console print:

SQL > select * from user where user = ‘user’;

@ Test public void testSelectOrderAndUserByOrderId () {/ / session sqlSessionFactory produced SqlSession SqlSession = sessionFactory.openSession(); String statement = "one.to.one.mapper.OrdersMapper.selectOrderAndUserByOrderID"; UserMapper userMapper = sqlSession.getMapper(UserMapper.class); / / the first query, SQL statements, and the results of a query into the cache User u1 = userMapper. SelectUserByUserId (1); System.out.println(u1); SqlSession.com MIT () u1.setsex (" female "); sqlSession.com MIT () u1.setsex (" female "); userMapper.updateUserByUserId(u1); sqlSession.commit(); / / the second query, because it is the same sqlSession.com MIT (), clears the cache information / / is the query will also send the SQL statement User u2 = userMapper. SelectUserByUserId (1); System.out.println(u2); sqlSession.close(); }Copy the code

View the console print:

(3),

1. Query information about the user whose ID is 1 for the first time. Check whether there is information about the user whose ID is 1 in the cache. Get the user information and store the user information in the level 1 cache.

2. If the sqlSession commit operation (insert, update, delete) is performed, the sqlSession level 1 cache will be cleared. The purpose of this operation is to store the latest information in the cache and avoid dirty reads.

3. On the second attempt to query information about the user whose ID is 1, check whether there is information about the user whose ID is 1 in the cache.

2. Level 2 cache

Level 2 caching works the same way as Level 1 caching. The first query puts the data into the cache, and the second query directly fetches the data from the cache. But level cache is based on the sqlSession, while the second level cache is based on the namespace mapper files, which means more sqlSession can share a level 2 cache region in the mapper, and if the two same namespace mapper, Even if there are two Mappers, the data from the two Mappers will be stored in the same level 2 cache area.

So how does level 2 cache work?

1. Enable level 2 cache

Unlike level 1 cache, which is enabled by default, level 2 cache needs to be enabled manually

First add the following code to the global configuration file mybatis-configuration. XML:

<! < Settings > <setting name="cacheEnabled" value="true"/> </ Settings >Copy the code

Second, enable caching in the usermapper.xml file

<! <cache></cache>Copy the code

The mapper. XML file has an empty label, but it is possible to configure PerpetualCache, which is myBatis’ default caching class. We do not write type to use mybatis default Cache, also can implement Cache interface custom Cache.

We can see that the underlying level 2 cache is still a HashMap architecture.

② Class Po implements Serializable interface

Opened up after the second level cache, also need to be cached pojo implement the Serializable interface, in order to remove the cache data to perform deserialization operation, because the second level cache data storage medium is varied, not only exist in memory, hard drives, possible if we want to take this cache, then you need deserialization. So poJOs in Mybatis implement Serializable interface.

(3), test,

Test level 2 cache is independent of sqlSession

@test public void testTwoCache(){// Generate session according to sqlSessionFactory. SqlSession sqlSession1 = sessionFactory.openSession(); SqlSession sqlSession2 = sessionFactory.openSession(); String statement = "com.ys.twocache.UserMapper.selectUserByUserId"; UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); / / the first query, SQL statements, and the results of a query into the cache User u1 = userMapper1. SelectUserByUserId (1); System.out.println(u1); sqlSession1.close(); / / after the first query closed sqlSession / / the second query, even if sqlSession1 has shut down, the query is still not a SQL statement User u2 = userMapper2. SelectUserByUserId (1); System.out.println(u2); sqlSession2.close(); }Copy the code

The SQL session is closed and the SQL query is still not issued.

Perform commit() to clear level 2 cache data

@test public void testTwoCache(){// Generate session according to sqlSessionFactory. SqlSession sqlSession1 = sessionFactory.openSession(); SqlSession sqlSession2 = sessionFactory.openSession(); SqlSession sqlSession3 = sessionFactory.openSession(); String statement = "com.ys.twocache.UserMapper.selectUserByUserId"; UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class); UserMapper userMapper3 = sqlSession2.getMapper(UserMapper.class); / / the first query, SQL statements, and the results of a query into the cache User u1 = userMapper1. SelectUserByUserId (1); System.out.println(u1); sqlSession1.close(); Commit () u1.setusername ("aaa"); // Close sqlSession after the first query. userMapper3.updateUserByUserId(u1); sqlSession3.commit(); / / the second query, since the last update operation, the cache data has been cleared (to prevent dirty read data), the User u2 = userMapper2 here must once again issued a SQL statement. The selectUserByUserId (1); System.out.println(u2); sqlSession2.close(); }Copy the code

View the console:

UseCache and flushCache

In mybatis, you can also configure userCache and flushCache. UserCache is used to disable level-2 caching. UseCache =false is used to disable level-2 caching in the current select statement. That is, SQL is issued for each query. The default value is true, which means that the SQL uses level 2 caching.

<select id="selectUserByUserId" useCache="false" resultType="com.ys.twocache.User" parameterType="int">
	select * from user where id=#{id}
</select>
Copy the code

Set useCache=false, disable level 2 caching, and fetch data directly from the database.

In the same namespace of the Mapper, if the cache needs to be refreshed after other INSERT, update, or DELETE operations, dirty reads will occur if the cache is not refreshed.

Set the flushCache= “true” attribute in the statement configuration. The default value is true, that is, the cache is flushed. If the value is false, the cache is not flushed. If you manually modify the query data in a database table while using caching, dirty reads will occur.

<select id="selectUserByUserId" flushCache="true" useCache="false" resultType="com.ys.twocache.User" parameterType="int">
	select * from user where id=#{id}
</select>
Copy the code

In general, the cache is flushed after the commit operation. FlushCache =true flusher the cache to avoid dirty database reads. So we don’t have to set it, just default.

Level 2 cache integrated ehcache

Above we introduced mybatis own level 2 cache, but this cache is a single server work, can not achieve distributed cache. So what is distributed caching? If a user accesses server 1, the cache will be stored on server 1. If a user accesses server 2, the cache will not be available on server 2, as shown in the following figure:

To solve this problem, we need to find a distributed cache, which is specially used to store cached data, so that different servers can store the cached data there, and also fetch the cached data from it, as shown in the following figure:

As shown in the figure above, we use a third-party caching framework between several different servers. We put all the caches in this third-party framework, and then we can fetch data from the caches no matter how many servers there are.

Here we introduce the integration of Mybatis with third-party framework EhCache.

As mentioned above, Mybatis provides a cache interface. If you want to implement your own cache logic, you can implement the cache interface development. Mybatis implements one by default, but the implementation of this cache cannot implement distributed caching, so we will implement it ourselves. Mybatis provides an ehcache implementation class for the cache interface. This class is included in the integrated package of Mybatis and EhCache.

Import mybatis- EhCache integration package (included in the top source code)

Enable caching in the global configuration file mybatis-configuration. XML

<! < Settings > <setting name="cacheEnabled" value="true"/> </ Settings >Copy the code

(3) Compile ehCache in xxxmapper. XML file

Write the full class name of the following class to the type attribute

<! Mybatis defaults to PerpetualCache and needs to be connected to EhCache with PerpetualCache. Need to configure the ehcache type so as to realize the cache the type of the interface - > < cache type = "org. Mybatis. Caches. Ehcache. EhcacheCache" > < / cache >Copy the code

4. Set cache parameters

Create a new ehcache. XML file in the classpath directory and add the following configuration:

<? The XML version = "1.0" encoding = "utf-8"? > <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation=".. /config/ehcache.xsd"> <diskStore path="F:\develop\ehcache"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </defaultCache> </ehcache>Copy the code
DiskStore: Specifies the storage location of data on a disk. DefaultCache: When cachemanager.add ("demoCache") is used to create a Cache, EhCache uses the management policy specified in <defalutCache/>. The following attributes are required: MaxElementsInMemory - Maximum number of elements cached in memory maxElementsOnDisk - Maximum number of elements cached on disk, If 0 indicates infinite eternal - Sets whether cached elements never expire. If true, then the cached data is always valid, if false then also according to timeToIdleSeconds, TimeToLiveSeconds Judge overflowToDisk - Sets whether to cache expired elements onto disk when the memory cache overflows. The following attributes are optional: TimeToIdleSeconds - Data cached in EhCache will be deleted when two consecutive access times exceed the timeToIdleSeconds property value, The default is 0, which is infinite idle time. TimeToLiveSeconds - Cache element's valid lifetime, DiskSpoolBufferSizeMB This parameter sets the size of the DiskStore cache. The default value is 30MB. Each Cache should have its own buffer. DiskExpiryThreadIntervalSeconds - disk cache cleaning thread running interval, default is 120 seconds. Each of the 120 s, the corresponding thread will conduct a data cleaning work in EhCache memoryStoreEvictionPolicy - reached the maximum when the memory cache, there is a new element to join, to remove the cache element of strategy. The default is LRU (least recently used), and the options are LFU (least often used) and FIFO (FIFO)Copy the code

4. Application scenarios of level 2 cache

For the query requests with many visits and users’ low real-time requirements for the query results, MyBatis level 2 caching technology can be used to reduce the database access and improve the access speed. Business scenarios include: time-consuming statistical analysis SQL, telephone bill query SQL, etc. The implementation method is as follows: by setting the flushInterval, mybatis will automatically flush the cache at intervals and set the cache flushInterval to flushInterval according to the data change frequency, for example, 30 minutes, 60 minutes, 24 hours, etc., as required.

Mybatis level 2 cache does not implement fine grained data level cache well, such as the following requirements: To cache commodity information, because the volume of commodity information query is large, but users are required to query the latest commodity information every time, at this time, if the second-level cache of Mybatis is used, it is not possible to refresh the cache information of a commodity without refreshing the information of other commodities when a commodity changes. Because the secondary cache area of MyBaits is divided by Mapper, when a commodity information changes, all the cache data of commodity information will be emptied. Addressing such issues may require targeted caching of data at the business layer based on demand.

This series of tutorials will continue to be updated, you can search “IT Cola” on wechat to read the first time. Reply ebook has a selection of books that I have selected for you