Mybatis [20] - how to deal with mybatis delayed loading?

Note: the code has been hosted on GitHub at: https://github.com/Damaer/Mybatis-Learning , the project is mybatis-16-lazload. You need to get it yourself. You need to configure maven environment and mysql environment (the SQL statement is in test.sql under resource). If you find it useful, you can click a small star.

docsify document address: https://damaer.github.io/Mybatis-Learning/#/

  • Associated object load type
    • 1. Direct loading
    • 2. Intrusive delayed loading
    • 3. Deep delayed loading
  • Direct loading
  • Intrusive delayed loading
  • Deep delayed loading

Lazy loading of mybatis, also known as delayed loading, refers to delaying the select query of associated objects according to the set delay rules during association query. Delayed loading can effectively reduce the pressure on the database. Delay loading only has delay settings for associated objects, and the main loading objects directly execute query statements

Associated object load type

The execution timing of the query select statement of the associated object of mybatis can be divided into three categories: direct loading, intrusive loading and deep delayed loading.

1. Direct loading

After executing the select statement of the main load object, the select statement of the associated object will be executed immediately.

2. Intrusive delayed loading

When the query of the main load object is executed, the query of the associated object will not be executed, but when the details of the main load object are accessed, the select query of the associated object will be executed immediately, that is, the query execution of the associated object intrudes into the details access of the main load object. It can be understood that the details of the associated object intrude into the details of the main load object, Appeared as part of it.

3. Deep delayed loading

When the query of the main load object is executed, the query of the associated object will not be executed. When accessing the details of the main load object, the select query of the associated object will not be executed. The select query of the associated object will be executed only when the details of the associated object are actually accessed.

Note: the most basic requirement of delayed loading is that the query of the associated object and the query of the main loading object must be placed in two statements respectively. Multi table connection query cannot be used, because multi table connection query is equivalent to the query of connecting multiple tables into one table. Separate query cannot be achieved, and the contents of the table will be queried at one time. Delayed loading can be applied to one to many, one to one, many to one and many to many association queries.

For example, we use the previous demo to query the relationship between Miner and country. The database is as follows:

#Create database
CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
#Create data table
CREATE TABLE `test`.`student` ( `sid` INT(10) NOT NULL AUTO_INCREMENT ,`sname` VARCHAR(20) NOT NULL ,PRIMARY KEY(`sid`)) ENGINE = MyISAM;
CREATE TABLE `test`.`course` ( `cid` INT(10) NOT NULL AUTO_INCREMENT ,`cname` VARCHAR(20) NOT NULL ,PRIMARY KEY(`cid`)) ENGINE = MyISAM;
CREATE TABLE `test`.`middle` (
`id` INT(10) NOT NULL AUTO_INCREMENT ,`studentId` INT(10) NOT NULL ,`courseId` INT(10) NOT NULL ,PRIMARY KEY(`id`)) ENGINE = MyISAM;
#Initialize data table
INSERT INTO `course` (`cid`, `cname`) VALUES ('1', 'JAVA') ;
INSERT INTO `course` (`cid`, `cname`) VALUES ('2', 'C++') ;
INSERT INTO `course` (`cid`, `cname`) VALUES ('3', 'JS') ;

INSERT INTO `student` (`sid`, `sname`) VALUES ('1', 'Jam') ;
INSERT INTO `student` (`sid`, `sname`) VALUES ('2', 'Lina') ;

INSERT INTO `middle` (`id`, `studentId`, `courseId`) VALUES ('1', '1', '1');
INSERT INTO `middle` (`id`, `studentId`, `courseId`) VALUES ('2', '1', '2');
INSERT INTO `middle` (`id`, `studentId`, `courseId`) VALUES ('3', '2', '1');
INSERT INTO `middle` (`id`, `studentId`, `courseId`) VALUES ('4', '2', '3');

Corresponding entity class: country class

public class Country {
 private Integer cid;
 private String cname;

 private Set<Minister> ministers;
 public Integer getCid() {
  return cid;
 }
 public void setCid(Integer cid) {
  this.cid = cid;
 }
 public String getName() {
  return cname;
 }
 public void setName(String cname) {
  this.cname = cname;
 }
 public Set<Minister> getMinisters() {
  return ministers;
 }
 public void setMinisters(Set<Minister> ministers) {
  this.ministers = ministers;
 }
 @Override
 public String toString() {
  return "Country [cid=" + cid + ", cname=" + cname + ", ministers="
    + ministers + "]";
 }
}

Minister.class: leader entity class

