一、MongoTemplate 实现分页

springboot集成Mongodb好像没有现成的分页工具,只能自己先查总数再查数据,需要进行两次查询。

例如:

@Test
public void test_119() throws Exception{
    Query query = new Query();
    long total = mongoTemplate.count(query, Dog.class);
    query.with(PageRequest.of(0,3));
    List<Dog> dogList = mongoTemplate.find(query, Dog.class);
    System.out.println("总行数:"+total);
    System.out.println(JSON.toJSONString(dogList, SerializerFeature.PrettyFormat));
}

上述代码查询了dog列表的第0页,每页3条数据。运行结果如下:SpringBoot MongoTemplate 实现分页_List

PS:注意PageRequest起始页码为0。与MySQL起始页为1不同。

二、自定义AOP实现分页工具

可以发现分页都是这个逻辑:先查总数、添加分页信息、再查列表数据。

由于开发中同时用到了MyBatis,MyBatis有自己的分页插件 PageHelper 。PageHelper还是很好用的,只需要PageHelper.startPage(0,3); 之后的首次查询就自带分页了。

那能否让 PageHelper 也对 MongoTemplate 有效呢?

PageHelper是通过ThreadLocal来传递数据的,我们可以通过自定义一个AOP对MongoTemplate的查询方法进行增强即可。

首先引入AOP相关starter:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

编写切面类:

package org.example.aop;

import com.github.pagehelper.Page;
import com.github.pagehelper.SqlUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import java.util.List;

@Aspect
@Component
public class MongoTemplateAop {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Pointcut("execution(public * org.springframework.data.mongodb.core.MongoTemplate.find(..))")
    public void findPoint(){}

    @Pointcut("execution(public * org.springframework.data.mongodb.core.MongoTemplate.findAll(..))")
    public void findAllPoint(){}

    @Around("findPoint()")
    public Object findPointAround(ProceedingJoinPoint pjp){
        try {
            Object[] args = pjp.getArgs();
            // 是否开启了分页查询
            Page<Object> localPage = SqlUtil.getLocalPage();
            if (localPage != null && args[0] instanceof Query) {
                Query query = (Query) args[0];
                int pageNum = localPage.getPageNum();
                int pageSize = localPage.getPageSize();

                // 没有主动设置过分页相关的限制时,PageHelper 才生效
                if (query.getLimit() == 0 && query.getSkip() == 0) {
                    // 查询总数
                    long total = 0;
                    Class<?> entityClass = (Class<?>) args[1];
                    if (args.length == 3) {
                        String collectionName = (String) args[2];
                        total = mongoTemplate.count(query, entityClass, collectionName);
                    } else {
                        total = mongoTemplate.count(query, entityClass);
                    }
                    localPage.setTotal(total);

                    // 添加分页条件
                    query.with(PageRequest.of(pageNum - 1, pageSize));
                    List<?> result = (List<?>) pjp.proceed(args);
                    localPage.addAll(result);
                    return localPage;
                }
            }

            // 不分页直接查询
            return pjp.proceed(args);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            // PageHelper 作废
            SqlUtil.clearLocalPage();
        }
    }

    @Around("findAllPoint()")
    public Object findAllPointAround(ProceedingJoinPoint pjp){
        try {
            Object[] args = pjp.getArgs();
            // 是否开启了分页查询
            Page<Object> localPage = SqlUtil.getLocalPage();
            if (localPage != null) {
                Query query = new Query();
                int pageNum = localPage.getPageNum();
                int pageSize = localPage.getPageSize();

                // 没有主动设置过分页相关的限制时,PageHelper 才生效
                if (query.getLimit() == 0 && query.getSkip() == 0) {
                    // 查询总数
                    long total = 0;
                    List<?> result;
                    Class<?> entityClass = (Class<?>) args[0];
                    if (args.length == 2) {
                        String collectionName = (String) args[1];
                        total = mongoTemplate.count(query, entityClass, collectionName);
                        // 分页查询
                        query.with(PageRequest.of(pageNum - 1, pageSize));
                        result = mongoTemplate.find(query, entityClass, collectionName);
                    } else {
                        total = mongoTemplate.count(query, entityClass);
                        // 分页查询
                        query.with(PageRequest.of(pageNum - 1, pageSize));
                        result = mongoTemplate.find(query, entityClass);
                    }
                    localPage.setTotal(total);
                    localPage.addAll(result);
                    return localPage;
                }
            }

            // 不分页直接查询
            return pjp.proceed(args);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            // PageHelper 作废
            SqlUtil.clearLocalPage();
        }
    }

}

使用:

@Test
public void test_82() throws Exception{
    PageHelper.startPage(1,3);
    List<Dog> dogList = mongoTemplate.find(Query.query(Criteria.where("type").is(DogTypeEnum.ROBOT_DOG)), Dog.class);
    // 包装分页信息
    PageInfo<Dog> dogPageInfo = new PageInfo<>(dogList);
    System.out.println(JSON.toJSONString(dogPageInfo, SerializerFeature.PrettyFormat));
}

结果:

SpringBoot MongoTemplate 实现分页_spring_02