文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Mybatis Plus一对多联表查询及分页解决方案

2023-10-22 06:49

关注

文章目录

需求

查询用户信息列表,其中包含用户对应角色信息,页面检索条件有根据角色名称查询用户列表;

需求分析

一个用户对应多个角色,用户信息和角色信息分表根据用户id关联存储,用户和角色一对多进行表连接查询,

创建对应表:

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 '角色ID',  `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 '角色ID',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4  COMMENT='用户和角色关联表';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);

对应实体类:

@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;}

分页问题说明

在使用一对多连接查询并且分页时,发现返回的分页列表数据数量不对
比如这里查询用户对应角色列表,如果使用直接映射,那么 roleList 的每个 Role 对象都会算一条数据;比如查第一页,一个用户有三个角色每页三条数据,就会出现查出一个 User ,三个 Role 的这些情况,这它也算每页三条(其实就只查到一个用户)

分页问题原因

mybatis-plus一对多分页时,应该使用子查询的映射方式,使用直接映射就会出错
所以直接映射适用于一对一,子查询映射使用于一对多;

一对多场景一

查询用户表的内容,角色表不参与条件查询,用懒加载形式

// 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  @Overridepublic 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>

查询结果
图片alt

一对多场景二

查询用户表的内容,角色表要作为查询条件参与查询,例如要根据角色名称查询出用户列表

// 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@Overridepublic 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>

查询结果
在这里插入图片描述

性能优化

原因:

场景一二中使用 select方式会触发多次子查询(SELECT *FROM sys_user_role ur inner join sys_role …),当数据量大时会使查询速度很慢。

场景二中查询时产生的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各用户就要执行一百次子查询,效率极低。

优化解决方案

sql中只查询sys_user相关信息并且做roleName 过滤,roleList在java代码中用stream关联role并赋值roleList;

// serviceimpl  @Override  public Map<String, Object> pageListByRoleName(int offset, int pagesize,String roleName) {     // List 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>  

查询结果

同场景二。

查询时产生的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方式效率高一些

来源地址:https://blog.csdn.net/weixin_38898423/article/details/127939212

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