Understand mybatis gradually through the project (4)

Time:2020-10-22

Related reading:

1、Understand mybatis < 1 > step by step through the project

2、Understand mybatis < 2 > step by step through the project

3、Understand mybatis < 3 > gradually through the project

All code and documents of this project are hosted in GitHub address:https://github.com/zhisheng17/mybatis

Delay loading

What is lazy loading?

Resultmap can realize high-level mapping (one-to-one and one to many mapping using association and collection). Association and collection have the function of delayed loading.
Demand:
If you query the order and query the user information by association. If the order information can meet the requirements first, we can query the user information when we need to query the user information. To query user information on demand is to delay loading.

Delayed loading: query from a single table first, and then associate queries from associated tables when necessary, which greatly improves database performance because querying a single table is faster than querying multiple tables by association.

Turn on the delay load switch

In mybatis core configuration file, configure:

lazyLoadingEnabled、aggressiveLazyLoading

Set item describe Allowable value Default value
lazyLoadingEnabled Set lazy loading globally. If set to ‘false’, all associated will be initialized to load. true false false
aggressiveLazyLoading When set to ‘true’, lazy loaded objects may be loaded by any lazy attribute. Otherwise, each property is loaded on demand. true false true
<settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
</settings>

Using association to implement delayed loading

Requirements: query orders and query user information by association

Mapper.xml

You need to define the statement corresponding to the methods of two mappers.

1. Query order information only

SQL statement:select * from orders

In the statement of the query order, use association to delay the loading (execution) of the statement below

<! -- query the order and query the user information by association. The associated user information needs to be delayed loaded through association -- >
    <select id="findOrdersUserLazyLoading" resultMap="OrdersUserLazyLoadingResultMap">
        select * from orders
    </select>

2. Association query user information

Query the user in the order information through the above_ ID to query user information. use UserMapper.xml Finduserbyid in

SQL statement:select * from user where id = user_id

<select id="findUserById" parameterType="int" resultType="user">
        select * from user where id = #{value}
    </select>

First, findorders userlazyloading is executed. When the user needs to be queried, finduserbyid is executed. Through the definition of resultmap, the deferred loading execution is configured. That is to load it through resultmap UserMapper.xml Select = finduserbyid in the file

Delayed loading resultmap

<! -- define the resultmap of associated user information (loaded by association delay) - >
    <resultMap id="OrdersUserLazyLoadingResultMap" type="cn.zhisheng.mybatis.po.Orders">
        <! -- mapping order information -- >
        <id column="id" property="id"/>
        <result column="user_id" property="userId"/>
        <result column="number" property="number"/>
        <result column="createtime" property="createtime"/>
        <result column="note" property="note"/>
        <! -- delay loading of user information
        Select: Specifies the ID of the statement to be executed for deferred loading (based on user_ ID query user information statement)
        To use userMapper.xml According to the user ID (user_ ID) user information query. If finduserbyid is not in this mapper, you need to add a namespace before it
        Column: the column associated with user information query in order information, which is user_ ID
        The SQL of associated query is understood as:
            SELECT orders.*,
            (SELECT username FROM USER WHERE orders.user_id = user.id)username,
            (SELECT sex FROM USER WHERE orders.user_id = user.id)sex
            FROM orders-->
        <association property="user" javaType="cn.zhisheng.mybatis.po.User" select="cn.zhisheng.mybatis.mapper.UserMapper.findUserById" column="user_id">
        </association>
    </resultMap>

OrderMapperCustom.java

public List<Orders> findOrdersUserLazyLoading() throws Exception;

Test code:

@Test
    public void testFindOrdersUserLazyLoading() throws Exception
    {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //Create an ordersmappercustom object, and mybatis automatically generates a proxy object
        OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
        //Query order information
        List<Orders> list = ordersMapperCustom.findOrdersUserLazyLoading();
        //Traverse the query order information
        for (Orders orders : list)
        {
            //Query user information
            User user = orders.getUser();
            System.out.println(user);
        }
        sqlSession.close();
    }

Test results:

Understand mybatis gradually through the project (4)

