MyBatis delay load (lazy load) Introduction

Keywords: Programming SQL Mybatis xml Database

MyBatis delayed loading

Introduction

In the previous article, multi table query is introduced. In practice, we often involve multi table joint query, but sometimes, not all query results will be used immediately. Let me give two examples:

  • For example, to query the purchase details of a batch of notebook computers, instead of directly displaying the details of the corresponding computer configuration or price of each column of details, wait until the user needs to take out the relevant details of a notebook, and then conduct a single table query
  • For example, in a bank, if a user has 50 accounts (for example), then we can query the user's information. Obviously, it is more reasonable to query the details of all the accounts under the user when using

In such a case, the mechanism of delayed loading appears. As the name implies, delayed loading (lazy loading) is to delay the loading of certain information. Such technology also helps us realize the mechanism of "query on demand". In the case of one to many, or many to many

Since the delay loading is mentioned, of course, by the way, immediate loading means to query immediately no matter whether the user needs it or not. This method is suitable for many to one or one to one situations

(1) Necessary preparation

First, configure the basic environment, and then we prepare two tables in the database

User table

CREATE TABLE USER (
 `id`			INT(11)NOT NULL AUTO_INCREMENT,
 `username` 	VARCHAR(32) NOT NULL COMMENT 'User name',
 `telephone`    VARCHAR(11) NOT NULL COMMENT 'Mobile phone',
 `birthday`		DATETIME DEFAULT NULL COMMENT 'Birthday',
 `gender`  		CHAR(1) DEFAULT NULL COMMENT 'Gender',
 `address` 		VARCHAR(256) DEFAULT NULL COMMENT 'address',
  PRIMARY KEY  (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

Account table

CREATE TABLE `account` (
  `ID` int(11) NOT NULL COMMENT 'number',
  `UID` int(11) default NULL COMMENT 'User number',
  `MONEY` double default NULL COMMENT 'Amount of money',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Then create the corresponding entity classes

Class User

public class User implements Serializable {
    private Integer id;
    private String username;
    private String telephone;
    private Date birthday;
    private String gender;
    private String address;
    //For one to many relationship mapping, the primary table entity should contain the set reference of the secondary table entity
    private List<Account> accounts;
	...... Please add get set and toString Method
}

Class Account

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    //The slave table entity should contain an object reference of the master table entity
    private User user;
    ...... Please add get set and toString Method
}

UserMapper.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="cn.ideal.mapper.UserMapper">

    <!-- Definition User Of resultMap-->
    <resultMap id="userAccountMap" type="User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="telephone" column="telephone"></result>
        <result property="birthday" column="birthday"></result>
        <result property="gender" column="gender"></result>
        <result property="address" column="address"></result>
        <collection property="accounts" ofType="account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>
    </resultMap>
    
    <!-- Query all users and display corresponding account information -->
    <select id="findAll" resultMap="userAccountMap">
       SELECT u.*,a.id as aid,a.uid,a.money FROM user u LEFT OUTER JOIN account a on u.id = a.uid;
    </select>

    <!-- according to id Query users -->
    <select id="findById" parameterType="INT" resultType="User">
        select * from user where id = #{uid}
    </select>
    
</mapper>

Create corresponding methods in two interfaces

public interface AccountMapper {
    /**
     * Query all accounts
     * @return
     */
    List<Account> findAll();
}
public interface UserMapper {
    /**
     * Query all user information and display all accounts under the user at the same time
     *
     * @return
     */
    List<User> findAll();

    /**
     * Query user information according to id
     * @param userId
     * @return
     */
    User findById(Integer userId);
}

##(1) Delay loading code implementation

First of all, let me show you how we used to query users one-on-one, and at the same time, we will also query all the account information corresponding to users

/**
* Test query all
*/
@Test
public void testFindAll() {
	List<User> users= userMapper.findAll();
    for (User user : users) {
        System.out.println("---------------------");
        System.out.println(user);
        System.out.println(user.getAccounts());
    }
}

Effect:

In this way, SQL statements and resultmaps are used to query the information of users and accounts at the same time

So how to implement the delayed loading we mentioned above?

This time, we choose to query the account, and then delay loading the user's information

(1) Modify AccountMapper.xml

The first thing that needs to be modified is the Account mapping configuration file. You can see that when querying, we still define a resultMap, encapsulate the Account first, and then associate the User through the association, in which select and column are used to delay the loading of User information

  • Select is used to specify the SQL statement to be executed for delayed loading, that is, to specify the id of a select tag pair in a SQL mapping file. Here we specify the method of querying information through id in the user
  • Column refers to the column of associated user information query, which is the primary key of associated user, i.e. id
<mapper namespace="cn.ideal.mapper.AccountMapper">
	<!-- Define encapsulation Account and User Of resultMap -->
    <resultMap id="userAccountMap" type="Account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- Configuration encapsulation User Content
            select: Unique identification of the query user
            column: User base id Required parameter value when querying
        -->
        <association property="user" column="uid" javaType="User" select="cn.ideal.mapper.UserMapper.findById"></association>
    </resultMap>

    <!-- Query all accounts according to -->
    <select id="findAll" resultMap="userAccountMap">
        SELECT * FROM account
    </select>
</mapper>

(2) First test code

We only need to perform all methods of account query to see if we can achieve our effect

@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
}

(3) Execution effect

You can see that all three SQL statements have been executed. Why?

This is because we need to turn on the delay loading function before testing the method

(4) Delay loading function

We can go to the official website, how to configure and enable such a function

After consulting the documents, we know that if you want to start the delay loading function, you need to configure the setting property in the general configuration file SqlMapConfig.xml, that is, set the switch of delay loading lazyloading enable to teue. Because it is on-demand loading, you need to change the active loading to passive loading, that is, change the aggressiveLazyLoading to false

Of course, since the version of MyBatis I imported here is 3.4.5, the default value is false. In fact, it doesn 't need to be set, but we still write it out

<settings>
	<setting name="lazyLoadingEnabled" value="true"/>
	 <setting name="aggressiveLazyLoading" value="false"></setting>
</settings>

Note: if you use typeAliases to configure aliases, you must put the typeAliases label after it

(5) Retest

Still only query methods are executed

@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
}

