Things management and caching in MyBatis

Posted by Sonic_Rage on Tue, 26 Nov 2019 06:13:06 +0100

Things about MyBatis
The concept of things
In the framework of Java language database, the transaction management of database is very important.
Each business logic is completed by a series of database accesses, which may modify multiple data records. This series of modifications should be a whole, and it is absolutely not allowed to modify only a few data records.
Multiple database atomic access should be bound as a whole, which is the thing. A transaction is a logical execution unit composed of one or several operations. These basic operations are taken as a whole execution unit. They can either be executed in full or cancelled in full, and can never be executed only in part.
A user request corresponds to a business logic method, and a logic method is often logically atomic. In this case, things should be used.
For example, for a transfer operation, the balance of two accounts should be modified. The modification of these two accounts either takes effect at the same time or is cancelled at the same time. If the modification takes effect at the same time, the transfer is successful and the cancellation is transfer failure. However, you cannot modify only one account, which will damage the integrity of the database.
Four characteristics of things
1. Atomicity: things are the smallest executive unit in application, just as atoms are the smallest particles in nature and cannot be subdivided, things are the smallest logical executive body that cannot be subdivided in application.
2. Consistency: the execution result of a thing must change the database from one consistency state to another. When the database contains only the results of the successful submission of things, the database is in a consistent state. When the operation of the system is interrupted, something has not been completed and is forced to be interrupted, and the modification of the unfinished thing to the database has been written to the database, at this time, the database is in an incorrect state. Consistency is guaranteed by atomicity.
3. Isolation: the execution of each thing does not interfere with each other. The internal operation of any one thing is isolated from other concurrent things.
4. Persistence: persistence is also known as persistence. Once something is submitted, any changes made to the data must be recorded in the use memory, usually saved to the physical database.
Transaction interface
For database objects, there should be several actions: create, submit, roll back and close. MyBatis focuses on the org.apache.ibatis.transaction.Transaction interface. The source code of the interface is as follows:

  1 public interface Transaction {
  2     Connection getConnection() throws SQLException;
  3 
  4     void commit() throws SQLException;
  5 
  6     void rollback() throws SQLException;
  7 
  8     void close() throws SQLException;
  9 
 10     Integer getTimeout() throws SQLException;
 11 }
 12 


