This blog post focuses on implementing nested queries using the Collection tag.

1. Upgrade requirements

In the last blog post, we implemented the requirement to retrieve the roles owned by the user while querying the user’s information based on the user ID.

Because a role can have multiple permissions, we need to upgrade this blog to query the user information according to the user ID and obtain the role owned by the user and the permissions contained by the role.

2. Implementation method

Since we need to use the mapping to the permission table, we need to add the following mapping to sysprivilegemapper.xml first:

<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>
Copy the code

It is generally not recommended to modify the entity class corresponding to the database table, so we create a new class SysRoleExtend that extends from SysRole and add the following fields:

package com.zwwhnly.mybatisaction.model;

import java.util.List;

public class SysRoleExtend extends SysRole {
    /** * List of permissions contained in roles */
    private List<SysPrivilege> sysPrivilegeList;

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

    public void setSysPrivilegeList(List<SysPrivilege> sysPrivilegeList) {
        this.sysPrivilegeList = sysPrivilegeList; }}Copy the code

Then create the following mapping in 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>
Copy the code

The roleMap we defined in a previous blog is as follows:

<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>
Copy the code

. Com. Zwwhnly. Mybatisaction mapper. SysPrivilegeMapper. SysPrivilegeMap is. We just in SysPrivilegeMapper sysPrivilegeMap new mapping XML.

Then, you need to change the userRoleListMap from the previous blog to:

<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>
Copy the code

And change the select tag code from the previous blog with the id selectAllUserAndRoles, because you want to associate the role permission relationship table with the permission table:

<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>
Copy the code

Matters needing attention:

Here, the column names of the sys_privilege table are aliases prefixed with ROle_privilege_ because the columnPrefix attribute of the Collection in the userRoleListMap is role_. And the specified com. Zwwhnly. Mybatisaction. Mapper. SysRoleMapper. Collection columnPrefix attributes for privilege_ rolePrivilegeListMap, So the prefix here needs to be superimposed, so it becomes role_privilege_.

Unit testing

Modify the last blog build test method testSelectAllUserAndRoles () code is:

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

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

        List<SysUserExtend> sysUserList = sysUserMapper.selectAllUserAndRoles();
        System.out.println("User number:" + sysUserList.size());
        for (SysUserExtend sysUser : sysUserList) {
            System.out.println("Username:" + sysUser.getUserName());
            for (SysRoleExtend sysRoleExtend : sysUser.getSysRoleList()) {
                System.out.println("Character name:" + sysRoleExtend.getRoleName());
                for (SysPrivilege sysPrivilege : sysRoleExtend.getSysPrivilegeList()) {
                    System.out.println("Permission name:"+ sysPrivilege.getPrivilegeName()); }}}}finally{ sqlSession.close(); }}Copy the code

Run the test code, the test passes, the output log is as follows:

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, 2019-06-27 18:21:12.0, 1, /users

TRACE [main] – <== Row: 1, admin, 123456, [email protected], 2019-06-27 18:21:07.00, 1, administrator, 1, 1, 2019-06-27 18:21:12.00, 2, Roles management, /

TRACE [main] – <== Row: 1, admin, 123456, [email protected], 2019-06-27 18:21:07.0, 1, administrator, 1, 2019-06-27 18:21:12.0, 3, system log, /logs

TRACE [main] – <== Row: 1, admin, 123456, [email protected], 2019-06-27 18:21:07.0, 2, 1, 2019-06-27 18:21:12.0, 4, personnel maintenance, /persons

TRACE [main] – <== Row: 1, admin, 123456, [email protected], 2019-06-27 18:21:07.0, 2, general user, 1, 1, 2019-06-27 18:21:12.0, 5, unit maintenance, /companies

TRACE [main] – <== Row: 1001, test, 123456, [email protected], 2019-06-27 18:21:07.0, 2, 1, 1, 2, /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, unit maintenance, /companies

DEBUG [main] – <== Total: 7

Number of users: 2

User name: admin

