在用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. phpstorm自定义代码片段

    右上角file--settings--Editor--Live Templates---+

  2. mysql数据库操作语句整合

    查看版本:select version();显示当前时间:select now(); 注意:在语句结尾要使用分号; 远程连接 一般在公司开发中,可能会将数据库统一搭建在一台服务器上,所有开发人员共用一 ...

  3. Eclipse块选择快捷键

    快捷键:Shift+Alt+A 功能:可删除复制选中区域中的内容 效果图:

  4. Java发邮件基础篇

    1. 电子邮件协议 电子邮件的在网络中传输和网页一样需要遵从特定的协议,常用的电子邮件协议包括 SMTP,POP3,IMAP.其中邮件的创建和发送只需要用到 SMTP协议,所以本文也只会涉及到SMTP ...

  5. php xml格式对象 返回->对应格式数组

    /*     * $objXml xml格式对象      * 返回 : 对应格式数组     */    public function XmlString2Arr($xml)    {       ...

  6. 潭州课堂25班:Ph201805201 django 项目 第十课 自定义错误码,完成图片验证码,用户是否被注册功能 (课堂笔记)

    把 视图传到前台的  JsonResponse(data=data) 先进行处理,之后再传到前台, 处理:引用自定义错误代码,把错误代码返回给前台,前台根据错误代码中文提示 class Code: O ...

  7. STM32——TIM2定时器定时

    STM32 中一共有11 个定时器,其中2 个高级控制定时器,4 个普通定时器和2 个基本定时器,以及2 个看门狗定时器和1 个系统嘀嗒定时器.其中系统嘀嗒定时器是前文中所描述的SysTick,看门狗 ...

  8. ns2.34 移植MFLOOD协议时出现的问题

    安全按照<NS网络模拟核协议仿真>第11章的步骤进行修改,但是make的时候出现了一下错误: make[1]: 正在进入目录 `/home/wang/ns/ns-allinone-2.34 ...

  9. Spring 的 AOP 进行事务管理的一些问题

    AspectJ AOP事务属性的配置(隔离级别.传播行为等): <tx:advice id="myAdvice" transaction-manager="mtTx ...

  10. shell脚本8--录制终端会话

    准备: script -t 2> timing.log -a output.session type commands; ... . .. exit 回放: scriptreplay timin ...