一文看懂Mybatis中的延遲加載
1. 延遲加載簡介
MyBatis中的延遲加載,也稱為懶加載,是指在進行表的關聯(lián)查詢時,按照設置延遲規(guī)則推遲對關聯(lián)對象的select查詢。例如在進行一對多查詢的時候,只查詢出一方,當程序中需要多方的數(shù)據時,mybatis再發(fā)出sql語句進行查詢,這樣子延遲加載就可以的減少數(shù)據庫壓力。MyBatis 的延遲加載只是對關聯(lián)對象的查詢有遲延設置,對于主加載對象都是直接執(zhí)行查詢語句的。
好處:
先從單表查詢,需要時再從關聯(lián)表去關聯(lián)查詢,大大提高數(shù)據庫性能,因為查詢單表要比關聯(lián)查詢多張表速度要快。
壞處:
因為只有當需要用到數(shù)據時,才會進行數(shù)據庫查詢,這樣在大批量數(shù)據查詢時,因為查詢工作也要消耗時間,所以可能造成用戶等待時間變長,造成用戶體驗下降。
2. 關聯(lián)對象加載時機
MyBatis根據對關聯(lián)對象查詢的select語句的執(zhí)行時機,分為三種類型:直接加載、侵入式延遲加載與深度延遲加載。
- 直接加載:執(zhí)行完對主加載對象的 select 語句,馬上執(zhí)行對關聯(lián)對象的 select 查詢。
- 侵入式延遲: 執(zhí)行對主加載對象的查詢時,不會執(zhí)行對關聯(lián)對象的查詢。但當要訪問主加載對象的詳情屬性時,就會馬上執(zhí)行關聯(lián)對象的select查詢。
- 深度延遲: 執(zhí)行對主加載對象的查詢時,不會執(zhí)行對關聯(lián)對象的查詢。訪問主加載對象的詳情時也不會執(zhí)行關聯(lián)對象的select查詢。只有當真正訪問關聯(lián)對象的詳情時,才會執(zhí)行對關聯(lián)對象的 select 查詢。
需要注意的是, 延遲加載的應用要求,關聯(lián)對象的查詢與主加載對象的查詢必須是分別進行的 select 語句,不能是使用多表連接所進行的select查詢。因為多表連接查詢,其實質是對一張表的查詢,對由多個表連接后形成的一張表的查詢。會一次性將多張表的所有信息查詢出來。
MyBatis中對于延遲加載設置,只對于resultMap中的collection和association起作用,可以應用到一對一、一對多、多對一、多對多的所有關聯(lián)關系查詢中。
下面以一對多關聯(lián)關系查詢?yōu)槔v解 MyBatis 中的延遲加載應用。
3. 直接加載
主配置文件SqlMapConfig.xml中設置lazyLoadingEnabled為false,默認為false。(因為默認式false,所以也可以不設置lazyLoadingEnabled)
<?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>
<!-- 配置properties -->
<properties resource="jdbcConfig.properties"></properties>
<!-- 配置參數(shù) -->
<settings>
<!-- 關閉mybatis支持延遲加載 -->
<setting name="lazyLoadingEnabled" value="false"/>
</settings>
<!-- 使用typeAliases配置別名,它只能配置domain中類的別名 -->
<typeAliases>
<package name="com.siyi.domain"></package>
</typeAliases>
<!-- 配置環(huán)境 -->
<environments default="mysql">
<!-- 配置mysql的環(huán)境 -->
<environment id="mysql">
<!-- 配置事務 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置連接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 映射配置文件 -->
<mappers>
<mapper resource="com/siyi/dao/IUserDao.xml"/>
<mapper resource="com/siyi/dao/IAccountDao.xml"/>
</mappers>
</configuration>
dao映射配置文件
<?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="com.siyi.dao.IUSerDao">
<!-- 定義User的resultMap -->
<resultMap id="userAccountMap" type="user">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="address" column="address"/>
<result property="sex" column="sex"/>
<result property="birthday" column="birthday"/>
<collection property="accounts" ofType="account" select="com.siyi.dao.IAccountDao.findAccountByUid" column="id"/>
</resultMap>
<!-- 查詢所有 -->
<select id="findAll" resultMap="userAccountMap">
<!-- select * from user; -->
select * from user
</select>
</mapper>
測試
package com.siyi.test;
import com.siyi.dao.IUSerDao;
import com.siyi.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserTest {
private InputStream in;
private SqlSession session;
private IUSerDao iuSerDao;
@Before//用于在測試方法執(zhí)行之前執(zhí)行
public void init() throws IOException {
//1.讀取配置文件生成字節(jié)輸入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.獲取SqlSessionFactory對象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.獲取SqlSession對象
session = factory.openSession(true);
//4.獲取DAO的代理對象
iuSerDao = session.getMapper(IUSerDao.class);
}
@After//用于在測試方法之后執(zhí)行
public void destroy() throws IOException {
//事務提交
//session.commit();
//釋放資源
session.close();
in.close();
}
/**
* 測試查詢所有
* @throws IOException
*/
@Test
public void testFindAll() throws IOException {
//5.執(zhí)行查詢所有方法
List<User> users = iuSerDao.findAll();
}
}
運行過程中就查詢了所有的信息:

