最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MybatisPlus一對多聯(lián)表查詢及分頁的解決過程

 更新時(shí)間:2025年12月24日 15:35:28   作者:沙糖橘  
文章主要介紹了在使用MyBatis-Plus進(jìn)行一對多查詢時(shí)遇到的分頁問題及解決方案,在一對多場景下,直接映射會導(dǎo)致分頁數(shù)據(jù)數(shù)量不正確,而使用子查詢映射則可以解決這個問題,此外,通過在Java代碼中使用Stream進(jìn)行關(guān)聯(lián)處理,可以進(jìn)一步優(yōu)化查詢性能,避免多次子查詢帶來的效率問題

需求

查詢用戶信息列表,其中包含用戶對應(yīng)角色信息,頁面檢索條件有根據(jù)角色名稱查詢用戶列表;

需求分析

一個用戶對應(yīng)多個角色,用戶信息和角色信息分表根據(jù)用戶id關(guān)聯(lián)存儲,用戶和角色一對多進(jìn)行表連接查詢,

創(chuàng)建對應(yīng)表:

CREATE TABLE `sys_user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用戶ID',
  `name` varchar(50) DEFAULT NULL COMMENT '姓名',
  `age` int DEFAULT NULL COMMENT '年齡',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4  COMMENT='用戶信息表';

CREATE TABLE `sys_role` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色I(xiàn)D',
  `role_name` varchar(30) NOT NULL COMMENT '角色名稱',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4  COMMENT='角色信息表';


CREATE TABLE `sys_user_role` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `user_id` bigint NOT NULL COMMENT '用戶ID',
  `role_id` bigint NOT NULL COMMENT '角色I(xiàn)D',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4  COMMENT='用戶和角色關(guān)聯(lián)表';


INSERT INTO tsq.sys_user (name,age) VALUES
	 ('張三',18),
	 ('王二',19);

INSERT INTO tsq.sys_role (role_name) VALUES
	 ('角色1'),
	 ('角色2'),
	 ('角色3'),
	 ('角色4');

INSERT INTO tsq.sys_user_role (user_id,role_id) VALUES
	 (1,1),
	 (1,2),
	 (1,3),
	 (2,4);


對應(yīng)實(shí)體類:

@Data
@ApiModel("用戶信息表")
@TableName("sys_user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;
    
    @ApiModelProperty("用戶id")
    private Long id;
    @ApiModelProperty("姓名")
    private String name;
    @ApiModelProperty("年齡")
    private Integer age;
}


@Data
@ApiModel("角色信息表")
@TableName("sys_role")
public class Role implements Serializable {

    private static final long serialVersionUID = 1L;
    
    @ApiModelProperty("角色id")
    private Long id;
    @ApiModelProperty("角色名稱")
    private String roleName;
}



@Data
@ApiModel("用戶信息表")
public class UserVo implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty("用戶id")
    private Long id;
    @ApiModelProperty("姓名")
    private String name;
    @ApiModelProperty("年齡")
    private Integer age;
    
    private List<Role> roleList;

}


分頁問題說明

在使用一對多連接查詢并且分頁時(shí),發(fā)現(xiàn)返回的分頁列表數(shù)據(jù)數(shù)量不對

比如這里查詢用戶對應(yīng)角色列表,如果使用直接映射,那么 roleList 的每個 Role 對象都會算一條數(shù)據(jù);比如查第一頁,一個用戶有三個角色每頁三條數(shù)據(jù),就會出現(xiàn)查出一個 User ,三個 Role 的這些情況,這它也算每頁三條(其實(shí)就只查到一個用戶)

分頁問題原因

mybatis-plus一對多分頁時(shí),應(yīng)該使用子查詢的映射方式,使用直接映射就會出錯

所以直接映射適用于一對一,子查詢映射使用于一對多;

一對多場景一

查詢用戶表的內(nèi)容,角色表不參與條件查詢,用懶加載形式

// controller
  @GetMapping("/pageList")
	public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = "0") int offset,
	                                    @RequestParam(required = false, defaultValue = "10") int pagesize) {
		return userService.pageList(offset, pagesize);
	}
  
  
// serviceimpl
  @Override
	public Map<String, Object> pageList(int offset, int pagesize) {
      List<UserVo> pageList = userMapper.pageList(offset, pagesize);
      int totalCount = userMapper.pageListCount();
      Map<String, Object> result = new HashMap<String, Object>();
      result.put("pageList", pageList);
      result.put("totalCount", totalCount);

      return result;
	}
  
// mapper.xml

   <resultMap id="getUserInfo" type="com.tsq.democase.onetomany.domain.vo.UserVo" >
        <result column="id" property="id" />
        <result column="name" property="name" />
        <result column="age" property="age" />
        <collection property="roleList" javaType="ArrayList" ofType="com.tsq.democase.onetomany.domain.Role"
                    select="getRolesByUserId" column="{userId = id}"/>
    </resultMap>

    <select id="getRolesByUserId" resultType="com.tsq.democase.onetomany.domain.Role">
        SELECT *
        FROM sys_user_role ur
                 inner join sys_role r on ur.role_id = r.id
        where ur.user_id = #{userId}
    </select>
    
    <select id="pageList"  resultMap="getUserInfo">
        SELECT *
        FROM sys_user
        LIMIT #{offset}, #{pageSize}
    </select>
    
    <select id="pageListCount" resultType="java.lang.Integer">
        SELECT count(1)
        FROM sys_user
    </select>

查詢結(jié)果

一對多場景二

查詢用戶表的內(nèi)容,角色表要作為查詢條件參與查詢,例如要根據(jù)角色名稱查詢出用戶列表

// controller
	@GetMapping("/pageListByRoleName")
	public Map<String, Object> pageListByRoleName(@RequestParam(required = false, defaultValue = "0") int offset,
	                                    @RequestParam(required = false, defaultValue = "10") int pagesize,
	                                    @RequestParam String roleName) {
		return userService.pageListByRoleName(offset, pagesize, roleName);
	}
  
  
// serviceimpl
	@Override
	public Map<String, Object> pageListByRoleName(int offset, int pagesize,String roleName) {
      List<UserVo> pageList = userMapper.pageListByRoleName(offset, pagesize, roleName);
      int totalCount = userMapper.pageListCount();
      Map<String, Object> result = new HashMap<String, Object>();
      result.put("pageList", pageList);
      result.put("totalCount", totalCount);

      return result;
	}
  
// mapper.xml
   <resultMap id="getUserInfoByRoleName" type="com.tsq.democase.onetomany.domain.vo.UserVo" >
        <result column="id" property="id" />
        <result column="name" property="name" />
        <result column="age" property="age" />
        <collection property="roleList" javaType="ArrayList" ofType="com.tsq.democase.onetomany.domain.Role"
                    select="getRolesByUserIdAndRoleName" column="{userId = id,roleName = roleName}"/>
    </resultMap>

    <select id="getRolesByUserIdAndRoleName" resultType="com.tsq.democase.onetomany.domain.Role">
        SELECT *
        FROM sys_user_role ur
        inner join sys_role r on ur.role_id = r.id
        where ur.user_id = #{userId}
        <if test="roleName != null and roleName != ''" >
            and r.role_name LIKE concat('%', #{roleName}, '%')
        </if>
    </select>

    <select id="pageListByRoleName"  resultMap="getUserInfoByRoleName">
        SELECT temp.* FROM (
        SELECT distinct u.*,#{roleName} as roleName
        FROM sys_user u
        left join sys_user_role ur on u.id = ur.user_id
        left join sys_role r on r.id = ur.role_id
        <where>
            <if test="roleName != null and roleName != ''" >
                r.role_name LIKE concat('%', #{roleName}, '%')
            </if>
        </where>
        ) temp
        LIMIT #{offset}, #{pageSize}
    </select>

查詢結(jié)果

性能優(yōu)化

原因:

場景一二中使用 select方式會觸發(fā)多次子查詢(SELECT *FROM sys_user_role ur inner join sys_role …),當(dāng)數(shù)據(jù)量大時(shí)會使查詢速度很慢。

場景二中查詢時(shí)產(chǎn)生的sql日志如下:

--  ==>  
SELECT
    temp.* 
FROM
    ( SELECT
        distinct u.*,
        '角色' as roleName 
    FROM
        sys_user u 
    left join
        sys_user_role ur 
            on u.id = ur.user_id 
    left join
        sys_role r 
            on r.id = ur.role_id 
    WHERE
        r.role_name LIKE concat('%', '角色', '%') ) temp LIMIT 0,
    10 
 --  ====>  
SELECT
    * 
FROM
    sys_user_role ur 
inner join
    sys_role r 
        on ur.role_id = r.id 
where
    ur.user_id = 1 
    and r.role_name LIKE concat('%', '角色', '%') 
 --  ====>  
SELECT
    * 
FROM
    sys_user_role ur 
inner join
    sys_role r 
        on ur.role_id = r.id 
where
    ur.user_id = 2 
    and r.role_name LIKE concat('%', '角色', '%') 
 --  ==>  
SELECT
    count(1) 
FROM
    sys_user 

sql可見如果有100各用戶就要執(zhí)行一百次子查詢,效率極低。

優(yōu)化解決方案

sql中只查詢sys_user相關(guān)信息并且做roleName 過濾,roleList在java代碼中用stream關(guān)聯(lián)role并賦值roleList;

// serviceimpl
  @Override
  public Map<String, Object> pageListByRoleName(int offset, int pagesize,String roleName) {
     // List<UserVo> pageList = userMapper.pageListByRoleName(offset, pagesize, roleName);
     List<UserVo> pageList = userMapper.pageListByRoleName2(offset, pagesize, roleName);
     List<Long> userIds = pageList.stream().map(UserVo::getId).collect(Collectors.toList());
     List<UserRoleVo> userRoleVos =  userMapper.getUserRoleByUserIds(userIds);
     Map<Long, List<UserRoleVo>> userRoleMap = userRoleVos.stream().collect(Collectors.groupingBy(UserRoleVo::getUserId, Collectors.toList()));
     pageList.forEach(u -> {
        List<UserRoleVo> roleVos = userRoleMap.get(u.getId());
        List<RoleVo> roles = BeanUtils.listCopy(roleVos, CopyOptions.create(), RoleVo.class);
        u.setRoleList(roles);
     });
     int totalCount = userMapper.pageListCount();
     Map<String, Object> result = new HashMap<String, Object>();
     result.put("pageList", pageList);
     result.put("totalCount", totalCount);

     return result;
  }

// mapper.xml
<select id="pageListByRoleName2"  resultType="com.tsq.democase.onetomany.domain.vo.UserVo">
    SELECT distinct u.*
    FROM sys_user u
    left join sys_user_role ur on u.id = ur.user_id
    left join sys_role r on r.id = ur.role_id
    <where>
        <if test="roleName != null and roleName != ''" >
            r.role_name LIKE concat('%', #{roleName}, '%')
        </if>
    </where>
    LIMIT #{offset}, #{pageSize}
</select>  

查詢結(jié)果

同場景二。

查詢時(shí)產(chǎn)生的sql如下:

  --  ==>  
  SELECT
      distinct u.* 
  FROM
      sys_user u 
  left join
      sys_user_role ur 
          on u.id = ur.user_id 
  left join
      sys_role r 
          on r.id = ur.role_id 
  WHERE
      r.role_name LIKE concat('%', '角色', '%') LIMIT 0, 10 
   --  ==>  
  SELECT
      ur.user_id ,
      r.id roleId,
      r.role_name 
  FROM
      sys_user_role ur 
  inner join
      sys_role r 
          on ur.role_id = r.id 
   --  ==>  
  SELECT
      count(1) 
  FROM
      sys_user 
 

由sql日志可見這種方式比純sql方式效率高一些

總結(jié)

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot使用@Cacheable出現(xiàn)預(yù)覽工具亂碼的解決方法

    SpringBoot使用@Cacheable出現(xiàn)預(yù)覽工具亂碼的解決方法

    直接使用注解進(jìn)行緩存數(shù)據(jù),我們再使用工具去預(yù)覽存儲的數(shù)據(jù)時(shí)發(fā)現(xiàn)是亂碼,這是由于默認(rèn)序列化的問題,所以接下來將給大家介紹一下SpringBoot使用@Cacheable出現(xiàn)預(yù)覽工具亂碼的解決方法,需要的朋友可以參考下
    2023-10-10
  • Java 的 Condition 接口與等待通知機(jī)制詳解

    Java 的 Condition 接口與等待通知機(jī)制詳解

    在 Java 并發(fā)編程里,實(shí)現(xiàn)線程間的協(xié)作與同步是極為關(guān)鍵的任務(wù),本文將深入探究Condition接口及其背后的等待通知機(jī)制,感興趣的朋友一起看看吧
    2025-05-05
  • java中volatile關(guān)鍵字解釋說明

    java中volatile關(guān)鍵字解釋說明

    volatile是Java中的一個關(guān)鍵字,用于修飾變量,保證其可見性和禁止指令重排序,但不保證原子性,下面我們從原理層面詳細(xì)解析volatile 的工作機(jī)制,感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • Mybatis分頁插件Pagehelper的PageInfo字段屬性使用及解釋

    Mybatis分頁插件Pagehelper的PageInfo字段屬性使用及解釋

    這篇文章主要介紹了Mybatis分頁插件Pagehelper的PageInfo字段屬性使用及解釋,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • ZIP4j 壓縮與解壓的實(shí)例詳解

    ZIP4j 壓縮與解壓的實(shí)例詳解

    這篇文章主要介紹了ZIP4j 壓縮與解壓的實(shí)例詳解的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-10-10
  • Java讀取Map的兩種方法與對比

    Java讀取Map的兩種方法與對比

    相信大家都知道在Java中Map的使用非常頻繁,我們經(jīng)常會需要對Map進(jìn)行遍歷和讀取,那么下面這篇文章將展示兩種遍歷的方法以及簡要分析。有需要的可以參考借鑒,下面來一起看看吧。
    2016-11-11
  • 一文詳解如何從零構(gòu)建Spring?Boot?Starter并實(shí)現(xiàn)整合

    一文詳解如何從零構(gòu)建Spring?Boot?Starter并實(shí)現(xiàn)整合

    Spring Boot是一個開源的Java基礎(chǔ)框架,用于創(chuàng)建獨(dú)立、生產(chǎn)級的基于Spring框架的應(yīng)用程序,這篇文章主要介紹了如何從零構(gòu)建Spring?Boot?Starter并實(shí)現(xiàn)整合的相關(guān)資料,需要的朋友可以參考下
    2025-03-03
  • java 將字符串、list 寫入到文件,并讀取內(nèi)容的案例

    java 將字符串、list 寫入到文件,并讀取內(nèi)容的案例

    這篇文章主要介紹了java 將字符串、list 寫入到文件,并讀取內(nèi)容的案例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Java讀取數(shù)據(jù)庫表的示例代碼

    Java讀取數(shù)據(jù)庫表的示例代碼

    這篇文章主要介紹了Java讀取數(shù)據(jù)庫表,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • SpringBoot引入SPEL模板字符串替換的兩種方式

    SpringBoot引入SPEL模板字符串替換的兩種方式

    在 Spring Boot 中,我們可以使用字符串替換工具類來實(shí)現(xiàn)這些功能,本文主要介紹了SpringBoot引入SPEL模板字符串替換的兩種方式,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03

最新評論

镇江市| 电白县| 威信县| 宝清县| 融水| 巴东县| 仙居县| 渭源县| 涟水县| 美姑县| 澄江县| 安化县| 晋中市| 江西省| 兴文县| 集贤县| 兴国县| 绥宁县| 乐业县| 北京市| 宁城县| 柏乡县| 定兴县| 金平| 突泉县| 南昌市| 阆中市| 隆回县| 南投县| 莎车县| 大余县| 辛集市| 华阴市| 沅江市| 海林市| 禹州市| 诸暨市| 平罗县| 鄂州市| 原平市| 苍梧县|