使用mybatis-plus自定义分页实现一对多的分页功能
自定义 Mapper 方法中使用分页
IPage<UserVo> selectPageVo(IPage<?> page, Integer state);
// 或者自定义分页类
MyPage selectPageVo(MyPage page);
// 或者返回 List
List<UserVo> selectPageVo(IPage<UserVo> page, Integer state);
对应的 XML 配置:
<select id="selectPageVo" resultType="xxx.xxx.xxx.UserVo">
SELECT id,name FROM user WHERE state=#{state}
</select>
如果返回类型是 IPage,则入参的 IPage 不能为 null。如果想临时不分页,可以在初始化 IPage 时 size 参数传入小于 0 的值。 如果返回类型是 List,则入参的 IPage 可以为 null,但需要手动设置入参的 IPage.setRecords(返回的 List)。 如果 XML 需要从 page 里取值,需要使用 page.属性 获取。
特殊需求
有两张表,商品表和商品属性表,具有一对多的关系
需求是:只有当商品存在至少一个价格为0的规格时,该商品才会被筛选出来。同时分页还是基于商品表的分页,但只保留符合条件的商品。
实现方式
在商品分页查询中,通过 EXISTS 子查询筛选出存在价格为 0 的规格的商品
<select id="selectProductPage" resultType="com.example.entity.Product">
SELECT p.id, p.name, p.price
FROM product p
WHERE p.state = #{state}
AND EXISTS (
SELECT 1
FROM product_spec ps
WHERE ps.product_id = p.id
AND ps.spec_price = 0 <!-- 筛选存在价格为0的规格 -->
)
</select>
Service 层查询规格时过滤价格为0,在查询商品规格时,直接过滤出价格为 0 的规格,将商品和商品规格进行组合,实现一对多的关系:
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductMapper productMapper;
@Autowired
private ProductSpecMapper productSpecMapper;
@Override
public PageResult<ProductPageVO> getProductPage(Integer pageNum, Integer pageSize, Integer state) {
// 1. 分页查询商品表(仅包含有价格为0规格的商品)
IPage<Product> page = new Page<>(pageNum, pageSize);
IPage<Product> productPage = productMapper.selectProductPage(page, state);
// 2. 提取商品ID列表
List<Long> productIds = productPage.getRecords().stream()
.map(Product::getId)
.collect(Collectors.toList());
if (productIds.isEmpty()) {
return new PageResult<>(Collections.emptyList(), 0L);
}
// 3. 批量查询商品规格(仅价格为0的规格)
List<ProductSpec> specs = productSpecMapper.selectList(
new QueryWrapper<ProductSpec>()
.in("product_id", productIds)
.eq("spec_price", BigDecimal.ZERO) // 直接过滤价格为0
);
// 4. 按商品ID分组规格
Map<Long, List<ProductSpec>> specMap = specs.stream()
.collect(Collectors.groupingBy(ProductSpec::getProductId));
// 5. 组装 VO 对象(规格已过滤)
List<ProductPageVO> voList = productPage.getRecords().stream()
.map(product -> {
ProductPageVO vo = new ProductPageVO();
BeanUtils.copyProperties(product, vo);
vo.setSpecs(specMap.getOrDefault(product.getId(), Collections.emptyList()));
return vo;
}).collect(Collectors.toList());
// 6. 返回分页结果
return new PageResult<>(voList, productPage.getTotal());
}
}
通过结合 EXISTS 子查询和内存数据组装,既保证了分页的准确性,又高效筛选出符合条件的商品及规格。