The whole idea of delay loading is as follows

1. Execute the upper mapper method (findordersuserlazyloading) and call it internally cn.zhisheng.mybatis . mapper.OrdersMapperCustom Findorders userlazyloading in only queries the orders information (single table).

2. In the program, go through the list < orders > found in the previous step. When we call the getuser method in orders, we start to delay loading.

3. Delay loading, calling UserMapper.xml The finduserbyid method is used to obtain user information.

reflection:

How to implement delayed loading without using the association and collection delay loading function provided by mybatis??

The implementation method is as follows:

Two mapper methods are defined

1. Query order list

2. Query user information according to user ID

Realization idea:

First, query the first mapper method to get the list of order information

In the program (service), call the second mapper method as needed to query the user information.

In a word:

Using the delayed loading method, first query simple SQL (preferably single table, or associated query), and then load other information of associated query as required.

One to many delayed loading

The case above is one-to-one delayed loading. So if we want to delay loading one to many, it is actually very simple.

The method of one to many delayed loading is the same as one-to-one delayed loading, and the select content is configured in the collection tag.

Summary of delayed loading:

effect:

When you need to query the association information, go to the database query again. By default, do not query the association, which improves the performance of the database.
Delayed load settings are only supported using resultmap.

Occasion:

When only some records need to be associated to query other information, the load can be delayed on demand, and SQL can be sent to the database when the association query is needed to improve the performance of the database.

When all the associated query information is needed, the associated query information can be returned directly without delay loading. The mapping can be completed by using resulttype or resultmap.

Query cache

What is query caching?

Mybatis is is used to improve the performance of database.

Mybaits provides first level cache and second level cache.

Understand mybatis gradually through the project (4)

  • The first level cache is a sqlsession level cache. When operating the database, we need to construct the sqlsession object, in which there is a data structure (HashMap) to store the cache data. The HashMap between different sqlsessions does not affect each other.

  • Level 2 cache is mapper level cache. Multiple sqlsessions operate on the same mapper SQL statement. Multiple sqlsessions can share the level 2 cache. The level 2 cache is cross sqlsession.

Why cache?

If there is data in the cache, there is no need to get it from the database, which greatly improves the system performance.

First level cache

working principle

Understand mybatis gradually through the project (4)

For the first time, query the user information with user ID 1. First, find out whether there is user information with ID 1 in the cache. If not, query the user information from the database.

The user information is obtained and stored in the first level cache.

If the sqlsession executes the commit operation (insert, update, delete), clear the first level cache in the sqlsession. The purpose of this is to make the cache store the latest information and avoid dirty reading.

For the second time, query the user information with user ID 1. First, find out whether there is user information with ID 1 in the cache. If there is, the user information is directly obtained from the cache.

Level 1 cache test

Mybatis supports the first level cache by default and does not need to be configured in the configuration file.

So we directly follow the above steps to test:

//Level 1 cache test
    @Test
    public void  testCache1() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //Create a usermapper object, and mybatis automatically generates a proxy object
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //The query uses the same session
        //For the first time, the user information with ID 1 is queried
        User user1 = userMapper.findUserById(1);
        System.out.println(user1);
        //The second request is initiated to query the user information with ID 1
        User user2 = userMapper.findUserById(1);
        System.out.println(user2);
        sqlSession.close();
    }

Understand mybatis gradually through the project (4)

From the results, we can see that no SQL query request was issued the second time,

So we need to commit in the middle

//If the sqlsession performs the commit operation (insert, update, delete),
//Empty the first level cache in sqlsession. The purpose of this is to make the latest information stored in the cache and avoid dirty reading.
//Update user1 information,
User1. Setusername ("Li Fei");
//User1. Setsex ("male");
//User1. Setaddress ("Beijing");
userMapper.updateUserById(user1);
//Only when the transaction is committed will the cache be emptied
sqlSession.commit();

test

Understand mybatis gradually through the project (4)

Level 1 cache application

Formal development is to integrate mybatis and spring, and control the transaction in service.

A service method includes many mapper method calls.

