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

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

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

1. 需求升級

在上篇博客中,咱們實現了需求:根據用戶id查詢用戶信息的同時獲取用戶擁有的角色。git

由於角色能夠擁有多個權限,因此本篇博客咱們升級需求爲:根據用戶id查詢用戶信息的同時獲取用戶擁有的角色以及角色包含的權限。github

2. 實現方式

由於咱們須要使用到權限表的映射,因此咱們須要先在SysPrivilegeMapper.xml中添加以下映射:sql

<resultMap id="sysPrivilegeMap" type="com.zwwhnly.mybatisaction.model.SysPrivilege">
    <id property="id" column="id"/>
    <result property="privilegeName" column="privilege_name"/>
    <result property="privilegeUrl" column="privilege_url"/>
</resultMap>
複製代碼

通常狀況下不建議修改數據庫表對應的實體類,因此這裏咱們新建類SysRoleExtend,讓它繼承SysRole類,並添加以下字段:數據庫

package com.zwwhnly.mybatisaction.model;

import java.util.List;

public class SysRoleExtend extends SysRole {
    /** * 角色包含的權限列表 */
    private List<SysPrivilege> sysPrivilegeList;

    public List<SysPrivilege> getSysPrivilegeList() {
        return sysPrivilegeList;
    }

    public void setSysPrivilegeList(List<SysPrivilege> sysPrivilegeList) {
        this.sysPrivilegeList = sysPrivilegeList;
    }
}
複製代碼

而後在SysRoleMapper.xml中新建以下映射:微信

<resultMap id="rolePrivilegeListMap" extends="roleMap" type="com.zwwhnly.mybatisaction.model.SysRoleExtend">
    <collection property="sysPrivilegeList" columnPrefix="privilege_" resultMap="com.zwwhnly.mybatisaction.mapper.SysPrivilegeMapper.sysPrivilegeMap"/>
</resultMap>
複製代碼

這裏的roleMap咱們在以前的博客中已經定義過,代碼以下:mybatis

<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>
複製代碼

com.zwwhnly.mybatisaction.mapper.SysPrivilegeMapper.sysPrivilegeMap就是咱們剛剛在SysPrivilegeMapper.xml中新建的映射sysPrivilegeMap。app

而後,須要將上篇博客中的userRoleListMap修改成:性能

<resultMap id="userRoleListMap" type="com.zwwhnly.mybatisaction.model.SysUserExtend" extends="sysUserMap">
    <collection property="sysRoleList" columnPrefix="role_" resultMap="com.zwwhnly.mybatisaction.mapper.SysRoleMapper.rolePrivilegeListMap">
    </collection>
</resultMap>
複製代碼

而且要修改上篇博客中id爲selectAllUserAndRoles的select標籤代碼,由於要關聯角色權限關係表和權限表:

<select id="selectAllUserAndRoles" resultMap="userRoleListMap">
    SELECT  u.id,
            u.user_name,
            u.user_password,
            u.user_email,
            u.create_time,
            r.id role_id,
            r.role_name role_role_name,
            r.enabled role_enabled,
            r.create_by role_create_by,
            r.create_time role_create_time,
            p.id role_privilege_id,
            p.privilege_name role_privilege_privilege_name,
            p.privilege_url role_privilege_privilege_url
    FROM sys_user u
    INNER JOIN sys_user_role ur ON u.id = ur.user_id
    INNER JOIN sys_role r ON ur.role_id = r.id
    INNER JOIN sys_role_privilege rp ON rp.role_id = r.id
    INNER JOIN sys_privilege p ON p.id = rp.privilege_id
</select>
複製代碼

注意事項:

這裏sys_privilege表的列名的別名前綴爲role_privilege_,這是由於userRoleListMap中collection的columnPrefix屬性爲role_,而且指定的com.zwwhnly.mybatisaction.mapper.SysRoleMapper.rolePrivilegeListMap中collection的columnPrefix屬性爲privilege_,因此這裏的前綴須要疊加,就變成了role_privilege_

3. 單元測試

修改上篇博客中建的測試方法testSelectAllUserAndRoles()代碼爲:

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

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

        List<SysUserExtend> sysUserList = sysUserMapper.selectAllUserAndRoles();
        System.out.println("用戶數:" + sysUserList.size());
        for (SysUserExtend sysUser : sysUserList) {
            System.out.println("用戶名:" + sysUser.getUserName());
            for (SysRoleExtend sysRoleExtend : sysUser.getSysRoleList()) {
                System.out.println("角色名:" + sysRoleExtend.getRoleName());
                for (SysPrivilege sysPrivilege : sysRoleExtend.getSysPrivilegeList()) {
                    System.out.println("權限名:" + sysPrivilege.getPrivilegeName());
                }
            }
        }
    } finally {
        sqlSession.close();
    }
}
複製代碼

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

