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

  • 相关阅读:
    poj 2528 Mayor's posters (线段树+离散化)
    poj 1201 Intervals (差分约束)
    hdu 4109 Instrction Arrangement (差分约束)
    poj 1195 Mobile phones (二维 树状数组)
    poj 2983 Is the Information Reliable? (差分约束)
    树状数组 讲解
    poj 2828 Buy Tickets (线段树)
    hdu 1166 敌兵布阵 (树状数组)
    Ubuntu网络配置
    Button控制窗体变量(开关控制灯的状态)
  • 原文地址:https://www.cnblogs.com/jycboy/p/8969035.html
Copyright © 2011-2022 走看看