This is the 12th day of my participation in the August More Text Challenge. For details, see: August More Text Challenge

The cache

What is caching

Temporary data that exists in memory

Why cache

Reduce the number of interactions with the database and improve the execution efficiency

  • Data used for caching:
  1. Data that is frequently queried and does not change often

  2. The correctness of the data has little effect on the final result

  • Data not available for caching:
  1. Constantly changing data

  2. The correctness of the data has a great impact on the final result, such as the inventory of goods, the exchange rate of the bank, etc

MyBatis caching mechanism

MyBatis provides level-1 cache and level-2 cache to save the data results frequently queried by users to level-1 cache or level-2 cache, so that users do not need to obtain from DB in the following queries, and return directly from the cache, thus reducing the interaction with DB and improving the response speed of the system.

Level 1 cache

🚦 During a database session, if we execute multiple SQL query operations with the same query conditions, that is, the data to be queried is the same, then will MyBatis fetch data from the database and return it to us each time or will it use the cache of MyBatis?

  • The answer is to use caching. MyBatis will enable level 1 cache by default. The level 1 cache refers to the SqlSession object cache in MyBatis. Each time the query will go to the first-level cache query, query from the cache, or back to DB queries, from DB query result at the same time to the SqlSession provides us with an area of structure is a Map of the area, so when we query the same data again, can be obtained from the cache and, Avoid directly querying the database to improve performance. Level 1 cache is SqlSession level, so when a database session ends and SqlSession object disappears, Mybatis level 1 cache will be invalid.

🚦 Then one may ask, if the same data is retrieved from the cache during a database session, when the corresponding data is modified, we cannot get the latest data.

  • It doesn’t, because MyBatis also provides a mechanism to trigger clearing of the level-1 cache when SqlSession is calledAdd, delete, commit(), close()And so on, it clears the level 1 cache, which is invalid at this point, and it goes to the database to get the latest data. 🍿 For example, if the user whose ID is 1 is queried for the first time, the system searches the level-1 cache for the user whose ID is 1. If the user does not exist, the system queries the user information from the database and stores the user information in the level-1 cache. The second time to query for the user whose ID is 1, check whether the information of the user whose ID is 1 exists in the cache. If yes, directly obtain the user information from the cache. If sqlSession performs an insert, update, delete, and then calls COMMIT (), the level 1 cache in sqlSession will be cleared for this purposeTo keep the cache up-to-date, avoid dirty reads. There is also another way to invalidate the level 1 cache by adding an attribute to the SELECT tag in the Mapper mapping fileFlushCache and set the value to trueWhen executing the query, all the data in the level-1 cache will be cleared first and then the data will be fetched from the DB.

Use of level 1 cache

MyBatis turns on the level 1 cache by default, so let’s test the level 1 cache.

  • Default Mybatis can not print SQL logs, so it is not easy to view debugging, so we first configure Mybatis and log4j to print SQL.
  1. Create one in the classpath(that is, in the resource directory)log4j.propertiesFile with the following contents
Log4j rootLogger = ERROR, R # configuration related configure log4j printed to the console. The appender. A1 = org.. Apache log4j. ConsoleAppender log4j.appender.A1.Target=System.out log4j.appender.A1.layout=org.apache.log4j.PatternLayout Log4j. Appender. A1. Layout. ConversionPattern = % d {MM - dd yyyy - HH: MM: ss, SSS} % - 5 p % t (% 10 c {1}) - # % m % n configuration print to file the relevant configuration Log4j. Appender. R = org, apache log4j. RollingFileAppender # to print the file name and location of log4j. Appender. R.F ile =). /logs/ cmanager.log . Each log file size log4j appenders. R.M axFileSize = 30 MB # log file number log4j. Keep appender, R.M axBackupIndex = 200 # set in the form of additional printing Log4j. Appender. State Richard armitage ppend = true # log level log4j. Appender. R.T hreshold = DEBUG Log4j. Appender. R.l ayout = org.. Apache log4j.. # PatternLayout log format log4j appenders. R.l ayout. ConversionPattern = % d {yyyy - MM - dd HH: mm: ss, SSS} % 5 p % c {1} : % L - % m % n # scope log4j.logger.com.asiainfo=TRACE log4j.logger.org.exam=INFO log4j.logger.org.springframework.beans.factory=INFOCopy the code
  1. Enable printing logs to SLF4J in the main configuration file