4. 侵入式延遲加載
主配置文件SqlMapConfig.xml中設置lazyLoadingEnabled為true,默認為false,并且將aggressiveLazyLoading設置為true。(aggressiveLazyLoading默認值為false (在 3.4.1 及之前的版本中默認為 true))
<?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>
<!-- 配置properties -->
<properties resource="jdbcConfig.properties"></properties>
<!-- 配置參數(shù) -->
<settings>
<!-- 開啟mybatis支持延遲加載 -->
<setting name="lazyLoadingEnabled" value="false"/>
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
<!-- 使用typeAliases配置別名,它只能配置domain中類的別名 -->
<typeAliases>
<package name="com.siyi.domain"></package>
</typeAliases>
<!-- 配置環(huán)境 -->
<environments default="mysql">
<!-- 配置mysql的環(huán)境 -->
<environment id="mysql">
<!-- 配置事務 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置連接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 映射配置文件 -->
<mappers>
<mapper resource="com/siyi/dao/IUserDao.xml"/>
<mapper resource="com/siyi/dao/IAccountDao.xml"/>
</mappers>
</configuration>
dao映射配置文件不變
測試
package com.siyi.test;
import com.siyi.dao.IUSerDao;
import com.siyi.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserTest {
private InputStream in;
private SqlSession session;
private IUSerDao iuSerDao;
@Before//用于在測試方法執(zhí)行之前執(zhí)行
public void init() throws IOException {
//1.讀取配置文件生成字節(jié)輸入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.獲取SqlSessionFactory對象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.獲取SqlSession對象
session = factory.openSession(true);
//4.獲取DAO的代理對象
iuSerDao = session.getMapper(IUSerDao.class);
}
@After//用于在測試方法之后執(zhí)行
public void destroy() throws IOException {
//事務提交
//session.commit();
//釋放資源
session.close();
in.close();
}
/**
* 測試查詢所有
* @throws IOException
*/
@Test
public void testFindAll() throws IOException {
//5.執(zhí)行查詢所有方法
List<User> users = iuSerDao.findAll();
}
}
沒有使用主對象的屬性(詳情)時,只執(zhí)行了主對象的查詢。

修改測試方法為
@Test
public void testFindAll() throws IOException {
//5.執(zhí)行查詢所有方法
List<User> users = iuSerDao.findAll();
System.out.println(users.get(0).getUsername());
}
在執(zhí)行主對象屬性(詳情)的時候,執(zhí)行關聯(lián)對象:

