MyBatis 延遲加載(懶加載)一篇入門

引言

前面一篇文章,介紹了多表查詢,在實際使用中,咱們會常常性的涉及到多表聯合查詢,可是有時候,並不會當即用到全部的查詢結果,我來舉兩個例子:java

  • 例如,查詢一批筆記本電腦的進貨明細,而不直接展現每列明細對應電腦配置或者價格等的詳細信息,等到用戶須要取出某筆記本相關的詳細信息的時候,再進行單表查詢
  • 再例如 ,銀行中,某個用戶擁有50個帳戶(打比方),再咱們查詢這個而用戶的信息,這個用戶下全部帳戶的詳細信息很顯然,在使用的時候再查詢纔是比較合理的

針對這樣一種狀況,延遲加載這一種機制就出現了,延遲加載(懶加載)顧名思義,就是對某種信息推遲加載,這樣的技術也就幫助咱們實現了 「按需查詢」 的機制,在一對多,或者多對多的狀況下sql

既然提到了延遲加載,固然順便提一句當即加載,它的含義就是不論是否用戶須要,一調用,則立刻查詢,這種方式,適合與多對一,或者一對一的狀況下數據庫

(一) 必要準備

首先,配置基本的環境,而後咱們首先在數據庫準備兩張表微信

User表

CREATE TABLE USER (
 `id`            INT(11)NOT NULL AUTO_INCREMENT,
 `username`     VARCHAR(32) NOT NULL COMMENT '用戶名',
 `telephone`    VARCHAR(11) NOT NULL COMMENT '手機',
 `birthday`        DATETIME DEFAULT NULL COMMENT '生日',
 `gender`          CHAR(1) DEFAULT NULL COMMENT '性別',
 `address`         VARCHAR(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

Account表

CREATE TABLE `account` (
  `ID` int(11) NOT NULL COMMENT '編號',
  `UID` int(11) default NULL COMMENT '用戶編號',
  `MONEY` double default NULL COMMENT '金額',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

而後分別建立出其對應的實體類mybatis

User類

public class User implements Serializable {
    private Integer id;
    private String username;
    private String telephone;
    private Date birthday;
    private String gender;
    private String address;
    //一對多關係映射,主表實體應該包含從表實體的集合引用
    private List<Account> accounts;
    ...... 請補充 get set 和 toString 方法
}

Account類

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    //從表實體應該包含一個主表實體的對象引用
    private User user;
    ...... 請補充 get set 和 toString 方法
}

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">

    <!-- 定義User的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>
    
    <!-- 查詢全部用戶 而且顯示對應帳戶信息 -->
    <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>

    <!-- 根據id查詢用戶 -->
    <select id="findById" parameterType="INT" resultType="User">
        select * from user where id = #{uid}
    </select>
    
</mapper>

兩個接口中建立對應方法

public interface AccountMapper {
    /**
     * 查詢全部帳戶
     * @return
     */
    List<Account> findAll();
}
public interface UserMapper {
    /**
     * 查詢全部用戶信息,同時顯示出該用戶下的全部帳戶
     *
     * @return
     */
    List<User> findAll();

    /**
     * 根據id查詢用戶信息
     * @param userId
     * @return
     */
    User findById(Integer userId);
}

(一) 延遲加載代碼實現

首先,給你們演示一下,咱們以前一對一查詢用戶的方式,同時會將用戶對應全部的帳戶信息,也查詢出來app

/**
* 測試查詢全部
*/
@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());
    }
}

效果:ide

這種方式是經過 SQL 語句,以及resultMap將 用戶和帳戶的信息同時查詢出來測試

那麼如何實現咱們上面所說的延遲加載呢?ui

此次咱們選擇 查詢帳戶,而後延遲加載用戶的信息idea

(1) 修改AccountMapper.xml

首先須要修改的就是帳戶的映射配置文件,能夠看到咱們在查詢時,依舊定義了一個 resultMap 先封裝了 Account ,而後經過association 進行關聯 User,其中使用的就是 select 和 column 實現了延遲加載用戶信息

  • select 用來指定延遲加載所須要執行的 SQL 語句,也就是指定 某個SQL映射文件中的某個select標籤對的 id,在這裏咱們指定了用戶中經過id查詢信息的方法
  • column 是指關聯的用戶信息查詢的列,在這裏也就是關聯的用戶的主鍵即,id
<mapper namespace="cn.ideal.mapper.AccountMapper">
    <!-- 定義封裝 Account和User 的resultMap -->
    <resultMap id="userAccountMap" type="Account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 配置封裝 User 的內容
            select:查詢用戶的惟一標識
            column:用戶根據id查詢的時候,須要的參數值
        -->
        <association property="user" column="uid" javaType="User" select="cn.ideal.mapper.UserMapper.findById"></association>
    </resultMap>

    <!-- 根據查詢全部帳戶 -->
    <select id="findAll" resultMap="userAccountMap">
        SELECT * FROM account
    </select>
</mapper>

(2) 第一次測試代碼

咱們只執行一下帳戶的查詢全部方法,看一下,是否可以實現咱們的效果

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

(3) 執行效果

能夠看到,三條 SQL 語句都執行了,這是爲何呢?

這是由於,咱們在測試方法以前,須要開啓延遲加載功能

(4) 延遲加載功能

咱們能夠去官網,如何配置開啓這樣一個功能

通過查閱文檔,咱們知道了,若是想要開始延遲加載功能,就須要在總配置文件 SqlMapConfig.xml 中配置 setting 屬性,也就是將延遲加載 lazyLoadingEnable 的開關設置成 teue ,因爲是按需加載,因此還須要將積極加載修改成消極加載,也就是將 aggressiveLazyLoading 改成 false

固然,因爲我這裏導入的 MyBatis 版本爲 3.4.5 因此這個值默認就是 false 實際上不用設置也能夠,不過咱們仍是寫出來

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

注意:若是有使用typeAliases配置別名的話必定要將 typeAliases 標籤放在後面

(5) 再次測試

仍然只執行查詢方法

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

執行效果

這一次果真只執行了一條查詢 account 的命令

那麼當用戶想要查看到,每一個帳戶對應下的用戶的時候呢?這也就是按需查詢,只須要在測試時,加入對應獲取方法就能夠了

@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());
    }
}

執行一下

能夠看到,咱們延遲加載的目的達到了

總結

上面的測試,咱們已經實現了延遲加載,簡單的總結一下步驟:

  • ①:執行對應的 mapper 方法,也就是上例中執行 Mapper 中 id 值爲 findAll 的對應 SQL配置,只查詢到帳戶的信息
  • ②:在程序中,遍歷查詢到的 accounts ,調用 getUser() 方法時,開始進行延遲加載

    • List<Account> accounts = accountMapper.findAll();
  • ③:進行延遲加載,調用映射文件中 id 值爲 findById 的對應 SQL配置,獲取到對應用戶的信息

能夠看到,咱們以前經過使用 左外鏈接等的 SQL書寫方式,直接就能夠查詢到多張表

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

可是咱們能夠經過延遲加載,實現咱們按需查詢的需求,綜上所述,在使用的時候,先執行簡單的 SQL,而後再按照需求加載查詢其餘信息

結尾

若是文章中有什麼不足,歡迎你們留言交流,感謝朋友們的支持!

若是能幫到你的話,那就來關注我吧!若是您更喜歡微信文章的閱讀方式,能夠關注個人公衆號

在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤

一個堅持推送原創開發技術文章的公衆號:理想二旬不止

相關文章
相關標籤/搜索