service{

//At the beginning of execution, the transaction is opened and the sqlsession object is created

     //First call to mapper's method finduserbyid (1)

     //The second time, mapper's method finduserbyid (1) is called to fetch data from the first level cache

     //Method ends and sqlsession closes

}

If you perform two service calls to query the same user information, do not go to the first level cache, because the session method ends, sqlsession is closed, and the first level cache is cleared.

L2 cache

principle

Understand mybatis gradually through the project (4)

First, turn on the second level cache of mybatis.

Sqlsession1 will query the user information with user ID 1. If the user information is found, the query data will be stored in the secondary cache.

If sqlsession3 executes SQL under the same mapper, execute commit to clear the data in the secondary cache area under the mapper.

Sqlsession2 is used to query the user information with user ID 1, and to find whether there is data in the cache. If so, the data is directly extracted from the cache.

The second level cache is different from the first level cache. The scope of the second level cache is larger. Multiple SQL sessions can share a second level cache area of usermapper.

User mapper has a second level cache area (divided by namespace), and other mappers also have their own secondary cache area (divided by namespace).

Each mapper of a namespace has a secondary cache area. If the two mappers have the same namespace, the two mappers will execute SQL query and the data will be stored in the same secondary cache area.

Enable L2 cache

The second level cache of mybaits is the mapper range level, except for the SqlMapConfig.xml To set the master switch of level 2 cache, you need to set the mapper.xml Enable Level 2 cache in

In SqlMapConfig.xml Turn on the secondary switch

<! -- enable L2 cache -- >
<setting name="cacheEnabled" value="true"/>

Then add a line to your mapper map file: < cache / >, indicating that the mapper has secondary cache enabled.

Calling POJO class to realize serialization interface

Second level cache needs POJO object implementation of query result mapping java.io.Serializable Interface implements serialization and deserialization operations (because the secondary cache data storage media are various, and the memory is different). Note that if there is a parent class or member POJO, you need to implement the serialization interface.

public class Orders implements Serializable
public class User implements Serializable

test

//L2 cache test
    @Test
    public void testCache2() throws Exception
    {
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        SqlSession sqlSession3 = sqlSessionFactory.openSession();


        //Create a usermapper object, and mybatis automatically generates a proxy object
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        //Sqlsession1 execute query write cache (first query request)
        User user1 = userMapper1.findUserById(1);
        System.out.println(user1);
        sqlSession1.close();


        //Sqlsession3 performs commit empty cache
        UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
        User user3 = userMapper3.findUserById(1);
        User3. Setsex ("female");
        User3. Setaddress ("Shandong Jinan");
        User3. Setusername ("Cui Jian");
        userMapper3.updateUserById(user3);
        //Commit transaction, clear cache
        sqlSession3.commit();
        sqlSession3.close();
        
        //Sqlsession2 execute query (second query request)
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        User user2 = userMapper2.findUserById(1);
        System.out.println(user2);
        sqlSession2.close();
   }

result

Understand mybatis gradually through the project (4)

Usecache configuration

Setting usecache = false in the statement can disable the second level cache of the current select statement. That is, each query will issue SQL to query. The default is true, that is, the SQL uses the second level cache.

<select id="findUserById" parameterType="int" resultType="user" useCache="false">

Summary: for each query, the latest data SQL is required. To set usecache = false, disable the secondary cache.

Refresh cache (empty cache)

In the same namespace of the mapper, if the cache needs to be refreshed after other operations such as insert, update and delete, dirty read will occur if the cache is not refreshed.

Set the flushcache = true property in the statement configuration. By default, it is true, that is, the cache will be refreshed. If it is changed to false, the cache will not be refreshed. When using cache, if you manually modify the query data in the database table, dirty reading will occur.

As follows:

<insert id="insetrUser" parameterType="cn.zhisheng.mybatis.po.User" flushCache="true">

Generally, the cache needs to be refreshed after the commit operation. Flushcache = true indicates that the cache is refreshed, so as to avoid dirty reading of the database.

Mybatis cache parameter

The flush interval can be set to any positive integer, and they represent a reasonable period of time in milliseconds. By default, it is not set, that is, there is no refresh interval, and the cache is only refreshed when the statement is called.