The Transaction interface has two implementation classes: org.apache.ibatis.transaction.jdbc.JdbcTransaction and org.apache.ibatis.transaction.managed.ManagedTransaction.
So MyBatis has two forms of transaction management:
1. Using JDBC's thing management mechanism, using java.sql.Connection object to complete the operations of submitting, rolling back and closing things.
2. Using the MANAGED thing management mechanism, MyBatis will not realize transaction management by itself, but let containers such as WebLogic and JBOSS realize the management of things.
##Creation and use of things
When using MyBatis, it will be defined in mybatis-config.xml, the configuration file of MyBatis. Here, use the previous( https://www.jianshu.com/p/063a5ca8874c )Configuration information:

  1 <environment id="mysql">
  2     <!--Specify the type of transaction management, which is easy to use here Java Of JDBC Commit and rollback settings for-->
  3     <transactionManager type="JDBC"></transactionManager>
  4     <!--dataSource Refers to the connection source configuration, POOLED yes JDBC The implementation of data source connection pool of connection object-->
  5     <dataSource type="POOLED">
  6         <property name="driver" value="com.mysql.jdbc.Driver"></property>
  7         <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"></property>
  8         <property name="username" value="root"></property>
  9         <property name="password" value="****"></property>
 10     </dataSource>
 11 </environment>

The < environment > element defines the information to connect to the database, and the type of the < transactionmanager > child element determines what type of transaction management mechanism to use.
MyBatis cache
Overview of caching
In the actual project development, the performance requirements of database query are usually very high. MyBatis provides query cache to cache data, so as to improve the query performance.
The query cache of MyBatis is divided into primary cache and secondary cache:
1. The first level cache is the SqlSession level cache.
2. The second level cache is a mapper level cache, which is shared by multiple sqlsessions.

MyBatis reduces data pressure and improves database performance through caching mechanism.
First level cache
The SqlSession object needs to be constructed when operating the database. There is a HashMap user cache data in the object. The cache data regions (hashmaps) between different sqlsessions do not affect each other.
The function of the first level cache is SqlSession scope. When the same sql statement is executed twice in the same SqlSession, the query data in the database will be written to the cache (memory) after the first execution, and the data will be obtained from the cache during the second query, so that the query efficiency will be improved.
Be careful:
If SqlSession performs DML operations (insert, update and delete) and submits to the database, MyBatis will clear the first level cache in SqlSession to ensure that the latest data is stored in the cache and avoid dirty reading.
When a SqlSession ends, the first level cache in the SqlSession does not exist.
MyBatis opens the first level cache by default and does not need to be configured.
L1 cache test
Item code use previous item( https://www.jianshu.com/p/063a5ca8874c)
Now insert several pieces of data into the TB user table of the database:

Then add a query and deletion program to the UserMapper.xml file of the project. The complete program is as follows:

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4 <mapper namespace="com.snow.dcl.mapper.UserMapper">
  5     <!--Insert user data-->
  6     <insert id="saveUser" parameterType="com.snow.dcl.domain.User" useGeneratedKeys="true">
  7         insert into tb_user(name,sex,age) values (#{name},#{sex},#{age});
  8     </insert>
  9     <!--according to id Query users-->
 10     <select id="selectUserById" parameterType="int" resultType="com.snow.dcl.domain.User">
 11         select * from tb_user where id=#{id};
 12     </select>
 13     <!--Query all users-->
 14     <select id="selectAllUser" resultType="com.snow.dcl.domain.User">
 15         select * from tb_user;
 16     </select>
 17     <!--according to id delete user-->
 18     <delete id="deleteUserById" parameterType="int">
 19         delete from tb_user where id=#{id};
 20     </delete>
 21 </mapper>

Right click the Java directory of the project, create the com.snow.dcl.mapper package, and create the UserMapper.java interface class in the package:

Write the following procedure:
  1 public interface UserMapper {
  2     //Query users by id
  3     User selectUserById(Integer id);
  4     //Query all users
  5     List<User> selectAllUser();
  6     //Delete user by id
  7     void deleteUserById(Integer id);
  8 }

Obtain the mybatis-config.xml configuration file, create SqlSessionFactory and SqlSession object according to the configuration file, which are used every time. Therefore, encapsulate them in a class file, right-click the Java directory of the project, create the com.snow.dcl.utils package, and create the FactoryUtil.java class file under the package:

Add the following procedure:
  1 public class FactoryUtil {
  2     private static SqlSessionFactory sqlSessionFactory = null;
  3 
  4     static {
  5         try {
  6             InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
  7             sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  8         } catch (IOException e) {
  9             e.printStackTrace();
 10         }
 11     }
 12 
 13     public static SqlSession getSqlSession(){
 14         return sqlSessionFactory.openSession();
 15     }
 16 }
 17 


Create the OneLevelCacheTest.java test class file in the Java directory under the test directory of the project:

Write the following procedure:

  1 public class OneLevelCacheTest {
  2     public static void main(String[] args) {
  3         OneLevelCacheTest oneLevelCacheTest = new OneLevelCacheTest();
  4         oneLevelCacheTest.cacheOneTest();
  5     }
  6 
  7     public void cacheOneTest(){
  8         SqlSession sqlSession = FactoryUtil.getSqlSession();
  9         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
 10         User user = userMapper.selectUserById(1);
 11         System.out.println(user);
 12         User anotherUser = userMapper.selectUserById(1);
 13         System.out.println(anotherUser);
 14         sqlSession.close();
 15     }
 16 }
 17 


Execute the test program OneLevelCacheTest1.java, and you can see the print result in the console:

You can see that when the User object with id 1 is queried for the first time, a select statement is executed. When the User object with id 1 is queried for the second time, the select statement is not executed. Because the User object with id 1 has been cached in the first level cache, MyBatis directly takes the User object out of the cache and does not go to the database for query again.
DML operation clear cache
Create the OneLevelCacheTest2 test class file in the java directory under the test directory of the project, and write the following code:

  1 public class OneLevelCacheTest2 {
  2     public static void main(String[] args) {
  3         OneLevelCacheTest2 oneLevelCacheTest = new OneLevelCacheTest2();
  4         oneLevelCacheTest.cacheOneTest();
  5     }
  6 
  7     public void cacheOneTest(){
  8         SqlSession sqlSession = FactoryUtil.getSqlSession();
  9         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
 10         User user = userMapper.selectUserById(1);
 11         System.out.println(user);
 12         userMapper.deleteUserById(7);
 13         sqlSession.commit();
 14         User anotherUser = userMapper.selectUserById(1);
 15         System.out.println(anotherUser);
 16         sqlSession.close();
 17     }
 18 }
 19 


Execute the test program OneLevelCacheTest2.java, and you can see the print result in the console:

You can see that when the User object with query id 1 is executed for the first time, a select statement is executed, and then a delete operation is executed. MyBatis empties the first level cache to ensure that the latest data is stored in the cache, so when the User object with query id 1 is executed for the second time, a select statement is executed.
The influence of different Session objects on the first level cache
Create the OneLevelCacheTest3 test class file in the java directory under the test directory of the project, and write the following code:

  1 public class OneLevelCacheTest3 {
  2     public static void main(String[] args) {
  3         OneLevelCacheTest3 oneLevelCacheTest = new OneLevelCacheTest3();
  4         oneLevelCacheTest.cacheOneTest();
  5     }
  6 
  7     public void cacheOneTest(){
  8         SqlSession sqlSession = FactoryUtil.getSqlSession();
  9         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
 10         User user = userMapper.selectUserById(1);
 11         System.out.println(user);
 12         sqlSession.close();
 13         sqlSession = FactoryUtil.getSqlSession();
 14         userMapper = sqlSession.getMapper(UserMapper.class);
 15         User anotherUser = userMapper.selectUserById(1);
 16         System.out.println(anotherUser);
 17         sqlSession.close();
 18     }
 19 }
 20 


Execute the test program OneLevelCacheTest2.java, and you can see the print result in the console:

You can see that when the User object with id 1 is queried for the first time, a select statement is executed, and then sqlSession.close() is called to close the first level cache. When the User object with id 1 is queried for the second time, the first level cache is a new object, and no data is cached in the cache, so the select statement is executed again.
Two level cache
When using the secondary cache, multiple sqlsessions use the same Mapper's sql statement to operate the database, and the resulting data will exist in the secondary cache area, which also uses HashMap for data storage. Compared with the first level cache SqlSession, the scope of the second level cache is larger. Multiple sqlsessions can share the second level cache, which is cross SqlSession.
The second level cache is shared by multiple sqlsessions, and its scope is the same namespace of mapper. Different sqlsessions execute sql statements under the same namespace twice, and the parameters passed to sql are the same. That is to say, if the same sql statement is executed finally, the data queried in the database will be written to the cache after the first execution, and the data will be obtained from the cache during the second query, so the underlying database will not be queried again, so as to improve efficiency.
MyBatis does not enable L2 cache by default. It needs to be configured in the setting global parameter to enable L2 cache.
L2 cache test
Open the secondary cache in the mubatis-config.xml configuration file. The complete configuration file is as follows:

  1 <configuration>
  2     <!-- Appoint Mybatis The implementation of the logs used -->
  3     <settings>
  4         <!--Enable L2 cache-->
  5         <setting name="cacheEnabled" value="true"/>
  6         <!--Open log-->
  7         <setting name="logImpl" value="Log4J"/>
  8     </settings>
  9     <!--Environment configuration, connected database-->
 10     <environments default="mysql">
 11         <environment id="mysql">
 12             <!--Specify the type of transaction management, which is easy to use here Java Of JDBC Commit and rollback settings for-->
 13             <transactionManager type="JDBC"></transactionManager>
 14             <!--dataSource Refers to the connection source configuration, POOLED yes JDBC The implementation of data source connection pool of connection object-->
 15             <dataSource type="POOLED">
 16                 <property name="driver" value="com.mysql.jdbc.Driver"></property>
 17                 <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8"></property>
 18                 <property name="username" value="root"></property>
 19                 <property name="password" value="Password@123"></property>
 20             </dataSource>
 21         </environment>
 22     </environments>
 23     <mappers>
 24         <!--tell Mybatis Mapping file path of persistence class-->
 25         <mapper resource="mapping/UserMapper.xml"></mapper>
 26     </mappers>
 27 </configuration>

cacheEnabled is false by default. If it is set to true, the second level cache is enabled.
Configure the cache parameters in the UserMapper.sml file. The complete configuration file is as follows:
  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4 <mapper namespace="com.snow.dcl.mapper.UserMapper">
  5     <!--Open current mapper Of namespace Second level cache under-->
  6     <cache eviction="LRU" flushInterval="20000" size="512" readOnly="true"/>
  7     <!--Insert user data-->
  8     <insert id="saveUser" parameterType="com.snow.dcl.domain.User" useGeneratedKeys="true">
  9         insert into tb_user(name,sex,age) values (#{name},#{sex},#{age});
 10     </insert>
 11     <!--according to id Query users-->
 12     <select id="selectUserById" parameterType="int" resultType="com.snow.dcl.domain.User">
 13         select * from tb_user where id=#{id};
 14     </select>
 15     <!--Query all users-->
 16     <select id="selectAllUser" resultType="com.snow.dcl.domain.User">
 17        select * from tb_user;
 18     </select>
 19     <!--according to id delete user-->
 20     <delete id="deleteUserById" parameterType="int">
 21         delete from tb_user where id=#{id};
 22     </delete>
 23 </mapper>

Parameter interpretation:
1.eviction: the recovery policy, which is LRU by default, in addition to FIFO, SOFT and WEAK.
LRU: the latest least used policy, removing objects that are not used for the longest time.
FIFO: first in first out policy, which is removed according to the order of objects entering the cache.
SOFT: SOFT reference policy to remove objects based on garbage collector state and SOFT reference rules.
WEAK: a WEAK reference policy that more actively removes objects based on garbage collector state and WEAK reference rules.
2.flushInterval: refresh interval time, any positive integer, in milliseconds. By default, there is no refresh interval. The cache is only refreshed when the statement is called.
3.size: the number of caches, any positive integer. Remember the number of cached objects and the number of memory resources available in the running environment. The default is 1024.
4.readOnly: read only, property is true or false. True cache will return the same instance of the cache object to all callers. The object cannot be modified and has high performance. False caching returns a copy of the cached object (through serialization), which is low performance but safe. The default is false.
Be careful:
When using the secondary cache, the Java objects mapped with query results must implement the serialization and deserialization operations of the java.io.Serializable interface. If there is a parent class, its members must implement the serialization interface. The reason for implementing this interface is to serialize and deserialize the cache data. Because the secondary cache data storage media are various, not necessarily in memory, it may be a hard disk or a remote server.
Create the TwoLevelCacheTest1.java test class file in the Java directory under the test directory of the project, and write the following code:
  1 public class TwoLevelCacheTest1 {
  2     public static void main(String[] args) {
  3         TwoLevelCacheTest1 twoLevelCacheTest = new TwoLevelCacheTest1();
  4         twoLevelCacheTest.cacheTwoTest();
  5     }
  6 
  7     public void cacheTwoTest(){
  8         SqlSession sqlSession = FactoryUtil.getSqlSession();
  9         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
 10         User user = userMapper.selectUserById(1);
 11         System.out.println(user);
 12         sqlSession.close();
 13         sqlSession = FactoryUtil.getSqlSession();
 14         userMapper = sqlSession.getMapper(UserMapper.class);
 15         User anotherUser = userMapper.selectUserById(1);
 16         System.out.println(anotherUser);
 17         sqlSession.close();
 18     }
 19 }
 20 


Execute the test program TwoLevelCacheTest.java, and you can see the print result in the console:

You can see that when the User object with query id 1 is executed for the first time, a select statement is executed, and then sqlSession.close() is called to close the level-1 cache. When the User object with query id 1 is executed for the second time, there is no object in the level-1 cache, but because the level-2 cache is enabled, the data of the first query will be cached in the level-2 cache, so the hit level-2 cache is displayed Data, no need to execute select statement.
Be careful:
Set usecache = false in the select statement of UserMapper.xml file to disable the secondary cache of the current select statement, that is, the database will be queried every time. The default value is true. The configuration content is as follows:

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4 <mapper namespace="com.snow.dcl.mapper.UserMapper">
  5     <!--Open current mapper Of namespace Second level cache under-->
  6     <cache eviction="LRU" flushInterval="20000" size="512" readOnly="true"/>
  7     <!--Insert user data-->
  8     <insert id="saveUser" parameterType="com.snow.dcl.domain.User" useGeneratedKeys="true">
  9         insert into tb_user(name,sex,age) values (#{name},#{sex},#{age});
 10     </insert>
 11     <!--according to id Query users-->
 12     <select id="selectUserById" parameterType="int" resultType="com.snow.dcl.domain.User" useCache="false">
 13         select * from tb_user where id=#{id};
 14     </select>
 15     <!--Query all users-->
 16     <select id="selectAllUser" resultType="com.snow.dcl.domain.User">
 17         select * from tb_user;
 18     </select>
 19     <!--according to id delete user-->
 20     <delete id="deleteUserById" parameterType="int">
 21         delete from tb_user where id=#{id};
 22     </delete>
 23 </mapper>

Topics: Java Mybatis Database JDBC