MybatisPlus 多表连接分页

1,302 阅读2分钟

最近碰到一个场景,需要多表连接查询分页。简化后代码如下:

MybatisPlus版本:

 <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>
  SELECT
          u.id,
          u.`name`,
          u.email,
          c.`name` AS courseName
      FROM
          `user` AS u
      LEFT JOIN course AS c ON u.id = c.u_id
      ORDER BY
          u.id

mapper代码如下

@Mapper
public interface UserCourseInfoMapper {

    IPage<UserCourseInfo> selectPageByUid(IPage<UserCourseInfo> page);
}

CREATE TABLE `course` (
  `id` bigint(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `u_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL COMMENT '主键ID',
  `name` varchar(30) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) DEFAULT NULL COMMENT '邮箱'
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

两张表关联数据,需要自行插入一些,便于测试。


打开mybatisplus官网,查找分页界面,需要添加一个拦截器。把代码抄下来

@Configuration
// 这里改成你代码中的mapper包名
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}

发现下面的configurationCustomizer()报错,然后去码云的提问中找答案,作者说在新版本里面取消了这个方法。那就直接删除。


开始测试分页。第一步,写单元测试:

@SpringBootTest
class UserCourseInfoMapperTest {


    @Autowired
    private UserCourseInfoMapper userCourseInfoMapper;

    @Test
    void testQueryPage() {
        IPage<UserCourseInfo> infoIPage = userCourseInfoMapper.selectPageByUid(Page.of(1, 5));
        Console.log(infoIPage.getRecords());
    }
}

测试前记得配置打印sql。

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

然后启动测试列,发现打印的查询总数的sql只有前半段,没有left join后半段。如下:

 SELECT COUNT(*) AS total FROM `user` AS u

这里就很奇怪了,然后只能看分页插件源码了,然后去类com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor#autoCountSql中打上断电。

   protected String autoCountSql(IPage<?> page, String sql) {
        // 是否开启优化countSql参数
        if (!page.optimizeCountSql()) {
            return lowLevelCountSql(sql);
        }
        ···
        下面代码太多,大致意思优化select conut(*)的生产代码。会去掉一些排序之类的。
        left join后面的语句中如果有参数,或者where条件中有参数,那么不处理,如果没有,那么去掉这个left join

因为我这里的xml语句中,没有带参数,所有会把left join后面语句全部去掉,所以导致分页错误。

解决办法:

autoCountSql()这个方法第一行,有个是否开启优化countSql参数开关。这里默认为true,就会走下面的逻辑,我们这里关闭这个参数即可。

可以在测试类中配置。如下:

  @Test
  void testQueryPage() {
      Page page = Page.of(1, 5);
      page.setOptimizeCountSql(false);
      IPage<UserCourseInfo> infoIPage = userCourseInfoMapper.selectPageByUid();
      Console.log(infoIPage.getRecords());
  }

然后再次运行测试类,发现打印的sql正常,如下:

 SELECT COUNT(*) AS total FROM `user` AS u LEFT JOIN course AS c ON u.id = c.u_id ORDER BY  u.id

至此,问题解决!