Execution effect

This time, only one command to query account was executed

What about when users want to view the users corresponding to each account? This is also called on-demand query. You only need to add the corresponding acquisition method during the test

@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
    for (Account account : accounts){
        System.out.println("----------------------------");
        System.out.println(account);
        System.out.println(account.getUser());
    }
}

Execute it.

As you can see, we have achieved the goal of delaying loading

summary

In the above test, we have implemented delayed loading, and briefly summarize the following steps:

  • ① : execute the corresponding mapper method, that is, execute the corresponding SQL configuration with the id value of findAll in mapper in the above example, and only query the account information

  • ② : in the program, when you traverse the queried accounts and call the getUser() method, the delay load starts

    • List<Account> accounts = accountMapper.findAll();
  • ③ : delay loading, call the corresponding SQL configuration with the id value of findById in the mapping file, and get the information of the corresponding user

As you can see, we can directly query multiple tables by using SQL writing methods such as left outer connection

SELECT u.*,a.id as aid,a.uid,a.money FROM user u LEFT OUTER JOIN account a on u.id = a.uid;

However, we can achieve our on-demand query requirements through delayed loading. To sum up, when using, first execute simple SQL, and then load and query other information as required

Ending

If there are any deficiencies in the article, welcome to leave a message and exchange, thank you for your support!

If you can help, then pay attention to me! If you prefer the way of reading WeChat articles, you can pay attention to my public number.

We don't know each other here, but we are working hard for our dreams

A public figure that persists in pushing original development technology articles: ideal two days

Posted by kkeim on Mon, 10 Feb 2020 02:50:54 -0800