public class Minister {
 private Integer mid;
 private String mname;
 @Override
 public String toString() {
  return "Minister [mid=" + mid + ", mname=" + mname + "]";
 }
 public Integer getMid() {
  return mid;
 }
 public void setMid(Integer mid) {
  this.mid = mid;
 }
 public String getMname() {
  return mname;
 }
 public void setMname(String mname) {
  this.mname = mname;
 }
 
}

Main configuration file mybatis 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>
    <!-- Configuration database file -->
    <properties resource="jdbc_mysql.properties">

    </properties>
    <settings>
        <setting name="lazyLoadingEnabled" value="false"/>
        <!--<setting name="aggressiveLazyLoading" value="false"/>-->
    </settings>
    <!-- Alias. The full name of data object operation is too long. Alias is required -->
    <typeAliases>
        <!--<typeAlias type="bean.Student" alias="Student"/>-->
        <!--You can use the class name directly. For the path configuration (alias) of the whole package, it is simple and fast -->
        <package name="beans"/>
    </typeAliases>
    <!-- Configure the running environment -->
    <!-- default It indicates which environment is used by default, and multiple can be configured, such as test environment during development, formal environment after online, etc -->
    <environments default="mysqlEM">
        <environment id="mysqlEM">
            <transactionManager type="JDBC">
            </transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.user}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!-- Register mapping file -->
    <mappers>
        <mapper resource="mapper/mapper.xml"/>
    </mappers>
</configuration>

mapper.xml file:

<?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="dao.ICountryDao">
    <!--  resultMap It can solve the problem of different fields and attributes -->
    <!-- It will be used more in the future because delayed loading can be used-->
    <!-- nested queries  -->
    <select id="selectMinisterByCountry" resultType="Minister">
 select mid,mname from minister where countryId=#{ooo}
 </select>
    <resultMap type="Country" id="countryMapper">
        <id column="cid" property="cid"/>
        <result column="cname" property="cname"/>
        <!-- country One of the member variables in is ministers,Its generic type is Minister -->
        <collection property="ministers"
                    ofType="Minister"
                    select="selectMinisterByCountry"
                    column="cid">
        </collection>
    </resultMap>
    <select id="selectCountryById" resultMap="countryMapper">
  select cid,cname
  from country
  where
  cid=#{cid}
 </select>
</mapper>

Corresponding sql interface:

public interface ICountryDao {
 Country selectCountryById(int cid);
}

Tool classes used:

public class MyBatisUtils {
  private static SqlSessionFactory sqlSessionFactory;

  public static SqlSession getSqlSession() {
    InputStream is;
    try {
      is = Resources.getResourceAsStream("mybatis.xml");
      if (sqlSessionFactory == null) {
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
      }
      return sqlSessionFactory.openSession();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    return null;
  }
}

Direct load query

For lazy loading configuration, we only need to use mybatis It is OK to use < Settings > < / Settings > in the XML file. Lazy loading has a master switch, lazy loading enabled. As long as it is set to false, the delayed loading can be turned off, that is, the query can be loaded directly. The configuration is between < Properties > < / Properties > and < typealiases > < / typealiases >.

    <properties resource="jdbc_mysql.properties">
    </properties>
    <settings>
        <setting name="lazyLoadingEnabled" value="flase"/>
    </settings>
    <!-- Alias. The full name of data object operation is too long. Alias is required -->
    <typeAliases>
        <!--<typeAlias type="bean.Student" alias="Student"/>-->
        <!--You can use the class name directly. For the path configuration (alias) of the whole package, it is simple and fast -->
        <package name="bean"/>
    </typeAliases>

When the unit test is to directly check the country object:

