最近碰到一个场景,需要多表连接查询分页。简化后代码如下:
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
至此,问题解决!