在MyBatis Plus中实现多表联合查询并分页,主要有两种核心思路:一种是使用自定义SQL(XML或注解)配合MyBatis Plus的分页对象,另一种是借助第三方扩展库如 mybatis-plus-join。下面的表格详细对比了这两种主流方案及其实现要点,可以帮助你快速选择。
| 实现方案 | 核心思路 | 优点 | 适用场景 |
|---|---|---|---|
| 自定义SQL | 在Mapper的XML文件或@Select注解中编写完整的联表SQL,方法参数和返回值使用MyBatis Plus的IPage对象。 | 灵活性极高,可以编写任意复杂的SQL语句;性能可控性好。 | 中、高度复杂的多表关联查询,需要精细控制SQL逻辑的场景。 |
mybatis-plus-join扩展库 | Mapper继承MPJBaseMapper,使用专用的MPJLambdaWrapper或MPJQueryWrapper构建关联查询和分页。 | 开发效率高,能用Lambda表达式和链式调用,写法与单表查询类似,类型安全。 | 关联关系清晰、复杂度中等的查询,追求快速开发和代码简洁性的场景。 |
💡 方案一:自定义SQL实现联表分页
这是最常用且基础的方式,其工作流程可以概括为:在Service层准备分页信息,在Mapper层通过自定义SQL执行查询,最后由MyBatis Plus的分页插件自动处理分页参数和结果。
1. 实现步骤与代码示例
- Service层:创建分页对象并调用Mapper方法。
@Service
public class UserServiceImpl {
@Autowired
private UserMapper userMapper;
public IPage<UserOrderDTO> getUserOrdersPage(Page<UserOrderDTO> page, Long userId) {
// 将分页对象和查询条件传递给Mapper方法
return userMapper.selectUserOrdersPage(page, userId);
}
}
- Mapper接口:方法的第一个参数和返回值均为MyBatis Plus的分页类型。
public interface UserMapper extends BaseMapper<User> {
// 定义分页查询方法
IPage<UserOrderDTO> selectUserOrdersPage(IPage<UserOrderDTO> page, @Param("userId") Long userId);
}
- Mapper XML:编写联表SQL,无需在SQL中书写
LIMIT等分页关键字,分页插件会自动处理。
<select id="selectUserOrdersPage" resultType="com.example.dto.UserOrderDTO">
SELECT
u.id AS userId,
u.name AS userName,
o.product_name AS productName
FROM
user u
LEFT JOIN
order o ON u.id = o.user_id
WHERE
u.id = #{userId}
<!-- 分页插件会自动在此SQL基础上添加分页逻辑 -->
</select>
2. 使用QueryWrapper传递动态条件
如果你的查询条件需要动态构建,可以将QueryWrapper作为参数传到XML中。
- Mapper接口
IPage<UserOrderDTO> selectUserOrdersPage(IPage<UserOrderDTO> page, @Param("ew") Wrapper<UserOrderDTO> wrapper);
- Mapper XML:在SQL中使用
${ew.customSqlSegment}来插入Wrapper生成的条件。
<select id="selectUserOrdersPage" resultType="com.example.dto.UserOrderDTO">
SELECT ... FROM ...
WHERE 1=1
${ew.customSqlSegment} <!-- Wrapper生成的条件(如 eq, like)会在这里填充 -->
</select>
🚀 方案二:使用mybatis-plus-join扩展库
这个第三方库为MyBatis Plus提供了类似单表查询的联表操作体验。
1. 引入依赖
首先需要在项目中添加依赖。
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join</artifactId>
<version>1.2.4</version> <!-- 请使用最新版本 -->
</dependency>
2. 代码示例
- Mapper接口继承
MPJBaseMapper。
public interface UserMapper extends MPJBaseMapper<User> {
}
- 使用
MPJLambdaWrapper进行类型安全的联表分页查询。
@Autowired
private UserMapper userMapper;
public IPage<UserOrderDTO> selectJoinPage() {
MPJLambdaWrapper<User> wrapper = new MPJLambdaWrapper<User>()
.selectAll(User.class) // 查询User表所有字段
.select(Order::getProductName) // 查询Order表的指定字段
.leftJoin(Order.class, Order::getUserId, User::getId) // 左关联
.eq(User::getStatus, 1); // 条件
return userMapper.selectJoinPage(new Page<>(1, 10), UserOrderDTO.class, wrapper);
}
⚠️ 重要前提配置
无论选择哪种方案,都必须确保MyBatis Plus的分页插件已正确配置,否则分页功能不会生效。
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
💎 如何选择?
- 面对复杂、定制化要求高的联表查询(例如涉及多个分组、聚合函数、子查询或数据库特定语法),自定义SQL方案是更可靠的选择。
- 对于关联关系简单明了的常规查询,并且希望代码简洁、易于维护,
mybatis-plus-join扩展库能极大提升开发效率。
希望这些详细的解释和代码示例能帮助你顺利实现需求。如果你的查询场景有特定的复杂性,比如需要多个LEFT JOIN或者复杂的结果映射,可以分享更多细节,我能提供更具体的建议。