5. 深度延遲加載
主配置文件SqlMapConfig.xml中設置lazyLoadingEnabled為true,默認為false,并且將aggressiveLazyLoading設置為false。(aggressiveLazyLoading默認值為false (在 3.4.1 及之前的版本中默認為 true))
<?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>
<!-- 配置properties -->
<properties resource="jdbcConfig.properties"></properties>
<!-- 配置參數(shù) -->
<settings>
<!-- 開啟mybatis支持延遲加載 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!-- 使用typeAliases配置別名,它只能配置domain中類的別名 -->
<typeAliases>
<package name="com.siyi.domain"></package>
</typeAliases>
<!-- 配置環(huán)境 -->
<environments default="mysql">
<!-- 配置mysql的環(huán)境 -->
<environment id="mysql">
<!-- 配置事務 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置連接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 映射配置文件 -->
<mappers>
<mapper resource="com/siyi/dao/IUserDao.xml"/>
<mapper resource="com/siyi/dao/IAccountDao.xml"/>
</mappers>
</configuration>
dao映射配置文件不變
測試
package com.siyi.test;
import com.siyi.dao.IUSerDao;
import com.siyi.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserTest {
private InputStream in;
private SqlSession session;
private IUSerDao iuSerDao;
@Before//用于在測試方法執(zhí)行之前執(zhí)行
public void init() throws IOException {
//1.讀取配置文件生成字節(jié)輸入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.獲取SqlSessionFactory對象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.獲取SqlSession對象
session = factory.openSession(true);
//4.獲取DAO的代理對象
iuSerDao = session.getMapper(IUSerDao.class);
}
@After//用于在測試方法之后執(zhí)行
public void destroy() throws IOException {
//事務提交
//session.commit();
//釋放資源
session.close();
in.close();
}
/**
* 測試查詢所有
* @throws IOException
*/
@Test
public void testFindAll() throws IOException {
//5.執(zhí)行查詢所有方法
List<User> users = iuSerDao.findAll();
System.out.println(users.get(0).getUsername());
}
}
沒有用到關聯(lián)對象時,只查詢主對象一次

修改測試方法
@Test
public void testFindAll() throws IOException {
//5.執(zhí)行查詢所有方法
List<User> users = iuSerDao.findAll();
System.out.println(users.get(0).getAccounts());
}
用到關聯(lián)對象數(shù)據的時候執(zhí)行關聯(lián)對象的查詢。

6. 延遲加載總結
延遲加載一般就是按需加載,在需要查詢的時候再去查詢,使用延遲加載可以避免表連接查詢,表連接查詢比單表查詢的效率低,但是它需要多次與數(shù)據庫進行交互,所以延遲加載使用需謹慎。
關于延遲加載有兩個重要的設置:lazyLoadingEnabled表示延遲加載是否開啟,如果設置為true表示開啟,此時還需要設置aggressiveLazyLoading為false,才能做到按需加載,如果aggressiveLazyLoading設置為true則按需加載關閉,此時只要加載了某個屬性就會將所有屬性都加載。
lazyLoadingEnabled的默認值為false
aggressiveLazyLoading的默認值為true
注意: 延遲加載的應用要求,關聯(lián)對象的查詢與主加載對象的查詢必須是分別進行的 select 語句,不能是使用多表連接所進行的select查詢。
到此這篇關于一文看懂Mybatis中的延遲加載的文章就介紹到這了,更多相關Mybatis延遲加載內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
java后臺本地文件轉為MultipartFile類型的實現(xiàn)方式
在Java后臺將本地文件轉換為MultipartFile類型,可以通過使用FileItemFactory創(chuàng)建FileItem,然后使用CommonsMultipartFile類構造一個MultipartFile對象,將本地文件流轉換為MultipartFile,getMultipartFiles()和getMultipartFiles()方法2025-02-02
WebDriver中實現(xiàn)對特定的Web區(qū)域截圖方法
這篇文章主要介紹了WebDriver中實現(xiàn)對特定的Web區(qū)域截圖方法,本文直接給出實現(xiàn)代碼,需要的朋友可以參考下2015-06-06
mybatis collection關聯(lián)查詢多個參數(shù)方式
在使用MyBatis進行關聯(lián)查詢時,往往需要根據多個參數(shù)進行查詢,例如,使用evtId和businessType作為查詢條件,同時在resultMap中配置id和businessType1作為結果映射,這種情況下,可以通過<sql>標簽定義參數(shù)模板,或者使用@Param注解指定參數(shù)名稱2024-10-10
Open-Feign整合hystrix降級熔斷實戰(zhàn)記錄
這篇文章主要介紹了Open-Feign整合hystrix降級熔斷實戰(zhàn)記錄,本文通過實例圖文相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09
詳解SpringMVC @RequestBody接收Json對象字符串
這篇文章主要介紹了詳解SpringMVC @RequestBody接收Json對象字符串,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01