Size can be set to any positive integer, keeping in mind the number of objects you cache and the number of memory resources available to your running environment. The default value is 1024.

The readonly property can be set to true or false. A read-only cache returns the same instance of the cache object to all callers. Therefore, these objects cannot be modified. This provides a significant performance advantage. A read-write cache returns a copy of the cached object (via serialization). This is slower, but safe, so the default is false.

Here are some examples:

<cache  eviction="FIFO" flushInterval="60000"  size="512" readOnly="true"/>

This more advanced configuration creates a FIFO cache, which is refreshed every 60 seconds, stores 512 references to the result object or list, and the returned objects are considered read-only, so modifying them between callers in different threads can cause conflicts. The available recall strategies are: LRU: LRU

  1. LRU – least recently used: removes objects that have not been used for the longest time.

  2. FIFO – first in, first out: remove objects in the order they enter the cache.

  3. Soft – soft reference: removes objects based on the garbage collector state and soft reference rules.

  4. Weak reference: remove objects based on garbage collector state and weak reference rules more actively.

Mybatis integrates ehcache

Ehcache is a distributed caching framework.

Distributed cache

In order to improve the concurrency and performance of our system, we usually deploy the system in a distributed way (cluster deployment)

Understand mybatis gradually through the project (4)

Without distributed cache, the cached data is stored separately in each service, which is not convenient for system development. Therefore, the distributed cache should be used for centralized management of cache data.

Mybatis can’t implement distributed caching, so it needs to be integrated with other distributed caching frameworks.

Integration approach

Mybatis provides a secondary cache interface(org.apache.ibatis.cacheUnder theCache)If you want to implement your own cache logic, you can develop the cache interface.

import java.util.concurrent.locks.ReadWriteLock;
public interface Cache {
    String getId();
    void putObject(Object var1, Object var2);
    Object getObject(Object var1);
    Object removeObject(Object var1);
    void clear();
    int getSize();
    ReadWriteLock getReadWriteLock();
}

Mybatis and ehcache are integrated. An implementation class of cache interface is provided in mybatis and ehcache integration package(org.apache.ibatis.cache.implUnder thePerpetualCache)。

package org.apache.ibatis.cache.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
public class PerpetualCache implements Cache {
    private String id;
    private Map<Object, Object> cache = new HashMap();
    public PerpetualCache(String id) {
        this.id = id;
    }
    public String getId() {
        return this.id;
    }
    public int getSize() {
        return this.cache.size();
    }
    public void putObject(Object key, Object value) {
        this.cache.put(key, value);
    }
    public Object getObject(Object key) {
        return this.cache.get(key);
    }
    public Object removeObject(Object key) {
        return this.cache.remove(key);
    }
    public void clear() {
        this.cache.clear();
    }
    public ReadWriteLock getReadWriteLock() {
        return null;
    }
    public boolean equals(Object o) {
        if(this.getId() == null) {
            throw new CacheException("Cache instances require an ID.");
        } else if(this == o) {
            return true;
        } else if(!(o instanceof Cache)) {
            return false;
        } else {
            Cache otherCache = (Cache)o;
            return this.getId().equals(otherCache.getId());
        }
    }
    public int hashCode() {
        if(this.getId() == null) {
            throw new CacheException("Cache instances require an ID.");
        } else {
            return this.getId().hashCode();
        }
    }
}

Through the implementation of cache interface, mybatis cache data can be integrated through other cache databases. The specialty of mybatis is SQL operation, and the management of cache data is not mybatis. In order to improve the performance of cache, mybatis and third-party cache databases are integrated, such as ehcache, Memcache, redis, etc.

  • Introduce dependency package

ehcache-core-2.6.5.jarandmybatis-ehcache-1.0.2.jar

  • Introduce cache profile

Under classpath, add: ehcache.xml

