MyBatis從入門到精通(十):使用association標籤實現嵌套查詢

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,頗有收穫,因而將本身學習的過程以博客形式輸出,若有錯誤,歡迎指正,如幫助到你,不勝榮幸!java

本篇博客主要講解使用association標籤實現嵌套查詢的方法。git

1. 明確需求

仍然延用上篇博客中的需求:根據用戶id查詢用戶信息的同時獲取該用戶的角色信息(假設一個員工只能擁有一個角色)。github

在上篇博客中,咱們分別使用了3種方式來實現這個需求,但這3個需求都有一個共同點,就是咱們使用了多表查詢,即查詢一次數據庫就獲取到咱們想要的全部數據。sql

有的同窗就說了,我不喜歡多表查詢的方式,數據量大的時候會影響性能,這麼簡單的需求,我能夠拆分紅兩步啊,第一步,先根據用戶id查詢出用戶的信息和用戶的角色id(仍然要關聯表,只是由3張表關聯減爲了2張表關聯),第二步,根據第一步查詢出的角色id再去查詢角色信息。數據庫

這種方式固然能夠,並且使用業務代碼就能實現這個邏輯,不過本篇博客咱們不講這種方式,而是經過association標籤來實現。mybatis

2. 實現方式

由於咱們須要根據角色id查詢角色的信息,因此咱們須要先在SysRoleMapper.xml中添加以下查詢:app

<select id="selectRoleById" resultMap="roleMap">
    SELECT * FROM sys_role WHERE id = #{id}
</select>

這裏的roleMap就是咱們在上篇博客中定義的,代碼以下:性能

<resultMap id="roleMap" type="com.zwwhnly.mybatisaction.model.SysRole">
    <id property="id" column="id"/>
    <result property="roleName" column="role_name"/>
    <result property="enabled" column="enabled"/>
    <result property="createBy" column="create_by"/>
    <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>

而後,在接口SysUserMapper中添加以下方法:單元測試

/**
 * 根據用戶id獲取用戶信息和用戶的角色信息,嵌套查詢方式
 *
 * @param id
 * @return
 */
SysUserExtend selectUserAndRoleByIdSelect(Long id);

接着,在對應的SysUserMapper.xml中添加以下代碼:學習

<resultMap id="userRoleMapSelect" type="com.zwwhnly.mybatisaction.model.SysUserExtend" extends="sysUserMap">
    <association property="sysRole"
                 select="com.zwwhnly.mybatisaction.mapper.SysRoleMapper.selectRoleById"
                 column="{id=role_id}"/>
</resultMap>
<select id="selectUserAndRoleByIdSelect" resultMap="userRoleMapSelect">
    SELECT  u.id,
            u.user_name,
            u.user_password,
            u.user_email,
            u.user_info,
            u.head_img,
            u.create_time,
            ur.role_id
    FROM sys_user u
    INNER JOIN sys_user_role ur ON u.id = ur.user_id
    WHERE u.id = #{id}
</select>

能夠發現,咱們給association標籤添加了select="com.zwwhnly.mybatisaction.mapper.SysRoleMapper.selectRoleById",引用的就是咱們在SysRoleMapper.xml中定義的查詢。

還添加了column="{id=role_id}",這裏的id就是com.zwwhnly.mybatisaction.mapper.SysRoleMapper.selectRoleById須要的參數名稱,role_id是參數值,名稱要和上面的查詢中的最後一列保持一致。

注意事項:若是是多個參數的話,可使用column="{id=role_id,name=role_name}"這樣的格式。

3. 單元測試

在SysUserMapperTest類中添加測試方法以下:

@Test
public void testSelectUserAndRoleByIdSelect() {
    SqlSession sqlSession = getSqlSession();

    try {
        SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);

        SysUserExtend sysUserExtend = sysUserMapper.selectUserAndRoleByIdSelect(1001L);
        Assert.assertNotNull(sysUserExtend);

        Assert.assertNotNull(sysUserExtend.getSysRole());
    } finally {
        sqlSession.close();
    }
}

運行測試代碼,測試經過,輸出日誌以下:

DEBUG [main] - ==> Preparing: SELECT u.id, u.user_name, u.user_password, u.user_email, u.create_time, ur.role_id FROM sys_user u INNER JOIN sys_user_role ur ON u.id = ur.user_id WHERE u.id = ?

DEBUG [main] - ==> Parameters: 1001(Long)

TRACE [main] - <== Columns: id, user_name, user_password, user_email, create_time, role_id

TRACE [main] - <== Row: 1001, test, 123456, test@mybatis.tk, 2019-06-27 18:21:07.0, 2

DEBUG [main] - ====> Preparing: SELECT * FROM sys_role WHERE id = ?

DEBUG [main] - ====> Parameters: 2(Long)

TRACE [main] - <==== Columns: id, role_name, enabled, create_by, create_time

TRACE [main] - <==== Row: 2, 普通用戶, 1, 1, 2019-06-27 18:21:12.0

DEBUG [main] - <==== Total: 1

DEBUG [main] - <== Total: 1

