MyBatis - Caching

Keywords: Mybatis xml SQL Database

Articles Catalogue

What is Mybatis cache?

The use of caching can reduce the number of interactions between Java Application and database, thereby improving the efficiency of the program. For example, when a user object with id=1 is queried, it is automatically saved into the cache after the first query. The next time the object is queried, it can be retrieved directly from the cache without sending an SQL query to the database.

Mybatis Cache Classification

Level 1 cache: SqlSession level, open by default, and cannot be closed.

The first level cache of mybatis is the SqlSession level cache. SqlSession objects need to be constructed when operating the database. A HashMap is used to store the cached data among the objects. The cache data area (HashMap) between different SqlSessions does not affect each other.

The scope of the first level cache is SqlSession. When the same sql statement is executed twice in the same SqlSession, the data queried in the database will be written to the cache (memory) after the first execution. When the second query is executed, the data will be retrieved from the cache, and the database query will not be carried out at the bottom, thus improving the performance. Query efficiency. It should be noted that if SqlSession performs DML operations (insert, update, delete) and commit () operations, mybatis will empty the primary cache in SqlSession. The purpose of this is to ensure that the latest information stored in the cache data is kept 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 without any configuration.

Level 2 cache: Mapper level, closed by default, can be turned on.

Secondary cache is a Mapper-level cache. When using secondary cache, multiple SqlSessions use sql statements of the same Mapper to operate the database. The resulting data will have a secondary cache area. It also uses HashMap for data storage. Compared with the first-level cache SqlSession, the second-level cache has a larger scope and more SqlSessi. On can share a secondary cache, which is across SqlSession.

Secondary cache is shared by multiple SqlSessions. Its domain 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, when the same sql statement is executed eventually, the data queried in the database will be written to the cache after the first execution. (memory), the second query will get data from the cache, no longer go to the underlying database query, thereby improving query efficiency.

Mybatis closes the secondary cache by default, and can configure to open the secondary cache in setting s global parameters.

Next, we'll learn how to use MyBatis caching through code.

First of all, we will demonstrate the first-level caching, taking querying Student objects as an example.

/**
 * @ClassName Student
 * @Description
 * @Author lzq
 * @Date 2019/7/26 13:53
 * @Version 1.0
 **/
public class Student {
    private int SID;
    private String Sname;
    private String Ssex;
    private int Age;

    public int getSID() {
        return SID;
    }

    public void setSID(int SID) {
        this.SID = SID;
    }

    public String getSname() {
        return Sname;
    }

    public void setSname(String sname) {
        Sname = sname;
    }

    public String getSsex() {
        return Ssex;
    }

    public void setSsex(String ssex) {
        Ssex = ssex;
    }

    public int getAge() {
        return Age;
    }

    public void setAge(int age) {
        Age = age;
    }

    @Override
    public String toString() {
        return "[id"+SID+" Name"+Sname+" Gender"+Ssex+" Age"+Age+"]";
    }
}

StudentMapper interface:

import org.apache.ibatis.annotations.*;
public interface StudentMapper {
      public Student getStudentById(int id);
}

mybatis-config.xml:

<?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>
        <!-- Print Query Statement -->
       <setting name="logImpl" value="STDOUT_LOGGING"/>
      
    </settings>

    <!--Data source configuration-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="StudentMapper.xml"/>
    </mappers>
</configuration>

StudentMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        
<mapper namespace="tulun.dao.StudentMapper">
    <select id="getStudentById" parameterType="int" resultType="tulun.bean.Student">
        select * from student where SID = #{id}
    </select>
</mapper>

Test code:

**
 * @ClassName Test
 * @Description
 * @Author lzq
 * @Date 2019/7/26 13:53
 * @Version 1.0
 **/
public class Test {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        //Read configuration files
        InputStream asStream = Resources.getResourceAsStream(resource);
        //Create sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);

        //Create sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //Generating StudentMapper Objects through Dynamic Agents
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        //Query tuples with id 1
        Student student = mapper.getStudentById(1);
        System.out.println(student);
        Student student1 = mapper.getStudentById(1);
        System.out.println(student1);
    }
}


As you can see, an SQL statement is executed and two objects are queried. The first object is queried by SQL and stored in the cache. The second object is obtained directly from the cache.

We said that the first level cache is SqlSession level, so once SqlSession is closed, the cache will no longer exist, modify the code, and test again.

Test code:

**
 * @ClassName Test
 * @Description
 * @Author lzq
 * @Date 2019/7/26 13:53
 * @Version 1.0
 **/
public class Test {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        //Read configuration files
        InputStream asStream = Resources.getResourceAsStream(resource);
        //Create sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);

        //Create sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //Generating StudentMapper Objects through Dynamic Agents
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        //Query tuples with id 2
        Student student = mapper.getStudentById(1);
        System.out.println(student);
        sqlSession.close();  //Close the original sqlSession
        sqlSession = sqlSessionFactory.openSession();  //Create a new one
        mapper = sqlSession.getMapper(StudentMapper.class);
        Student student1 = mapper.getStudentById(1);
        System.out.println(student1);
    }
}


As you can see, the SQL was executed twice.

When SqlSession is closed and the first level cache is invalid, the second level cache can be enabled to meet the requirement of improving efficiency.

MyBatis can use its own secondary cache or third-party ehcache secondary cache.

Self-contained secondary cache

mybatis-config.xml configuration opens secondary cache

<?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>
        <!-- Print Query Statement -->
       <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!-- Open Level 2 Cache -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <!--Data source configuration-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="StudentMapper.xml"/>
    </mappers>
</configuration>

Open the secondary cache in the StudentMapper.xml configuration:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        
<mapper namespace="tulun.dao.StudentMapper">
    <!-- Open Level 2 Cache -->
    <cache></cache>
    
    <select id="getStudentById" parameterType="int" resultType="tulun.bean.Student">
        select * from student where SID = #{id}
    </select>
</mapper>

Student class implements Serializable interface:

import java.io.Serializable;

/**
 * @ClassName Student
 * @Description
 * @Author lzq
 * @Date 2019/7/26 13:53
 * @Version 1.0
 **/
public class Student implements Serializable{
    private int SID;
    private String Sname;
    private String Ssex;
    private int Age;

    public int getSID() {
        return SID;
    }

    public void setSID(int SID) {
        this.SID = SID;
    }

    public String getSname() {
        return Sname;
    }

    public void setSname(String sname) {
        Sname = sname;
    }

    public String getSsex() {
        return Ssex;
    }

    public void setSsex(String ssex) {
        Ssex = ssex;
    }

    public int getAge() {
        return Age;
    }

    public void setAge(int age) {
        Age = age;
    }

    @Override
    public String toString() {
        return "[id"+SID+" Name"+Sname+" Gender"+Ssex+" Age"+Age+"]";
    }
}

The test code is still the last one used:

As you can see, we executed SQL once and queried two objects. The hit rate of cache was 0.5.

Posted by shahansudu on Thu, 01 Aug 2019 02:06:42 -0700