在用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 看一下代码很简单,就是对查询结果封装了下,方便数据的返回.

调用方法:

 // 序列号
String deviceSerial="123456";
//集合的名字,就相当于表名
String cllectionName="device";
//返回第几页
int pageIndex = 0;
//每页的大小
int pageSize = 10;
PageImpl<DeviceStatusItem> pageImpl = deviceStatusRepository
.pageDeviceStatusItemByDeviceSerial(deviceSerial, collectionName, pageIndex, pageSize);
System.out.println("list:" + pageImpl.getContent() + " number:" + pageImpl.getNumber() + " size:"
+ pageImpl.getSize() + " pages:" + pageImpl.getTotalPages()
+ " 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

Mongodb系列- spring-data-mongodb使用MongoTemplate实现分页查询的更多相关文章

  1. MongoDB学习-->Spring Data Mongodb框架之Repository

    application-dev.yml server: port: 8888 mongo: host: localhost port: 27017 timeout: 60000 db: mamabik ...

  2. mongodb Decimal Spring data mongodb Decimal128 SpringMvc 序列化字符串 json converter

    Mongodb 3.4 就开始支持Decimal 类型,解决double的精度问题,但是不太好用,MapReduce的时候Array.sum 也不能计算 Decimal.比较坑,但是聚合可以用 Spr ...

  3. Spring Data MongoDB example with Spring MVC 3.2

    Spring Data MongoDB example with Spring MVC 3.2 Here is another example web application built with S ...

  4. 使用Spring访问Mongodb的方法大全——Spring Data MongoDB查询指南

    1.概述 Spring Data MongoDB 是Spring框架访问mongodb的神器,借助它可以非常方便的读写mongo库.本文介绍使用Spring Data MongoDB来访问mongod ...

  5. Spring data mongodb 聚合,投射,内嵌数组文档分页.

    尽量别直接用 DBObject  ,Spring data mongodb 的api 本来就没什么多大用处,如果还直接用 DBObject 那么还需要自己去解析结果,说动做个对象映射,累不累 Spri ...

  6. JAVA 处理 Spring data mongodb 时区问题

    Spring data mongodb 查询出结果的时候会自动 + 8小时,所以我们看起来结果是对的 但是我们查询的时候,并不会自动 + 8小时,需要自己处理 解决方法 1   @JsonFormat ...

  7. Spring Data MongoDB 三:基本文档查询(Query、BasicQuery)(一)

    一.简单介绍 Spring Data  MongoDB提供了org.springframework.data.mongodb.core.MongoTemplate对MongoDB的CRUD的操作,上一 ...

  8. Introduction to Spring Data MongoDB

    Introduction to Spring Data MongoDB I just announced the new Spring 5 modules in REST With Spring: & ...

  9. Spring Data MongoDB 一:入门篇(环境搭建、简单的CRUD操作)

    一.简介 Spring Data  MongoDB 项目提供与MongoDB文档数据库的集成.Spring Data MongoDB POJO的关键功能区域为中心的模型与MongoDB的DBColle ...

  10. Spring Data MongoDB 分页查询

    在上篇文章 Spring Data MongoDB 环境搭建 基础上进行分页查询 定义公用分页参数类,实现 Pageable 接口 import java.io.Serializable; impor ...

随机推荐

  1. 用面向对象重写thread 实现多次调用一个线程

    思路: 利用thread类中,run方法在子线程中调用,其他方法在主线程调用,所以将生产者写入主线程,将消费者写入run函数中在子线程中执行,完成生产者消费者模型 注意: 1. 要在 init 函数中 ...

  2. AngularJS的Scope和Digest

    Angular是一个成熟和强大的JavaScript框架.它也是一个比较庞大的框架,在熟练掌握之前,需要领会它提出的很多新概念.很多Web开发人员涌向Angular,有不少人面临同样的障碍.Diges ...

  3. P3810 -三维偏序(陌上花开)cdq-分治

    P3810 [模板]三维偏序(陌上花开) 思路 :按照 1维排序 二维 分治三维树状数组维护 #include<bits/stdc++.h> using namespace std; #d ...

  4. Java-从Double类型精度丢失认识BigDecimal

    Java-从Double类型精度丢失认识BigDecimal 参考资料 https://www.jianshu.com/p/07e3eeb90f18 https://zh.wikipedia.org/ ...

  5. XamarinSQLite教程Xamarin.iOS项目中打开数据库文件

    XamarinSQLite教程Xamarin.iOS项目中打开数据库文件 以下是打开MyDocuments.db数据库的具体操作步骤: (1)将Mac电脑上的MyDocuments.db数据库移动到W ...

  6. jsoup 解析html

    http://www.cnblogs.com/jycboy/p/jsoupdoc.html http://www.cnblogs.com/mokafamily/p/3558620.html

  7. json信息的后台到前台的传输

    公共方法: /** * 实际返回的是 response.setContentType("text/html;charset=utf-8"); * * @param o */ pub ...

  8. 英语口语练习系列-C13-聚会

    词汇 音频 1. apartment [əˈpɑ:tmənt] n. 公寓 a big / small apartment 一个大的/小的公寓 in an apartment 在公寓 2. arm [ ...

  9. Java并发编程(三)-- 多线程的“问题”

    竞态条件与临界区 在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源.当多个线程同时访问同一个资源,并且其中的一个或者多个线程对这个资源进行了写操作,才会产生竞态条件.多个线程 ...

  10. 潭州课堂25班:Ph201805201 爬虫高级 第八课 AP抓包 SCRAPY 的图片处理 (课堂笔记)

    装好模拟器设置代理到 Fiddler  中, 代理 IP 是本机 IP, 端口是 8888, 抓包 APP斗鱼 用 format 设置翻页