從日誌能夠看出,分別執行了2次查詢,查詢了兩次數據庫。

4. 延遲加載

有的同窗可能會說,返回的角色信息我不必定用啊,每次都查詢一次數據庫,好浪費性能啊,能不能在我使用到角色信息即獲取sysRole屬性時再去查詢數據呢?答案固然是能,那麼如何實現呢?

實現延遲加載須要使用association標籤的fetchType屬性,該屬性有lazy和eager兩個值,分別表明延遲加載和積極加載。

因此上面的配置就要修改爲:

<association property="sysRole"
             fetchType="lazy"
             select="com.zwwhnly.mybatisaction.mapper.SysRoleMapper.selectRoleById"
             column="{id=role_id}"/>

爲了能看到效果,咱們在測試方法中添加一行輸出語句:

System.out.println("調用sysUserExtend.getSysRole()");
Assert.assertNotNull(sysUserExtend.getSysRole());

再次運行測試方法,發現輸出日誌和預期的不同,在獲取sysRole屬性前仍是查詢了2次數據庫,這是爲何呢?

這是由於MyBatis的全局配置中,有一個aggressiveLazyLoading參數,若是這個參數的值爲ture,會使帶有延遲加載屬性的對象完整加載,若是爲false,則會按需加載,這個參數默認值爲ture(3.4.5版本開始默認值改成false),而截止目前,咱們使用的版本爲3.3.1。

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.3.1</version>
</dependency>

因此咱們要在mybatis-config.xml中添加以下配置:

<settings>
    <!--其餘配置-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

再次運行測試方法,發現輸出日誌和預期的同樣了:

DEBUG [main] - ==> Preparing: SELECT u.id, u.user_name, u.user_password, u.user_email, u.create_time, ur.role_id FROM sys_user u INNER JOIN sys_user_role ur ON u.id = ur.user_id WHERE u.id = ?

DEBUG [main] - ==> Parameters: 1001(Long)

TRACE [main] - <== Columns: id, user_name, user_password, user_email, create_time, role_id

TRACE [main] - <== Row: 1001, test, 123456, test@mybatis.tk, 2019-06-27 18:21:07.0, 2

DEBUG [main] - <== Total: 1

調用sysUserExtend.getSysRole()

DEBUG [main] - ==> Preparing: SELECT * FROM sys_role WHERE id = ?

DEBUG [main] - ==> Parameters: 2(Long)

TRACE [main] - <== Columns: id, role_name, enabled, create_by, create_time

TRACE [main] - <== Row: 2, 普通用戶, 1, 1, 2019-06-27 18:21:12.0

DEBUG [main] - <== Total: 1

有的同窗可能又會說,你如今把全局的aggressiveLazyLoading改成了false,我能不能在觸發某個方法時將全部的數據都加載進來呢?答案固然是能(否則怎麼往下寫,哈哈),那麼如何實現呢?

MyBatis提供了參數lazyLoadTriggerMethods,這個參數的含義是,當調用配置中的方法時,加載所有的延遲加載數據,默認值爲「equals,clone,hashCode,toString」。

簡單修改下測試方法的代碼:

System.out.println("調用sysUserExtend.equals(null)");
sysUserExtend.equals(null);

System.out.println("調用sysUserExtend.getSysRole()");
Assert.assertNotNull(sysUserExtend.getSysRole());

再次運行測試方法,輸出的部分日誌以下:

調用sysUserExtend.equals(null)

DEBUG [main] - ==> Preparing: SELECT * FROM sys_role WHERE id = ?

DEBUG [main] - ==> Parameters: 2(Long)

TRACE [main] - <== Columns: id, role_name, enabled, create_by, create_time

TRACE [main] - <== Row: 2, 普通用戶, 1, 1, 2019-06-27 18:21:12.0

DEBUG [main] - <== Total: 1

調用sysUserExtend.getSysRole()

從日誌能夠看出,調用equals方法後,就觸發了延遲加載屬性的查詢。

5. 總結

使用association標籤實現嵌套查詢,用到的屬性總結以下:

1)select:另外一個映射查詢的id,MyBatis會額外執行這個查詢獲取嵌套對象的結果。

2)column:將主查詢中列的結果做爲嵌套查詢的參數,配置方式如column="{prop1=col1,prop2=col2}",prop1和prop2將做爲嵌套查詢的參數。

3)fetchType:數據加載方式,可選值爲lazy和eager,分別爲延遲加載和積極加載。

4)若是要使用延遲加載,除了將fetchType設置爲lazy,還須要注意全局配置aggressiveLazyLoading的值應該爲false。這個參數在3.4.5版本以前默認值爲ture,從3.4.5版本開始默認值改成false。

5)MyBatis提供的lazyLoadTriggerMethods參數,支持在觸發某方法時直接觸發延遲加載屬性的查詢,如equals()方法。

6. 源碼及參考

源碼地址:https://github.com/zwwhnly/mybatis-action.git,歡迎下載。

劉增輝《MyBatis從入門到精通》

相關文章
相關標籤/搜索