 @Test
 public void TestselectCountryById(){
  Country country=dao.selectCountryById(1);
 }

As a result, we can see two pieces of sql. In addition to querying the country, it is also queried together with the associated object of the miner. This is direct loading. It is simple and rough. Whether it is used or not, the associated query will be loaded first:

[service] 2018-07-17 09:59:00,796 - dao.ICountryDao.selectCountryById -491  [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? 
[service] 2018-07-17 09:59:00,823 - dao.ICountryDao.selectCountryById -518  [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)
[service] 2018-07-17 09:59:00,838 - dao.ICountryDao.selectMinisterByCountry -533  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ====>  Preparing: select mid,mname from minister where countryId=? 
[service] 2018-07-17 09:59:00,838 - dao.ICountryDao.selectMinisterByCountry -533  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ====> Parameters: 1(Integer)
[service] 2018-07-17 09:59:00,849 - dao.ICountryDao.selectMinisterByCountry -544  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - <====      Total: 2
[service] 2018-07-17 09:59:00,850 - dao.ICountryDao.selectCountryById -545  [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1

Intrusive delayed loading

The delayed loading switch needs to be turned on (true), and the intrusive loading switch needs to be turned on (true)

    <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressivelazyLoading" value="true"/>
    </settings>

1. When we only query the country, we will only execute the query of the country, not the associated query

 @Test
 public void TestselectCountryById(){
  Country country=dao.selectCountryById(1);
 }

The result is as follows: there is only one sql:

[service] 2018-07-17 14:30:55,471 - dao.ICountryDao.selectCountryById -902  [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? 
[service] 2018-07-17 14:30:55,494 - dao.ICountryDao.selectCountryById -925  [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)
[service] 2018-07-17 14:30:55,590 - dao.ICountryDao.selectCountryById -1021 [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1

When we query the country attribute, but not the minister attribute:

 @Test
 public void TestselectCountryById(){
  Country country=dao.selectCountryById(1);
  System.out.println(country.getCid());
 }

The results are as follows: it will be loaded into the associated object minister, which is intrusive delayed loading:

[service] 2018-07-17 14:32:37,959 - dao.ICountryDao.selectCountryById -724  [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? 
[service] 2018-07-17 14:32:37,979 - dao.ICountryDao.selectCountryById -744  [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)
[service] 2018-07-17 14:32:38,170 - dao.ICountryDao.selectCountryById -935  [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1
[service] 2018-07-17 14:32:38,171 - dao.ICountryDao.selectMinisterByCountry -936  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ==>  Preparing: select mid,mname from minister where countryId=? 
[service] 2018-07-17 14:32:38,171 - dao.ICountryDao.selectMinisterByCountry -936  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ==> Parameters: 1(Integer)
[service] 2018-07-17 14:32:38,173 - dao.ICountryDao.selectMinisterByCountry -938  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - <==      Total: 2
1

Deep delayed loading

You need to turn on the lazy loading enabled switch (true) and turn off the aggressive loading switch (false)

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

1. When we only query the country, we will only query the country, not the minister: unit test code:

 @Test
 public void TestselectCountryById(){
  Country country=dao.selectCountryById(1);
 }
[service] 2018-07-17 14:20:38,608 - dao.ICountryDao.selectCountryById -1271 [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? 
[service] 2018-07-17 14:20:38,631 - dao.ICountryDao.selectCountryById -1294 [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)
[service] 2018-07-17 14:20:38,980 - dao.ICountryDao.selectCountryById -1643 [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1

2. When we access the attribute of country, we will not load the Minster of the associated query:

 @Test
 public void TestselectCountryById(){
  Country country=dao.selectCountryById(1);
  System.out.println(country.getCid());
 }

The result is also:

[service] 2018-07-17 14:24:03,004 - org.apache.ibatis.transaction.jdbc.JdbcTransaction -686  [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@cb51256]
[service] 2018-07-17 14:24:03,030 - dao.ICountryDao.selectCountryById -712  [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? 
[service] 2018-07-17 14:24:03,078 - dao.ICountryDao.selectCountryById -760  [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)
[service] 2018-07-17 14:24:03,160 - dao.ICountryDao.selectCountryById -842  [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1

3. When we query the country attribute minister:

 @Test
 public void TestselectCountryById(){
  Country country=dao.selectCountryById(1);
  System.out.println(country.getMinisters());
 }

We can see the result. The query of the miner is executed. This is the time to load the miner query

[service] 2018-07-17 14:26:55,913 - dao.ICountryDao.selectCountryById -1540 [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? 
[service] 2018-07-17 14:26:55,943 - dao.ICountryDao.selectCountryById -1570 [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)
[service] 2018-07-17 14:26:56,161 - dao.ICountryDao.selectCountryById -1788 [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1
[service] 2018-07-17 14:26:56,162 - dao.ICountryDao.selectMinisterByCountry -1789 [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ==>  Preparing: select mid,mname from minister where countryId=? 
[service] 2018-07-17 14:26:56,163 - dao.ICountryDao.selectMinisterByCountry -1790 [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ==> Parameters: 1(Integer)
[service] 2018-07-17 14:26:56,168 - dao.ICountryDao.selectMinisterByCountry -1795 [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - <==      Total: 2
[Minister [mid=2, mname=bbb], Minister [mid=1, mname=aaa]]

Loading mode

lazyLoadingEnabled

aggressiveLazyLoading

Direct loading

Must be false. The default is false

No matter what it is, as long as lazyloading enabled is false, it is loaded directly

Intrusive delayed loading

Must be true

Must be true

Deep delayed loading

Must be true

Must be false. The default is false

Added by Afrojojo on Tue, 15 Feb 2022 08:54:19 +0200