mybatis-plus 一对多查询分页列表 结果集数量和数据总数量不匹配问题


前言

工作采坑记录: mabtis-plus分页列表records.size() 和 total 数量不一致问题。


一、total和records.size()数量不一致原因。

mybatis-plus3.2.0的版本在用mabatis-plus 一对多查询做关联查询时,前段传递的参数pagesize是在你sql关联之后的语句最后加上limit,则在对多的情况下,可能主表数据关联子表后同一条主表对应多条子表记录,但是在映射后只会返回一条主表信息,但是total计算的是关联后的总数量。接下来我将举例说明。

二、示例

1 代码

1.1 pom

  <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.2.0</version>
        </dependency>

1.2 分页插件配置

@Configuration
public class DateSourceConfig {
    /**
     * 注册分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

1.3 实体

@Data
@TableName("t_class")
public class ClassDO {
    /**
     * 类id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long classId;
    private String className;
}


@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private Long classId;
}

1.4 controller

@RestController
@RequestMapping("/class")
@AllArgsConstructor
public class ClassController {
    private final ClassMapper classMapper;
    @GetMapping("page")
    public R page(QueryRequest request, User user){
        Page<User> page = new Page<>(request.getPageNum(), request.getPageSize());
        IPage<ClassVO> resultPage = classMapper.page(page, user);
        return R.ok(resultPage);
    }
}

1.5 mapper

public interface ClassMapper extends BaseMapper<ClassDO> {
    IPage<ClassVO> page(Page<User> page, User user);
}

<mapper namespace="com.lfh.demo.mapper.ClassMapper">

    <resultMap id="pageMap" type="com.lfh.demo.entity.vo.ClassVO">
        <result column="classId" property="classId"/>
        <result column="className" property="className"/>
        <collection property="userList" ofType="com.lfh.demo.entity.User" javaType="ArrayList">
            <result column="userName" property="name"/>
        </collection>
    </resultMap>
    <select id="page" resultMap="pageMap">
        select tc.id as classId, tc.class_name as className,
            tu.name as userName
        from t_class tc
        left join t_user  tu on tu.class_id = tc.id
    </select>
</mapper>

2 测试结果

{
    "code": 0,
    "data": {
        "records": [
            {
                "classId": 1,
                "className": "一年级一班",
                "userList": [
                    {
                        "id": null,
                        "name": "张六",
                        "age": null,
                        "email": null,
                        "classId": null
                    },
                    {
                        "id": null,
                        "name": "张大",
                        "age": null,
                        "email": null,
                        "classId": null
                    },
                    {
                        "id": null,
                        "name": "王五",
                        "age": null,
                        "email": null,
                        "classId": null
                    },
                    {
                        "id": null,
                        "name": "李四",
                        "age": null,
                        "email": null,
                        "classId": null
                    },
                    {
                        "id": null,
                        "name": "张三",
                        "age": null,
                        "email": null,
                        "classId": null
                    }
                ]
            },
            {
                "classId": 2,
                "className": "一年级二班",
                "userList": []
            },
            {
                "classId": 3,
                "className": "一年级三班",
                "userList": []
            }
        ],
        "total": 7,
        "size": 10,
        "current": 1,
        "orders": [],
        "searchCount": true,
        "pages": 1
    },
    "msg": "执行成功"
}

由测试结果可看除返回的total是计算子表的数据 而实际我们需要的是返回几个班级的数量即可。

3 解决方案

3.1 升级mybatis-plus的版本

3. 1.1 修改pom文件

讲mybatis-plus-boot-starter的版本升级到3.4.1

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
3.1.2 修改分页插件的配置文件

@Configuration
public class DateSourceConfig {
    /**
     * 注册分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
3.1.3 测试结果
{
    "code": 0,
    "data": {
        "records": [
            {
                "classId": 1,
                "className": "一年级一班",
                "userList": [
                    {
                        "id": null,
                        "name": "张六",
                        "age": null,
                        "email": null,
                        "classId": null
                    },
                    {
                        "id": null,
                        "name": "张大",
                        "age": null,
                        "email": null,
                        "classId": null
                    },
                    {
                        "id": null,
                        "name": "王五",
                        "age": null,
                        "email": null,
                        "classId": null
                    },
                    {
                        "id": null,
                        "name": "李四",
                        "age": null,
                        "email": null,
                        "classId": null
                    },
                    {
                        "id": null,
                        "name": "张三",
                        "age": null,
                        "email": null,
                        "classId": null
                    }
                ]
            },
            {
                "classId": 2,
                "className": "一年级二班",
                "userList": []
            },
            {
                "classId": 3,
                "className": "一年级三班",
                "userList": []
            }
        ],
        "total": 3,
        "size": 10,
        "current": 1,
        "orders": [],
        "optimizeCountSql": true,
        "hitCount": false,
        "countId": null,
        "maxLimit": null,
        "searchCount": true,
        "pages": 1
    },
    "msg": "执行成功"
}

可以看出返回的total是正确的数量

3.2 修改mapper.xml的查询语句,通过子查询也可解决

3.2.1 修改mapper.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.lfh.demo.mapper.ClassMapper">

    <resultMap id="pageMap" type="com.lfh.demo.entity.vo.ClassVO">
        <result column="classId" property="classId"/>
        <result column="className" property="className"/>
        <collection column="classId" property="userList" ofType="com.lfh.demo.entity.User" javaType="ArrayList" select="selectUserByClassId">
            <result column="userName" property="name"/>
        </collection>
    </resultMap>
    <select id="selectUserByClassId" resultType="com.lfh.demo.entity.User">
        select * from t_user where class_id = #{classId}
    </select>
    <select id="page" resultMap="pageMap">
        select tc.id as classId, tc.class_name as className
        from t_class tc
    </select>
</mapper>
3.2.2 测试结果
{
    "code": 0,
    "data": {
        "records": [
            {
                "classId": 1,
                "className": "一年级一班",
                "userList": [
                    {
                        "id": 2,
                        "name": "张三",
                        "age": 19,
                        "email": null,
                        "classId": 1
                    },
                    {
                        "id": 3,
                        "name": "李四",
                        "age": 20,
                        "email": null,
                        "classId": 1
                    },
                    {
                        "id": 4,
                        "name": "王五",
                        "age": 51,
                        "email": null,
                        "classId": 1
                    },
                    {
                        "id": 5,
                        "name": "张大",
                        "age": 21,
                        "email": null,
                        "classId": 1
                    },
                    {
                        "id": 6,
                        "name": "张六",
                        "age": 22,
                        "email": null,
                        "classId": 1
                    }
                ]
            },
            {
                "classId": 2,
                "className": "一年级二班",
                "userList": []
            },
            {
                "classId": 3,
                "className": "一年级三班",
                "userList": []
            }
        ],
        "total": 3,
        "size": 10,
        "current": 1,
        "orders": [],
        "searchCount": true,
        "pages": 1
    },
    "msg": "执行成功"
}

total和数据的记录数一致

3.3 对返回记录进行重新封装(不推荐)

当前返回的记录拿到ipage之后直接进行返回的。可以通过工具类对结果集进行处理后返回。

/**
     * 封装前端分页表格所需数据
     *
     * @param pageInfo pageInfo
     * @return Map<String, Object>
     */
    public static Map<String, Object> getDataTable(IPage<?> pageInfo) {
        Map data = new ConcurrentHashMap<String, Object>(2);
        data.put("records", pageInfo.getRecords());
        data.put("total", pageInfo.getTotal());
        return data;
    }

通过对ipage处理后重新返回,虽然total和结果集能对应的上 但是还存在一个问题就是返回的分页数据可能是和前端一页暂时的条数不匹配的。打个比方:现在一个class对应10个user 这时mysql查到的数据为10条,但是通过mybatis对多映射之后到class实体类的时候其实只有一条数据,只是其字段userList的大小为10,那么我们返回到前端的结果也只是有一条数据在当前页,但是还有其他数据的情况下,点击下一页就会可能出现每页显示条数不一致的问题。用户体验稍差,但如果没这方便的要求,只是实现功能的话,这种方法也可行。

总结

以上则是几种分页数据total和records的数量不一致的解决方案,推荐第一种,当然此方法还是需要作者更新,另外存在版本冲突而无法更换mybatis-plus版本的话,就通过第二种的子查询的方式来实现。

MyBatis-Plus 提供了丰富的 CRUD 操作一些便捷的功能,其中的 join 功能用于在 SQL 查询中加入额外的表。对于一对多、多对一等关联查询场景非常有用。 ### mybatis-plus-join 一对一使用教程 #### 1. 引入依赖 首先需要在项目中引入 MyBatis-Plus 相关的 ORM 包如 MyBatis 或 Spring Data JPA 的依赖。通常在 Maven 或者 Gradle 中添加如下依赖: ```xml <!-- Maven --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>latest-version</version> </dependency> <!-- Gradle --> implementation 'com.baomidou:mybatis-plus-boot-starter:latest-version' ``` 确保更新 `latest-version` 为你使用的 MyBatis-Plus 版本。 #### 2. 创建实体类 创建两个实体类,并定义它们之间的关联字段。 假设我们有 `User` 类 `Address` 类,并且存在一对一的关系: ```java public class User { @TableId(value = "id", type = IdType.AUTO) private Integer id; private String name; // 省略其他属性... public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } private Address address; } public class Address { @TableId(value = "address_id") private Integer id; private String street; // 省略其他属性... } ``` 这里,`User` 类包含了一个 `Address` 实体作为属性,这表示每个用户有一个对应的地址信息,即一对一关系。 #### 3. 自动关联配置 为了自动地将关联的实体从数据查询结果中取出并注入到对应的属性上,只需要在对应的 Mapper 接口中增加注解即可。通常我们会在 `UserMapper` 中添加如下注解: ```java @Autowired private UserMapper userMapper; // 在这里可以声明一个返回类型为 List<User> 的方法,例如: List<User> getUsersWithAddresses(); ``` 然后在 `UserMapper.xml` 文件中添加如下 XML 配置: ```xml <mapper namespace="com.example.mapper.UserMapper"> <!-- 在这里写你的SQL查询语句 --> <select id="getUsersWithAddresses" resultType="com.example.entity.User"> SELECT * FROM users LEFT JOIN addresses ON users.id = addresses.user_id </select> </mapper> ``` 这里使用了 `LEFT JOIN` 来确保即使是空的关联也是可见的,这样可以保证如果某个用户没有地址的话,在查询结果中也能得到一个对应的 `null` 地址信息。 #### 4. 使用方法 现在,你可以在业务层通过 `getUsersWithAddresses()` 方法获取所有包含关联地址信息的用户列表: ```java List<User> users = userMapper.getUsersWithAddresses(); for (User user : users) { System.out.println(user.getName()); if (user.getAddress() != null) { System.out.println("Address: " + user.getAddress().getStreet()); } else { System.out.println("No address available."); } } ``` ### 相关问题: 1. **如何优化一对多关联查询性能**? - 可以考虑使用缓存机制减少频繁的数据库访问。 - 根据实际情况调整连接策略,如分页查询时只加载部分关联数据- 使用索引优化关联字段查询条件的性能。 2. **在使用一对一关联时遇到错误怎么办**? - 检查实体类、主键设置、数据库表结构是否匹配- 确认 Mapper 方法 SQL 是否正确无误。 - 查看日志是否有异常信息,可以帮助定位问题所在。 3. **MyBatis-Plus 是否支持动态 SQL 构建**? - MyBatis-Plus 支持一些基本的动态 SQL 功能,如 where 条件构建,但是复杂的动态 SQL 需要手动编写。 - 对于更复杂的动态 SQL 需要考虑使用原生的 MyBatis SQL 构建API。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值