<settings> <! -- log to slf4J --> <setting name="logImpl" value="STDOUT_LOGGING"/> </ Settings >Copy the code

Level 1 Cache test

  • Although level 1 cache is enabled for MyBatis by default, we can still configure level 1 cache for MyBatis by setting tag in the main configuration file. You can configure the level 1 cache, includingSESSION,STATEMENT.Defaults to the SESSIONLevel, let’s test:
  1. First, set the level of level 1 cache to SESSION, that is, SESSION level
<setting name="localCacheScope" value="SESSION"/>
Copy the code
  1. First, we perform two consecutive searches for user information with ID 42 in a database session, which isSESSIONLevel, the code is as follows:
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(is); SqlSession session = factory.openSession(true); UserDao userDao = session.getMapper(UserDao.class); User = userDao.getUserById(42); System.out.println(user); User user2 = userDao.getUserById(42); System.out.println(user2); session.close(); is.close();Copy the code

Print SQL log:As you can see from the SQL log above, only the first query is looked up from the database, and the second query is fetched directly from the cache.

  1. Next we test to change the level 1 cache toSTATEMENTThe effect of:
<setting name="localCacheScope" value="STATEMENT"/>
Copy the code

Execute the same query, and the SQL log print is as follows:

As you can see from the above results, both queries were retrieved from the database. Because when we change the level 1 cache toSTATEMENTLater, even though the two identical queries belong to the same database session (i.e., the same SqlSession), the two queries correspond to two different statements, so the second query will not be searched in the cache.

  1. We then test whether the level 1 cache is invalidated if changes, deletes, inserts, and so on are made to the database during a database session. Let’s add an insert operation to the same two queries.
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(is); SqlSession session = factory.openSession(true); UserDao userDao = session.getMapper(UserDao.class); User = userDao.getUserById(42); // insertUser userdao.insertuser (new User(" no tea Programmer",new Date()," male ","China")); User user2 = userDao.getUserById(42); session.close(); is.close();Copy the code

SQL execution logs are as follows:

From the above logs, we can see that if the database is modified, deleted, or inserted during a database session, the level-1 cache is invalid, and the second query goes to the database to obtain data.

The second level cache

  • The level 1 cache has limitations. The level 1 cache can be used only when the same query is executed in the same SqlSession. If you want to use the cache between different SQL sessions to speed up the query, you need to use the level 2 cache.
  • Mybatis level 2 cache refers to the SqlSessionFactory object cache. SqlSession objects created by the same SqlSessionFactory object share its cache. The level 2 cache is the mapper mapping level cache. Multiple SQLSessions can operate on the same MAPper mapped SQL statement. Multiple SQLsessions can share the level 2 cache, and the level 2 cache is cross-sqlsession.

The use of level 2 cache

Mybatis cache stores data, not objects, when the second query, from the cache data, and fill the newly created object, and then return the object.

(1) enable Mybatis to support level 2 cache (in the main configuration file sqlmapconfig.xml)

<! -- Add the following configuration in the < Configuration > TAB of the sqlmapConfig. XML main configuration file --> <! < Settings > <! -- Default is true--> <setting name="cacheEnabled" value="true"/> </ Settings >Copy the code

(2) Make the current mapping file support level 2 caching (configured in the userdao.xml configuration file)

<! -> <cache/>Copy the code

(3) Make the current operation support level 2 caching (configured in the select TAB)

