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 看一下代码很简单,就是对查询结果封装了下,方便数据的返回.
调用方法:
// 序列号
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实现分页查询的更多相关文章
- MongoDB学习-->Spring Data Mongodb框架之Repository
application-dev.yml server: port: 8888 mongo: host: localhost port: 27017 timeout: 60000 db: mamabik ...
- mongodb Decimal Spring data mongodb Decimal128 SpringMvc 序列化字符串 json converter
Mongodb 3.4 就开始支持Decimal 类型,解决double的精度问题,但是不太好用,MapReduce的时候Array.sum 也不能计算 Decimal.比较坑,但是聚合可以用 Spr ...
- 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 ...
- 使用Spring访问Mongodb的方法大全——Spring Data MongoDB查询指南
1.概述 Spring Data MongoDB 是Spring框架访问mongodb的神器,借助它可以非常方便的读写mongo库.本文介绍使用Spring Data MongoDB来访问mongod ...
- Spring data mongodb 聚合,投射,内嵌数组文档分页.
尽量别直接用 DBObject ,Spring data mongodb 的api 本来就没什么多大用处,如果还直接用 DBObject 那么还需要自己去解析结果,说动做个对象映射,累不累 Spri ...
- JAVA 处理 Spring data mongodb 时区问题
Spring data mongodb 查询出结果的时候会自动 + 8小时,所以我们看起来结果是对的 但是我们查询的时候,并不会自动 + 8小时,需要自己处理 解决方法 1 @JsonFormat ...
- Spring Data MongoDB 三:基本文档查询(Query、BasicQuery)(一)
一.简单介绍 Spring Data MongoDB提供了org.springframework.data.mongodb.core.MongoTemplate对MongoDB的CRUD的操作,上一 ...
- Introduction to Spring Data MongoDB
Introduction to Spring Data MongoDB I just announced the new Spring 5 modules in REST With Spring: & ...
- Spring Data MongoDB 一:入门篇(环境搭建、简单的CRUD操作)
一.简介 Spring Data MongoDB 项目提供与MongoDB文档数据库的集成.Spring Data MongoDB POJO的关键功能区域为中心的模型与MongoDB的DBColle ...
- Spring Data MongoDB 分页查询
在上篇文章 Spring Data MongoDB 环境搭建 基础上进行分页查询 定义公用分页参数类,实现 Pageable 接口 import java.io.Serializable; impor ...
随机推荐
- NP:建立可视化输入的二次函数数据点集np.linspace+np.random.shuffle+np.random.normal
import numpy as np import matplotlib.pyplot as plt def fix_seed(seed=1): #重复观看一样东西 # reproducible np ...
- NLR:利用非线性回归,梯度下降法求出学习参数θ,进而求得Cost函数最优值——Jason niu
import numpy as np import random def genData(numPoints,bias,variance): x = np.zeros(shape=(numPoints ...
- csrf技巧
Burpsuite,在数据包处右键,Engagement tools – Generate CSRF PoC,可生成csrf payload
- Java内存管理-掌握自定义类加载器的实现(七)
勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇分析了ClassLoader的类加载相关的核心源码,也简单介绍了ClassLoa ...
- ISP PIPLINE (一) BLC 以及 线性化
what is the BlackLevel? 暗电流来源1.raw8为例,单个pixel的有效值是0~255,但是实际AD芯片的精度可能无法将电压值很小的一部分转换出来,芯片厂会刻意添加一个固定的偏 ...
- 大数据环境完全分布式搭建hbase-0.96.2-hadoop2
1.上传hbase安装包 2.解压 3.配置hbase集群,要修改3个文件 (首先zookeeper集群已经安装好了 并且启动 hadoop启动) 注意:要把hadoop的hdfs-site.xml和 ...
- VMware5.5-虚拟交换机
虚拟交换机 即为[VM(ESXI内部.ESXI外部.ESXI之间等)]的各功能.提供的网桥 虚拟机选项负责虚机间的通讯 Vmkernel选项负责主机间的通讯 标准交换机 添加拓扑中vmotion的虚拟 ...
- 学不动了,ECMAScript2018都来了
原文:ECMAScript regular expressions are getting better! 作者: Mathias Bynens: Google V8引擎开发者 译者:Fundebug ...
- js计算本地时间
获取时间戳: 方法一 var dateTime = new Date();//获取本地时间 var nowTime = dateTime.getTime();//获取本地毫秒,即当前时间 var en ...
- 【C语言程序】让用户输入一句话,输出这句话中每个单词含有多少个字母
#include <stdio.h> #define N 100 //宏定义,用N表示100 int main(int argc, char *argv[]) { ; char s ...