DEBUG [main] - ==> Preparing: SELECT u.id, u.user_name, u.user_password, u.user_email, u.create_time, r.id role_id, r.role_name role_role_name, r.enabled role_enabled, r.create_by role_create_by, r.create_time role_create_time, p.id role_privilege_id, p.privilege_name role_privilege_privilege_name, p.privilege_url role_privilege_privilege_url FROM sys_user u INNER JOIN sys_user_role ur ON u.id = ur.user_id INNER JOIN sys_role r ON ur.role_id = r.id INNER JOIN sys_role_privilege rp ON rp.role_id = r.id INNER JOIN sys_privilege p ON p.id = rp.privilege_id

DEBUG [main] - ==> Parameters:

TRACE [main] - <== Columns: id, user_name, user_password, user_email, create_time, role_id, role_role_name, role_enabled, role_create_by, role_create_time, role_privilege_id, role_privilege_privilege_name, role_privilege_privilege_url

TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, 2019-06-27 18:21:07.0, 1, 管理員, 1, 1, 2019-06-27 18:21:12.0, 1, 用戶管理, /users

TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, 2019-06-27 18:21:07.0, 1, 管理員, 1, 1, 2019-06-27 18:21:12.0, 2, 角色管理, /roles

TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, 2019-06-27 18:21:07.0, 1, 管理員, 1, 1, 2019-06-27 18:21:12.0, 3, 系統日誌, /logs

TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, 2019-06-27 18:21:07.0, 2, 普通用戶, 1, 1, 2019-06-27 18:21:12.0, 4, 人員維護, /persons

TRACE [main] - <== Row: 1, admin, 123456, admin@mybatis.tk, 2019-06-27 18:21:07.0, 2, 普通用戶, 1, 1, 2019-06-27 18:21:12.0, 5, 單位維護, /companies

TRACE [main] - <== Row: 1001, test, 123456, test@mybatis.tk, 2019-06-27 18:21:07.0, 2, 普通用戶, 1, 1, 2019-06-27 18:21:12.0, 4, 人員維護, /persons

TRACE [main] - <== Row: 1001, test, 123456, test@mybatis.tk, 2019-06-27 18:21:07.0, 2, 普通用戶, 1, 1, 2019-06-27 18:21:12.0, 5, 單位維護, /companies

DEBUG [main] - <== Total: 7

用戶數:2

用戶名:admin

角色名:管理員

權限名:用戶管理

權限名:角色管理

權限名:系統日誌

角色名:普通用戶

權限名:人員維護

權限名:單位維護

用戶名:test

角色名:普通用戶

權限名:人員維護

權限名:單位維護

從日誌能夠看出,不只查詢出了用戶擁有的角色信息,也查詢出了角色包含的權限信息。

4. 延遲加載

有的同窗可能會說,返回的角色信息和權限信息我不必定用啊,每次關聯這麼多表查詢一次數據庫,好影響性能啊,能不能在我使用到角色信息即獲取sysRoleList屬性時再去數據庫查詢呢?答案固然是能,那麼如何實現呢?

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

因爲須要根據角色Id獲取該角色對應的全部權限信息,因此咱們要先在SysPrivilegeMapper.xml中定義以下查詢:

<select id="selectPrivilegeByRoleId" resultMap="sysPrivilegeMap">
    SELECT p.*
    FROM sys_privilege p
    INNER JOIN sys_role_privilege rp ON rp.privilege_id = p.id
    WHERE rp.role_id = #{roleId}
</select>
複製代碼

而後在SysRoleMapper.xml中添加以下查詢:

<resultMap id="rolePrivilegeListMapSelect" extends="roleMap" type="com.zwwhnly.mybatisaction.model.SysRoleExtend">
    <collection property="sysPrivilegeList" fetchType="lazy" column="{roleId=id}" select="com.zwwhnly.mybatisaction.mapper.SysPrivilegeMapper.selectPrivilegeByRoleId"/>
</resultMap>
複製代碼
<select id="selectRoleByUserId" resultMap="rolePrivilegeListMapSelect">
    SELECT
          r.id,
          r.role_name,
          r.enabled,
          r.create_by,
          r.create_time
    FROM sys_role r
    INNER JOIN sys_user_role ur ON ur.role_id = r.id
    WHERE ur.user_id = #{userId}
</select>
複製代碼

