1. 程式人生 > >MyBatis從入門到精通(十二):使用collection標籤實現巢狀查詢

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

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸!

本篇部落格主要講解使用collection標籤實現巢狀查詢的方法。

1. 需求升級

在上篇部落格中,我們實現了需求:根據使用者id查詢使用者資訊的同時獲取使用者擁有的角色。

因為角色可以擁有多個許可權,所以本篇部落格我們升級需求為:根據使用者id查詢使用者資訊的同時獲取使用者擁有的角色以及角色包含的許可權。

2. 實現方式

因為我們需要使用到許可權表的對映,所以我們需要先在SysPrivilegeMapper.xml中新增如下對映:

<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我們在之前的部落格中已經定義過,程式碼如下:

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

然後,需要將上篇部落格中的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, [email protected], 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, [email protected], 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, [email protected], 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, [email protected], 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, [email protected], 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, [email protected], 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, [email protected], 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, [email protected], 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. 原始碼及參考

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

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

7. 最後

打個小廣告,歡迎掃碼關注微信公眾號:「申城異鄉人」,不定期分享Java技術乾貨,讓我們一起進步。

相關推薦

MyBatis入門精通()使用collection標籤實現查詢

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解使用collection標籤實現巢狀查詢的方法。 1. 需求升級 在上篇部落格中,我們實現了需求:根據使用者id查詢使用者資訊的同時獲取使用

MyBatis入門精通()使用association標籤實現查詢

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解使用association標籤實現巢狀查詢的方法。 1. 明確需求 仍然延用上篇部落格中的需求:根據使用者id查詢使用者資訊的同時獲取該使用

【筆記】Mybatis高階查詢(三)--使用<association>標籤實現查詢及延遲載入

<association>標籤實現巢狀查詢,需要用到以下屬性: select:另一個對映查詢的ID,Mybatis會額外執行這個查詢獲取巢狀物件的結果。 column:列名或別名,將主查詢中列的結果作為巢狀查詢的引數,配置方式如column=

NDK開發 入門到放棄(動態註冊JNI、多JNI呼叫)

一、前言 上一篇我們講了NDK開發的最簡單的一個入門流程,且寫了一個例項。例項中java的native方法與C/C++程式碼函式是通過Java_<包名>_<類名>_<方法名>這種方式對應的,稱為靜態註冊。在上一篇的例子中,我

Spark 入門再看Spark中的排程策略(Standlone)

資源排程是Spark中比較重要的內容,對排程的相關原理以及策略的瞭解對叢集的執行以及優化都會有很大的幫助,資源排程的方式有多種,Local,Standlone,Yarn,Mesos等,本文只針對Standlone的方式做簡介 幾個重要的概念 開始文章之前

TensorFlow()使用RNN實現手寫數字識別

rop mea pre rnn ext ini tro truncate tutorial 上代碼: import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data #

SpringBoot專案開發()SpringBoot MongoDB實現副本集(讀寫分離)

1.MongoDB可以單庫、主從(官方現已不推薦)、副本集、分片加副本集,通過分片加副本集可以組成龐大的資料儲存叢集,而大部分專案使用副本集就能滿足需求了 2.MongoDB副本集(Replica Set):有自動故障恢復功能的主從叢集,有一個Primary節點和一個或多個Secon

練習三用python實現:按相反的順序輸出列表的每一位值

用python實現:按相反的順序輸出列表的每一位值 1. 使用list[::-1] 1 list1 = ["one","two","three","four"] 2 for i in list1[::-1]:#list[::-1]結果為列表的反向 3 print(i) 2. 使用l

[TensorFlow深度學習入門]實戰·使用DNN網路實現自動編碼器

[TensorFlow深度學習入門]實戰十二·使用DNN網路實現自動編碼器 測試程式碼 import os os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE" import tensorflow as tf from tensorflow

Tensorflow深度學習之AlexNet的實現(CIFAR-10資料集)

二、工程結構 由於我自己訓練的機器記憶體視訊記憶體不足,不能一次性讀取10000張圖片,因此,在這之前我按照圖片的類別,將每一張圖片都提取了出來,儲存成了jpg格式。與此同時,在儲存圖片的過程中,儲存了一個python的dict結構,鍵為每一張圖片的相對地

【筆記】Mybatis高階查詢(五)--使用resultMap的<collection>進行查詢及延遲載入

下面例子通過<collection>實現一個通過使用者編號查詢使用者下面的角色及許可權的需求,支援延遲載入。下面以自下而上的過程來實現這樣的巢狀查詢功能。並且這個自下而上的過程中每一個方法都是獨立可用的方法。上層的結果都以下層方法為基礎。所有物件都設定為延遲載入。

mybatis和<collection>標籤使用

mybatis中和標籤的巢狀使用 實現功能 為了解決A實體類的其中一個私有屬性b,對應的是另一個實體類B的物件,查詢A的所有資訊; 需求   通過mybatis框架:查詢年級表下的所有班級的詳細資

mybatis 使用collection標籤實現一對多查詢(多分頁使用)

在使用**ListPage分頁工具類進行分頁操作時,如果使用一對多查詢,會造成查詢出來每一頁資料數量不等於實際規定的每一個數據數量。原因在於,一對多查詢的結果是包含了與子錶鏈接的資料,例如在查詢10個訂單(order_header)資料時,假如有兩個訂單均包含2條訂單條目(

mybatiscollection一對多查詢

mybatis的collection對映 剛才在csdn上提了一個問題 三張表: create table stu( sno int auto_increment primary key , sname varchar(10) ); cre

MyBatis入門精通()MyBatis XML方式的基本用法之Select

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 1. 明確需求 書中提到的需求是一個基於角色的許可權控制需求(RBAC,即Role-Based Access Control),提到許可權管理,相信大家都不陌

MyBatis入門精通(一)MyBatis高階結果對映之一對多對映

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解MyBatis中如何使用collection標籤實現查詢結果一對多對映。 1. 使用collection標籤 需求:根據使用者id查詢使用者

MyBatis入門精通(四)MyBatis中使用型別處理器

最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸! 本篇部落格主要講解在MyBatis中如何使用型別處理器。 1. 明確需求 在設計之初,sys_role表的enabled欄位有2個可選值,其中0 代表禁用,1

Mybatis入門精通》讀書筆記(

第三章. Mybatis 註解方式的基本用法 表字段和Java屬性欄位對映的方式: 1. SQL語句中列取別名 2. 設定mapUnderscoreToCamelCase = true(下劃線轉駝峰) 3. resultMap對映 註解方式應用場景不多,不做過多

MyBatis入門精通第一章配置MyBatis

從入門到精通 sim efault 入門到 eve lse version 例如 div <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC

MyBatis入門精通第一章實體類與Mapper.xml文件

1.0 style 返回值 spa map 命名 定義 當前 入門到精通 實體類: package tk.mybatis.simple.model; public class Country { public Long getId() {