Role name: Administrator

Permission name: User Management

Permission name: Role management

Permission name: system log

Role name: Common user

Permission Name: Personnel maintenance

Permission name: Unit maintenance

User name: test

Role name: Common user

Permission Name: Personnel maintenance

Permission name: Unit maintenance

The logs show that not only the roles owned by users but also the permissions of roles are queried.

4. Lazy loading

If I use the sysRoleList attribute, I can query the database only when I use the sysRoleList attribute. If I use the sysRoleList attribute, I can query the database again. The answer is yes, of course, but how?

Lazy loading requires the fetchType attribute of the Collection tag, which has lazy and eager values for lazy and active loading, respectively.

To obtain all permissions of the role based on the role Id, define the following query in 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>
Copy the code

Then add the following query to 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>
Copy the code
<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>
Copy the code

In the column=”{roleId=id}” column above, roleId refers to parameters of the select privilegebyroleid method. Id refers to the roleId queried in the selectRoleByUserId query.

Then add the following query to 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>
Copy the code
<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>
Copy the code

The above column = “= {userId id}”, userId refers to the select the methods specified selectRoleByUserId parameters, id refers to the query in the query selectAllUserAndRolesSelect user id.

Then add the following method to the SysUserMapper interface:

/** * Obtain information about the specified user and the user's role and permission through nested query **@param id
 * @return* /
SysUserExtend selectAllUserAndRolesSelect(Long id);
Copy the code

Finally, add the following test methods to the SysUserMapperTest class:

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

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

        SysUserExtend sysUserExtend = sysUserMapper.selectAllUserAndRolesSelect(1L);
        System.out.println("Username:" + sysUserExtend.getUserName());
        for (SysRoleExtend sysRoleExtend : sysUserExtend.getSysRoleList()) {
            System.out.println("Character name:" + sysRoleExtend.getRoleName());
            for (SysPrivilege sysPrivilege : sysRoleExtend.getSysPrivilegeList()) {
                System.out.println("Permission name:"+ sysPrivilege.getPrivilegeName()); }}}finally{ sqlSession.close(); }}Copy the code

Run the test method and output the following log:

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

User name: 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

Role name: Administrator

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, admin, /users

TRACE [main] – <== Row: 2, Roles, /roles

TRACE [main] – <== Row: 3, system logs, /logs

DEBUG [main] – <== Total: 3

Permission name: User Management

Permission name: Role management

Permission name: system log

Role name: Common user

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, unit maintenance, /companies

DEBUG [main] – <== Total: 2

Permission Name: Personnel maintenance

Permission name: Unit maintenance

Careful analysis of the above logs shows that the database query was executed only when role and permission information was used.

Note that lazy loading relies on the introduction to the Association tag in MyBatis global configuration, which we configured to false in our previous blog about the Introduction to the Association tag, so this does exactly what we expected:

<settings>
    <! -- Other configuration -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
Copy the code

For a detailed explanation of this parameter, please see MyBatis from Getting started to Mastering (10) : Implementing nested queries with association tags.

5. To summarize

Using the Collection tag to implement nested queries, the attributes used are summarized as follows:

1) SELECT: the ID of another mapping query that MyBatis will execute to get the result of the nested object.

2)column: Use the results of the columns in the main query as parameters of the nested query, as column=”{prop1=col1,prop2=col2}”, and prop1 and prop2 as parameters of the nested query.

3)fetchType: data loading mode. The optional values are lazy and eager, which are lazy and active loading respectively.

4) In addition to setting fetchType to lazy, note that the slogs in the slogs program globally should have a value of false. This parameter defaults to true prior to version 3.4.5, and to false starting with version 3.4.5.

5) The lazyLoadTriggerMethods argument provided by MyBatis can trigger lazy-loaded attribute queries directly when a method is triggered, such as equals().

6. Source code and reference

Source code address: github.com/zwwhnly/myb… Welcome to download.

MyBatis from Entry to Mastery by Liu Zenghui