Mybatis查詢用戶權限之通過association、collection一次性查出樹形結構

前言

現在項目中一直在用Mybatis Plus框架,其中已經封裝好了大部分的CRUD代碼,還有很方便的條件構造器。雖然只是針對的單表操作,但也習慣了在Java代碼中遍歷組裝數據,其實通過mybatis可以一次性、按結構查出我們需要的數據來的,下面來看看怎麼做吧。

在項目中,某些實體類之間肯定有關鍵關係,比如一對一,一對多等。在hibernate 中用one to oneone to many,而mybatis 中就用associationcollection
association: 一對一關聯(has one)
collection:一對多關聯(has many)
注意,只有在做select查詢時纔會用到這兩個標籤。

已知數據庫資源表數據結構如下:

TIM截圖20200522143217

每個主菜單下,包含了多級子菜單,通過parentId關聯,這樣就構成了一個樹形結構。這也是大部分項目中默認架構設計。

比如同時有Menu.java和Meta.java兩個類

資源表實體Menu如下:

package com.junya.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import com.junya.entity.domain.Meta;
import lombok.Data;
import org.apache.catalina.LifecycleState;

import java.io.Serializable;
import java.util.List;

/**
 * <p>
 * 權限資源表實體類
 * </p>
 *
 * @author zhangchao
 * @since 2020-05-21
 */
@Data
@TableName("menu")
public class Menu extends Model<Menu> {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    @TableField("url")
    private String url;

    @TableField("path")
    private String path;

    @TableField("component")
    private String component;

    @TableField("name")
    private String name;

    @TableField("iconCls")
    private String iconCls;

    @TableField("parentId")
    private Integer parentId;

    @TableField("enabled")
    private Boolean enabled;

    @TableField(exist = false)
    private Meta meta;

    @TableField(exist = false)
    private List<Menu> children;

}

children集合是存放此菜單下的所有子菜單數據。

Meta類裏表示此資源的兩個屬性:是否存活、是否需要權限訪問,如下:

package com.junya.entity.domain;

import lombok.Data;

/**
 * @author ZHANGCHAO
 * @date 2020/5/22 10:54
 * @since 1.0.0
 */
@Data
public class Meta {
    private Boolean keepAlive;
    private Boolean requireAuth;
}

在映射Meta屬性時用association標籤, 映射children時用collection標籤.

所以association是用於一對一和多對一,而collection是用於一對多的關係

MeunMapper.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="com.junya.mapper.MenuMapper">

    <!-- 通用查詢映射結果 -->
    <resultMap id="BaseResultMap" type="com.junya.entity.Menu">
        <id column="id" property="id"/>
        <result column="url" property="url"/>
        <result column="path" property="path"/>
        <result column="component" property="component"/>
        <result column="name" property="name"/>
        <result column="iconCls" property="iconCls"/>
        <result column="parentId" property="parentId"/>
        <result column="enabled" property="enabled"/>
        <!--assocication可以指定聯合的JavaBean對象, property="role"指定哪個屬性是聯合的對象, javaType:指定這個屬性對象的類型-->
        <association property="meta" javaType="com.junya.entity.domain.Meta">
            <result column="keepAlive" property="keepAlive"/>
            <result column="requireAuth" property="requireAuth"/>
        </association>
    </resultMap>
    <resultMap id="BaseResultMap2" type="com.junya.entity.Menu" extends="BaseResultMap">
        <collection property="children" ofType="com.junya.entity.Menu">
            <id column="id2" property="id"/>
            <result column="url2" property="url"/>
            <result column="path2" property="path"/>
            <result column="component2" property="component"/>
            <result column="name2" property="name"/>
            <result column="iconCls2" property="iconCls"/>
            <result column="parentId2" property="parentId"/>
            <result column="enabled2" property="enabled"/>
            <association property="meta" javaType="com.junya.entity.domain.Meta">
                <result column="keepAlive2" property="keepAlive"/>
                <result column="requireAuth2" property="requireAuth"/>
            </association>
        </collection>
    </resultMap>

    <!-- 通用查詢結果列 -->
    <sql id="Base_Column_List">
        id, url, path, component, name, iconCls, keepAlive, requireAuth, parentId, enabled
    </sql>

    <select id="getMenusByHrId" resultMap="BaseResultMap2">
        SELECT DISTINCT
            m1.* ,
            m2.id id2,
            m2.component component2,
            m2.enabled enabled2,
            m2.iconCls iconCls2,
            m2.keepAlive keepAlive2,
            m2.requireAuth requireAuth2,
            m2.name name2,
            m2.parentId parentId2,
            m2.path path2
        FROM
            menu m1
            LEFT JOIN menu m2 ON m1.id = m2.parentId
            LEFT JOIN menu_role mr ON mr.mid = m2.id
            LEFT JOIN hr_role hrr ON mr.rid = hrr.rid
        WHERE
            hrr.hrid = #{hrid}
            AND m2.enabled = TRUE
        ORDER BY
            m1.id,
            m2.id
    </select>

</mapper>

查詢出的數據結構如下:

TIM截圖20200522152600

TIM截圖20200522152622

TIM截圖20200522152637

總結
  1. association表示的是has one的關係,一對一時使用。menu has one meta,所以在Menu的resultMap中接收Meta時應該用association;
  2. collection表示的是has many的關係,一對多時使用。menu has many chirdren,所以在Menu的resultMap中接收children時應該用collection 。
  3. 特別注意表中主鍵字段要有所區分,不能都寫成id,比如要寫成user_id、menu_id,反正要有所區分,不然查詢的時候會查不到完整的數據。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章