The contents are as follows:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
  <diskStore path="C:\JetBrains\IDEAProject\ehcache" />
  <defaultCache 
      maxElementsInMemory="1000" 
      maxElementsOnDisk="10000000"
      eternal="false" 
      overflowToDisk="false" 
      timeToIdleSeconds="120"
      timeToLiveSeconds="120" 
      diskExpiryThreadIntervalSeconds="120"
      memoryStoreEvictionPolicy="LRU">
  </defaultCache>
</ehcache>

Attribute description:

  • Diskstore: specifies where the data is stored on the disk.

  • Default cache: when using CacheManager.add (“democache”) when creating a cache, ehcache will adopt the management policy specified by < defalutcache / >

The following properties are required:

  • Maxelements inmemory – the maximum number of elements cached in memory

  • Maxelements ondisk – the maximum number of elements cached on the disk. If 0, it means infinity

  • Eternal – sets whether cached elements will never expire. If toseconds is always valid, then toseconds is still valid according to the data

  • Overflow todisk – sets whether to cache expired elements to disk when the memory cache overflows

The following properties are optional:

    • Timetoidleseconds – when the data cached in ehcache takes more than the value of timetoidleseconds property, the data will be deleted. The default value is 0, that is, the idle time is infinite

    • Timeto live seconds – the valid lifetime of the cache element. The default value is 0. That is, the element lifetime is infinite

      Diskspoolbuffersizemb this parameter sets the cache size of diskstore. The default value is 30MB. Each cache should have its own buffer
    • Diskpersistent – whether to enable the disk to save the data in ehcache during VM restart. The default value is false.

    • Diskexpirythreadintervalseconds – the interval between disk cache cleanup threads. The default is 120 seconds. Each 120s, the corresponding thread will clean up the data in ehcache once

    • Memorystoreevidence policy – the policy to remove elements from the cache when a new element is added when the memory cache reaches its maximum size. The default is LRU (least recently used), and LFU (least frequently used) and FIFO (first in first out) are optional

    • Enable ehcache cache

    Ehcache is the implementation of ehcache to cache interface mapper.xml File, specify ehcachecache in cache.

    Adjust cache parameters according to requirements:

    <cache type="org.mybatis.caches.ehcache.EhcacheCache" > 
            <property name="timeToIdleSeconds" value="3600"/>
            <property name="timeToLiveSeconds" value="3600"/>
            <! -- same as ehcache parameter maxelementsinmemory -- >
          <property name="maxEntriesLocalHeap" value="1000"/>
          <! -- same as ehcache parameter maxelementsondisk -- >
            <property name="maxEntriesLocalDisk" value="10000000"/>
            <property name="memoryStoreEvictionPolicy" value="LRU"/>
        </cache>

    test: (this hit rate represents the successful integration of ehcache and mybatis)

    Understand mybatis gradually through the project (4)

    Application scenarios

    For the query requests with multiple accesses and the user’s demand for real-time query results is not high, the mybatis secondary cache technology can be used to reduce the database access and improve the access speed. Business scenarios such as: time-consuming statistical analysis SQL, telephone bill query SQL, etc.

    The implementation method is as follows: by setting the refresh interval, mybatis automatically empties the cache at regular intervals. The cache refresh interval is set according to the data change frequency, such as 30 minutes, 60 minutes, 24 hours, etc., depending on the demand.

    limitations

    Mybatis Level 2 cache is not good for fine-grained data level cache. For example, the following requirements are required: cache the commodity information. Due to the large amount of commodity information query, users are required to query the latest product information every time. In this case, if you use mybatis When a product changes, it can only refresh the cache information of the product without refreshing the information of other products. Because the secondary cache area of mybaits is divided by mapper, when the information of a commodity changes, the cache data of all commodity information will be emptied completely. To solve this problem, we need to cache the data according to the requirements in the business layer.

    Recommended Today

    Layout of angular material (2): layout container

    Layout container Layout and container Using thelayoutDirective to specify the layout direction for its child elements: arrange horizontally(layout=”row”)Or vertically(layout=”column”)。 Note that if thelayoutInstruction has no value, thenrowIs the default layout direction. row: items arranged horizontally.max-height = 100%andmax-widthIs the width of the item in the container. column: items arranged vertically.max-width = 100%andmax-heightIs the height of the […]