上面的column="{roleId=id}"中,roleId指的是select指定的方法selectPrivilegeByRoleId的參數,id指的是查詢selectRoleByUserId中查詢出的角色id。

而後在SysUserMapper.xml中添加以下查詢:

<resultMap id="userRoleListMapSelect" extends="sysUserMap" type="com.zwwhnly.mybatisaction.model.SysUserExtend">
    <collection property="sysRoleList" fetchType="lazy" select="com.zwwhnly.mybatisaction.mapper.SysRoleMapper.selectRoleByUserId" column="{userId=id}"/>
</resultMap>
複製代碼
<select id="selectAllUserAndRolesSelect" resultMap="userRoleListMapSelect">
    SELECT
          u.id,
          u.user_name,
          u.user_password,
          u.user_email,
          u.create_time
    FROM sys_user u
    WHERE u.id = #{id}
</select>
複製代碼

上面的column="{userId=id}"中,userId指的是select指定的方法selectRoleByUserId的參數,id指的是查詢selectAllUserAndRolesSelect中查詢出的用戶id。

而後,在SysUserMapper接口中,添加以下方法:

/** * 經過嵌套查詢獲取指定用戶的信息以及用戶的角色和權限信息 * * @param id * @return */
SysUserExtend selectAllUserAndRolesSelect(Long id);
複製代碼

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

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

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

        SysUserExtend sysUserExtend = sysUserMapper.selectAllUserAndRolesSelect(1L);
        System.out.println("用戶名:" + sysUserExtend.getUserName());
        for (SysRoleExtend sysRoleExtend : sysUserExtend.getSysRoleList()) {
            System.out.println("角色名:" + sysRoleExtend.getRoleName());
            for (SysPrivilege sysPrivilege : sysRoleExtend.getSysPrivilegeList()) {
                System.out.println("權限名:" + sysPrivilege.getPrivilegeName());
            }
        }
    } finally {
        sqlSession.close();
    }
}
複製代碼

運行測試方法,輸出日誌以下:

DEBUG [main] - ==> Preparing: SELECT u.id, u.user_name, u.user_password, u.user_email, u.create_time FROM sys_user u WHERE u.id = ?

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

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

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

DEBUG [main] - <== Total: 1

用戶名:admin

DEBUG [main] - ==> Preparing: SELECT r.id, r.role_name, r.enabled, r.create_by, r.create_time FROM sys_role r INNER JOIN sys_user_role ur ON ur.role_id = r.id WHERE ur.user_id = ?

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

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

TRACE [main] - <== Row: 1, 管理員, 1, 1, 2019-06-27 18:21:12.0

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

DEBUG [main] - <== Total: 2

角色名:管理員

DEBUG [main] - ==> Preparing: SELECT p.* FROM sys_privilege p INNER JOIN sys_role_privilege rp ON rp.privilege_id = p.id WHERE rp.role_id = ?

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

TRACE [main] - <== Columns: id, privilege_name, privilege_url

TRACE [main] - <== Row: 1, 用戶管理, /users

TRACE [main] - <== Row: 2, 角色管理, /roles

TRACE [main] - <== Row: 3, 系統日誌, /logs

DEBUG [main] - <== Total: 3

權限名:用戶管理

權限名:角色管理

權限名:系統日誌

角色名:普通用戶

DEBUG [main] - ==> Preparing: SELECT p.* FROM sys_privilege p INNER JOIN sys_role_privilege rp ON rp.privilege_id = p.id WHERE rp.role_id = ?

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

TRACE [main] - <== Columns: id, privilege_name, privilege_url

TRACE [main] - <== Row: 4, 人員維護, /persons

TRACE [main] - <== Row: 5, 單位維護, /companies

DEBUG [main] - <== Total: 2

權限名:人員維護

權限名:單位維護

仔細分析上面的日誌,會發現只有在使用到了角色信息和權限信息時,才執行了對應的數據庫查詢。

須要注意的是,延遲加載依賴於MyBatis全局配置中的aggressiveLazyLoading,在以前的博客講解association標籤時,咱們已經將其配置爲了false,因此這裏的執行結果符合咱們的預期:

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

關於該參數的詳細講解,請查看MyBatis從入門到精通(十):使用association標籤實現嵌套查詢

5. 總結

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

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. 源碼及參考

源碼地址:github.com/zwwhnly/myb…,歡迎下載。

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

7. 最後

打個小廣告,歡迎掃碼關注微信公衆號:「申城異鄉人」,按期分享Java技術乾貨,讓咱們一塊兒進步。

相關文章
相關標籤/搜索