zoukankan      html  css  js  c++  java
  • Mongodb系列- spring-data-mongodb使用MongoTemplate实现分页查询

    在用spring-data-mongodb框架开发的过程中,需要实现分页查询,就百度了下,没找到满意的又google了下,找到了思路.

    在spring-data-mongodb 官方文档中,建议你使用PagingAndSortingRepository  来实现分页,但是我是真的不喜欢这个设计啊!!

    用方法名来映射查询语句,框架会自动生成执行代码,还为此定义了一套语法,举个例子:

    
    

    public interface UserRepository extends MongoRepository<User, String>, QueryDslPredicateExecutor<User> {
    @Query("{ 'name' : ?0 }")
    List<User> findUsersByName(String name);

    
    

    @Query("{ 'age' : { $gt: ?0, $lt: ?1 } }")
    List<User> findUsersByAgeBetween(int ageGT, int ageLT);

    
    

    List<User> findByName(String name);

    
    

    List<User> findByNameLikeOrderByAgeAsc(String name);

    
    

    List<User> findByAgeBetween(int ageGT, int ageLT);

    
    

    @Query(value = "{}", fields = "{name : 1}")
    List<User> findNameAndId();

    
    

    @Query(value = "{}", fields = "{_id : 0}")
    List<User> findNameAndAgeExcludeId();
    }

    这个接口类只定义了接口,并不需要实现,因为SDM框架(spring-data-mongodb简称,以下都使用简称)会帮你生成代码..

    findByAgeBetween(int ageGT, int ageLT);-> 就是where ageGT <age and age <ageLT;

    刚开始可能感觉很简单,但是一旦字段多了查询条件复杂了! 你根本不知道自己在写什么!别人看你的代码一长串方法名,也是直接懵逼的..

    而 查出来的许多分页查询也是直接使用的PagingAndSortingRepository  这个接口,自动生成...非常不喜欢...就去查怎么使用MongoTemplate实现...

    先下班....放假回来补上..哈哈

    庆祝五一上班,把没写的写完...

    使用MongoTemplate实现分页

    @Repository("deviceStatusRepository")
    public class DeviceStatusRepository {

        @Autowired
        private MongoOperations mongoOperations;


    /** * 分页查询 */ public PageImpl<DeviceStatusItem> pageDeviceStatusItemByDeviceSerial(String deviceSerial, String collectionName, int pageIndex, int pageSize) { Query query = Query.query( Criteria.where(CONSTS.DEVICE_SERIAL_FIELD).is(deviceSerial)); // 每页五个 Pageable pageable = new PageRequest(pageIndex, pageSize); // get 5 profiles on a page query.with(pageable); // 排序 query.with(new Sort(Direction.ASC, CONSTS.DEVICE_SERIAL_FIELD, CONSTS.DOMAINID_FIELD)); // 查询总数 int count = (int) mongoOperations.count(query, DeviceStatusItem.class, collectionName); List<DeviceStatusItem> items = mongoOperations.find(query, DeviceStatusItem.class, collectionName); // System.out.println("stories:" + stories.size() + " count:" + count); return (PageImpl<DeviceStatusItem>) PageableExecutionUtils.getPage(items, pageable, () -> count); }
    }

    解析:

    MongoOperations 是MongoTemplate的接口,它的具体实现就是MongoTemplate,所以这里使用MongoTemplate或MongoOperations 都可以.

    1. 创建PageRequest 对象,这是SDM框架提供的现成的分页请求类.构造函数很简单:

    // page:第几页, size:每页的大小
    public
    PageRequest(int page, int size) {
    this(page, size, null);
        }

    2. 构建Query 查询条件.我这里先是指定了根据序列号查询,然后设置分页请求 query.with(pageable);最后设置结果的排序.

    3. 使用SDM框架自带的工具类PageableExecutionUtils 返回PageImpl .这里的PageableExecutionUtils 和PageImpl 其实都可以自己实现,你可以打开PageImpl 看一下代码很简单,就是对查询结果封装了下,方便数据的返回.

    调用方法:

     1 // 序列号
     2 String deviceSerial="123456";
     3 //集合的名字,就相当于表名
     4 String cllectionName="device";
     5 //返回第几页
     6 int pageIndex = 0;
     7 //每页的大小
     8 int pageSize = 10;
     9 PageImpl<DeviceStatusItem> pageImpl = deviceStatusRepository
    10                     .pageDeviceStatusItemByDeviceSerial(deviceSerial, collectionName, pageIndex, pageSize);
    11   System.out.println("list:" + pageImpl.getContent() + " number:" + pageImpl.getNumber() + " size:"
    12    + pageImpl.getSize() + " pages:" + pageImpl.getTotalPages()
    13    + " TotalElements:" + pageImpl.getTotalElements());

    解析: 这个PageImpl 方法名很清晰了.

    查询的结果集: pageImpl.getContent(),

    当前页是第几个: pageImpl.getNumber()

    当前页的大小: pageImpl.getSize()

    一共多少页: pageImpl.getTotalPages()

    一共多少条记录:  pageImpl.getTotalElements()

    优化的分页实现

    使用上边的分页实现没有大的问题, 但是有一个性能问题, 当你的集合很大的时候, count每次执行都会全表扫描一下,因为你只有全表扫描才知道有多少数量,耗费很多时间.而这个时间是没有必要的.

    你优化的实现就是去掉count,就想下边这样:

        /**
         * deviceSerials分页查询,不使用count,不然每次都要全表扫描.
         */
        public PageImpl<DeviceStatusItem> pageDeviceStatusItemByDeviceSerialListNotCount(List<String> deviceSerials,
                String collectionName, int pageIndex, int pageSize) {
            Query query = Query.query(Criteria.where(CONSTS.DEVICE_SERIAL_FIELD).in(deviceSerials));
            // 每页五个
            Pageable pageable = new PageRequest(pageIndex, pageSize); // get 5 profiles on a page
            query.with(pageable);
            // 排序
            query.with(new Sort(Direction.ASC, CONSTS.DEVICE_SERIAL_FIELD, CONSTS.DOMAINID_FIELD));
            List<DeviceStatusItem> items = readMongoTemplate.find(query, DeviceStatusItem.class, collectionName);
            // System.out.println("stories:" + stories.size() + " count:" + count);
            return (PageImpl<DeviceStatusItem>) PageableExecutionUtils.getPage(items, pageable, () -> 0);
        }

    把count去掉就好. 

    这样去掉count后, 只有在最后一次查询时才会进行全表扫描.

    使用count和不使用count性能比较

    1.准备数据:

    准备了50万数据,不是很多,就简单测试下, 数据量越大效果越明显.

    2.测试程序

    只列了主要的程序:

        public static void readUseCount(IDeviceShadowQueryService deviceShadowQueryService, List<String> deviceSerials) {
            int pageIndex = 0;
            int pageSize = 80;
            int totalPages = 0;
            Pagination<DeviceStatusDto> pagination = deviceShadowQueryService
                    .readDeviceStatusDtoByDeviceSerials(deviceSerials, pageIndex, pageSize);
            int size = pagination.getRecords().size();
            totalPages = pagination.getTotalPages();
            // 第1页开始
            for (int i = 1; i < totalPages; i++) {
                pagination = deviceShadowQueryService.readDeviceStatusDtoByDeviceSerials(deviceSerials, i, pageSize);
                totalPages = pagination.getTotalPages();
                size = pagination.getRecords().size();
            }
            count++;
            if (count % 100 == 0)
                System.out.println("totalPages:" + totalPages + " size:" + size);
        }
    
        public static void readNoCount(IDeviceShadowQueryService deviceShadowQueryService, List<String> deviceSerials) {
            int pageIndex = 0;
            int pageSize = 80;
            Pagination<DeviceStatusDto> page = deviceShadowQueryService
                    .readDeviceStatusDtoByDeviceSerialsList(deviceSerials, pageIndex, pageSize);
            int size = page.getRecords().size();
            while (size == pageSize) {
                pageIndex++;
                page = deviceShadowQueryService.readDeviceStatusDtoByDeviceSerialsList(deviceSerials, pageIndex, pageSize);
                size = page.getRecords().size();
            }
            count++;
            if (count % 100 == 0)
                System.out.println("pageIndex:" + pageIndex + " size:" + size);
        }

    3.测试结果

    使用count,开始读取, 大小:99975
    使用count,读取完毕,大小:99975 花费时间:112792

    不使用count,读取完毕,大小:99975 花费时间:109696

    不使用count,节约时间: 112792-109696=2900= 2.9s

    参考: 

     https://stackoverflow.com/questions/27296533/spring-custom-query-with-pageable?rq=1 

    转载请注明出处: http://www.cnblogs.com/jycboy/p/8969035.html

  • 相关阅读:
    Git简介
    Git之 git status、git diff 的基本使用
    Git之撤销修改 git checkout file、git reset HEAD file 的使用
    git连接gitlab远程仓库
    Git版本回退及 git log 、 git reset hard commit_id 的基本使用
    Git创建版本库及git init 、add 和 commit m 的基本使用
    Git之工作区和暂存区
    Git的由来及分布式版本控制和集中式版本控制的区别
    MariaDB 主从同步与热备
    MariaDB 用户与权限管理
  • 原文地址:https://www.cnblogs.com/jycboy/p/8969035.html
Copyright © 2011-2022 走看看