<! -- Add the [useCache="true"] attribute to the query --> <! <select id="findById" parameterType="java.lang.Integer" resultType="user" useCache="true"> Select * from  user where id=#{uid}; </select> Set useCache= "true" in the <select> tag in the userdao. XML mapping file to indicate that the statement is using level 2 cache. If level 2 cache is not used, set this to false. Note: The latest data SQL is required for each query. Set useCache=false and disable level 2 caching.Copy the code

Level 2 cache test

  1. SqlSession1, sqlSession2, sqlSession1, sqlSession2, sqlSession1, sqlSession2
A little... SqlSession sqlSession1 = factory.openSession(true); SqlSession sqlSession2 = factory.openSession(true); UserDao userDao1 = sqlSession1.getMapper(UserDao.class); UserDao userDao2 = sqlSession2.getMapper(UserDao.class); // System.out.println("sqlSession1: first query -->"+ userdao1.getUserById (42)); Println ("sqlSession2: new query -->"+userDao2.getUserById(42)); A little...Copy the code

If sqlSession1 does not commit, sqlSession2 does not execute the same query a second time. (Avoid dirty reads)

  1. On the basis of the above example, after the first query in sqlSession1, commit the transaction, and then test the second query in sqlSession2 to see if the same query is retrieved from the level 2 cache.
A little... SqlSession sqlSession1 = factory.openSession(true); SqlSession sqlSession2 = factory.openSession(true); UserDao userDao1 = sqlSession1.getMapper(UserDao.class); UserDao userDao2 = sqlSession2.getMapper(UserDao.class); // System.out.println("sqlSession1: first query -->"+ userdao1.getUserById (42)); SqlSession1.com MIT (); Println ("sqlSession2: new query -->"+userDao2.getUserById(42)); A little...Copy the code

Execution log:

After sqlSession1 is committed, sqlSession2 executes the same query a second time and successfully obtains data from the level 2 cache. Therefore, we can see that when Sqlsession1 is not committed, the level 2 cache does not take effect.

  1. Finally, let’s test whether the level 2 cache is invalid when we update the database between two queries.
SqlSession sqlSession1 = factory.openSession(true); SqlSession sqlSession2 = factory.openSession(true); SqlSession sqlSession3 = factory.openSession(true); UserDao userDao1 = sqLsession1.getMapper (userDao.class); //4, create Dao dao1 = sqLsession1.getMapper (userDao.class); UserDao userDao2 = sqlSession2.getMapper(UserDao.class); UserDao userDao3 = sqlSession3.getMapper(UserDao.class); // System.out.println("sqlSession1: first query -->"+ userdao1.getUserById (42)); SqlSession1.com MIT (); Println (" sqlSession2: new query -->"+ usersession dao2.getUserByID (42)); // Update userdao3. updateById(new User(42," UserDao3. updateById ")); SqlSession3.com MIT (); sqlSession3.com MIT (); Println (" + useruserById = userdao2.getUserByID (42)); //sqlSession2 = system.out.println (" + useression3 = updatedao2.getUserByID (42));Copy the code

Execution log:

As you can see from the above log, the level 2 cache is invalid when the database is committed after the update during the two queries.

conclusion

After the above understanding, we understand the characteristics of MyBatis level1 cache and level2 cache. MyBatis level 1 cache is a session sqlSession level, cache data can be shared in the same sqlSession, and level 2 cache is namespace level or mapper level, that is, multiple SQLSessions to operate on the same SQL statement in mapper. Multiple SQLSessions can share the level 2 cache. Of course, whether the cache of MyBatis is enabled or not should be combined with our business situation. In the case of multi-table associated query, dirty read is very easy to occur in the secondary cache of MyBatis. Therefore, we should make reasonable use of the cache mechanism of MyBatis according to the business situation.

🏁 This is the introduction of MyBatis caching mechanism. If there are any errors, please leave a comment to correct them. If you think this article is helpful to you, please click 👍 😋😻😍