企业应用实战之MongoDB入门
课程内容MongoDB的基本介绍【了解】MongoDB的安装与使用【重要】MongoDB的基本命令【了解】MongoDB的集群【扩展】MongoDB的数据备份【扩展】SpringBoot整合MongoDB【重要】一、MongoDB的基本介绍
MongoDB是一个nosql数据库,它有高性能、无模式、文档型的特点。他是nosql数据库中功能最丰富,最像关系数据库的。1、核心概念文档
MongoDB数据库的最小数据集,是由多个键值对有序组合的数据单元,类似于mysql的一行数据记录,一般由json格式指定;集合
由一组文档构成,类似于mysql的表,可以存放多行数据;库
每个数据库都是独立的,有自己的用户,权限,独立存储集合,类似于mysql的库;实例
系统上运行的MongoDB的进程,类似于mysql实例;2、特性面向集合文档存储,适合存储json形式的数据;格式自由,数据格式不固定,数据结构发生变更的同时不会影响程序运行;面向对象的sql查询语句,基本涵盖关系型数据库的所有查询语句;有索引的支持,查询效率更快;支持复制和自动故障转移;可以使用分片集群提升查询性能。3、适用场景应用不需要事务(这一条是最重要的,如果应用的事务的依赖很强,是不能选择该数据库的);数据模型无法确定,经常发生变更;应用的qps达到2000以上;应用存储的数据很大,达到TB级别以上;应用需要大量的地理位置查询或者文本查询。
MongoDB适用于游戏,社交,物流,物联网,视频直播这些场景。二、MongoDB安装与使用1、安装MongoDB
这里的安装是以Windows安装为例的,但实际生产场景大部分都是在linux进行部署。接下来我看介绍一下mongdb的安装步骤;
MongoDB官网:https:www。mongodb。comtrydownloadcommunity1)Windows安装
下载:mongodbwindowsx86645。0。14signed。msi
安装:安装路径不能有中文、空格、以及其他特殊字符;
启动MongoDB服务:
在浏览器打开http:127。0。0。1:27017,有如下提示,则说明安装成功了
ItlookslikeyouaretryingtoaccessMongoDBoverHTTPonthenativedriverport。2)Linux安装先进入mongodb的官网MongoDB下载,选择要下载的版本以及系统,需要注意的是,在安装mongodb之前需要先准备好jdk的环境变量,选择好要下载的版本以后,点击copylink,在linux系统中执行以下命令;
执行:wgethttps:fastdl。mongodb。orglinuxmongodblinuxx8664rhel705。0。11。tgz下载完毕以后,解压该压缩包,接下来需要配置mongodb的环境变量通过vi命令编辑以下文件vietcprofile在该文件中追加以下命令解压完以后mongo的文件夹地址exportMONGODBHOMEoptselfmongodbexportPATHPATH:MONGODBHOMEbin刷新环境变量sourceetcprofile创建对应的mongodb的数据以及日志存储位置,然后启动即可创建mongodb存储位置mkdirmongodbdata进入到创建的文件家中,创建数据和ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a存储文件夹mkdirdatamkdirlogs启动mongodb,指定数据以及ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a存储位置,在解压后的mongodb的bin目录下执行mongoddbpathoptselfmongodbdatadatalogpathoptselfmongodbhomeogsmongodb。logfork3)连接mongodb命令行
将mongosh1。6。1win32x64。zip中bin目录下mongosh。exe和mongoshcryptv1。dll解压到MongoDB安装目录的bin目录下如果是在本机测试,可以简化为mongoshmongoshhost127。0。0。1port27017通过以下命令查看当前存在的数据库showdbsshowdatabases
注意:mongo在创建完成以后会默认存在admin,config,local这三个数据库,其中admin中存储的是mongodb的用户、角色等信息;config中存储的是分片集群基础信息;local中主要存储的是副本集的元数据可视化工具
MongoDB的可视化工具有很多,比如安装过程中的MongoDBCompass、NavicatPremium15、Robo
这里我们使用NavicatPremium15连接演示一下:
安装与破解过程参考:resourcesnavicate15Navicate15激活教程。docx
2、MongoDB基本命令【了解】1)切换或者创建库切换指定库,如果该库不存在,则创建该库,在切换到该库usedatabase;2)创建集合直接创建db。createCollection(name)如果collection不存在,则会先创建collection,这是一种隐式创建方式db。collection。insert({id:1})查询当前库中存在的集合showtables3)文档的增删改查查询集合所有db。collection。find()查询集合的第一个db。collection。findOne()根据条件进行查询,支持传入两个参数第一个参数是过滤条件第二个是控制显示的数据列,1代表显示,0代表不显示,如果不配置,默认显示所有db。collection。find({id:1},{id:1,name:0})使用正则表达式,匹配姓名以a开头的数据db。collection。find({name:a})插入数据db。collection。insert({id:1,name:a})批量插入数据db。collection。insertMany(〔{id:1,name:a},{id:2,name:b}〕)覆盖更新,将id为一的这条数据更新为只有name2,即使原数据存在sex0,执行完以下语句以后,这条语句也会变成只有name2db。correction。update({id:1},{name:2})选择更新,只更新对应的字段,下面的语句只会吧id1的数据中,name修改为2,其它不变但是它只会更新满足条件的第一条数据db。correction。update({id:1},{set:{name:2}})选择更新,将所有id1的数据中的name变更为2,其它数据不变db。correction。update({id:1},{set:{name:2}},{multi:true})删除数据,删除id为1的数据db。collection。remove({id:1})删除全部数据db。collection。remove({})4)统计操作统计所有数据db。collection。count()统计id为1的数据db。collection。count({id:1})5)分页与排序查询前5条数据db。collection。find()。limit(5)查询跳过前5条的其它数据db。collection。find()。skip(5)分页查询,每页显示5条,查询第一页和第二页的数据db。collection。find()。skip(0)。limit(5)db。collection。find()。skip(5)。limit(5)排序,1升序,1降序db。collection。sort({id:1,age:1})6)范围查找gt大于,lt小于,gte大于等于,lte小于等于,ne不等于查询id大于10的数据db。collection。find({id:{gt:10}})查询id大于10小于30的数据,需要注意这里必须使用anddb。collection。find({and:〔{id:{gt:10}},{id:{lt:30}}〕})查询id是1和3的数据db。collection。find({id:{in:〔1,3〕}})7)索引操作创建索引,1升序索引,1降序索引db。collection。createIndex({id:1})查看集合已经创建的索引db。collection。getIndexs()移除指定索引db。collection。dropIndex({id:1})移除集合的全部索引db。collection。dropIndexes()8)加锁操作加锁db。collection。fsyncLock()解锁db。collection。fsyncUnlock()9)事务操作(mongodb不推荐使用事务)插入多条数据db。test1。insertMany(〔{id:1},{id:2}〕);开启事务varsessiondb。getMongo()。startSession();session。startTransaction();获取需要修改的集合varcollsession。getDatabase(test)。getCollection(test1);进行数据更新coll。updateOne({id:1},{set:{name:a}});获取的文档为id:1,name:acoll。findOne({id:1});获取的文档为id:1db。test1。findOne({id:1});事务回滚或者提交session。abortTransaction();
针对事务有以下几个注意点:
1、事务必须在60s内完成,超时将取消
2、涉及事务的分片不能使用仲裁节点
3、事务会影响chunk(在后面的分片集群中会介绍)的迁移效率。在chunk迁移过程中会导致事务提交失败
4、多文档事务中的读操作必须使用主节点
5、readConcern(在后文的复制集中会进行介绍)只应该在事务级别设置,不能设置在每次读写操作。三、MongoDB集群【扩展】1、复制集
复制集指的就是mongodb的集群,它的主要意义是用来实现mongodb的高可用。其结构为一主多从的结构。其中从节点分为三类:
1、从主节点进行数据复制,可以对外部提供数据读取,并且当主节点出现问题以后,可以参与主节点的选举;
2、只能从主节点进行数据复制,但是没有选举功能;
3、只存在选举功能,这类节点是不推荐使用的,造成资源浪费;
复制集具有以下的作用:
数据分发:这种是针对全球性网站类似的情况,将数据分发到不同的服务器,在读取时减少读取时间,提高读取效率;
读写分离:提高并发量,将不同类型的操作压力分到不同的节点上面;
异地容灾:当本地的数据中心出现问题时,切换到异地的数据中心上;1。1、复制集的搭建
在这里简单介绍一下复制集的搭建,本文是在一台服务器上搭建三个mongodb节点来进行模拟,真实生产环境一个复制集的不同节点肯定是分布在不同的服务器上的。创建三个目录,分别为db1,db2,db3,在三个目录下添加mongod。conf配置文件:ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a文件systemLog:ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a为文件格式destination:fileahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a文件的存储路径,不同的节点需要配置不同的路径每个节点对应自己的目录path:datadb1mongod。loglogAppend:truestorage:mongodb数据存储位置,不同的节点需要配置不同的路径每个节点对应自己的目录dbPath:datadb1net:bindIp:0。0。0。0当前节点的端口每个节点对应自己的端口号port:28017replication:复制集名称replSetName:rs0processManagement:fork:true
在启动时需要指定配置文件:mongodfdatadb1mongod。confmongodfdatadb2mongod。confmongodfdatadb3mongod。conf
连接入作为主节点的mongodb,然后执行以下命令:复制集的初始化rs。initiate()添加从节点rs。add(centosvm:28018)rs。add(centosvm:28019)查看当前复制集的状态rs。status()
通过一下命令进行验证:在主节点插入一条数据db。test。insert({id:1})在从节点执行一下命令rs。slaveOk()db。test。find()
接下来用一个最简单的主从结构介绍复制集,一主三从。如小图所示:
在这个赋值集中,数据的同步流程是:当主节点发生写操作时,它的写操作会被记录到oplog中,然后主节点将这个oplog推送给从节点,从节点通过在主节点上打开一个taiable游标读取oplog中的新数据,让后将数据同步到自己节点上,通过这种方式与主节点保持数据一致。
mongdb的复制集两两节点之间每2s发送一次心跳,如果5次心跳未收到,则判断为节点失联。如果失联的节点为主节点,此时就会发起选举,选出新的节点,选举是基于raft一致性算法实现,选举成功的必要条件是超过一半具有投票功能的节点存活。对于上面那个集群,当主节点失联,从节点必须有两个存活才能进行选举(注,复制集最多有50个节点,具有投票权额节点最多有7个)。
在进行选举的时候,能够选取为主节点的从节点必须具有以下的特点:
1)能够与更多的节点建立链接
2)oplog读取的位置最新
3)如果从节点设置了优先级,满足上述要求的优先级最高的节点
针对从节点,还有一些常见选项:
投票权:从节点默认具有投票权,可以关闭,单纯作为复制节点,参数为v;
优先级:优先级越高的节点在选举时同等条件下优先成为主节点,优先级为0的节点无法成为主节点,参数为priority;
隐藏:单纯作为数据备份,无法被应用访问,隐藏节点的优先级必须为0,参数为hidden;
延迟:在复制数据时,复制指定延迟时间之前的数据,与主节点保持时间差,常用来作为数据备份与恢复,参数为slaveDelay,单位为秒。动态修改从节点的信息confrs。conf()conf。members〔2〕。secondaryDelaySecs5conf。members〔2〕。priority0rs。reconfig(conf)1。2、复制集的写策略
复制集的写策略是通过writeConcern来进行配置的,他代表的是多少个节点确认完成才算是吸入成功。当数据写入主节点的时候,主节点会向从节点发送确认消息,如果从节点返回确认成功,才算一个从节点完成了确认,接下来主节点会再向从节点发送写入消息。如下图:
writeConcern的配置方式为:writeConcern:{w:n},你的赋值规则如下:
0:不关心写入结果,当应用发送写入命令以后,直接返回成功;
1:当应用发送写入命令以后,只要写入主节点,则返回成功;
n:大于1小于总结点的一个数值,代表写入主节点以后,需要n1个节点确认成功,才返回成功;
majority:需要保证大多数节点完成确认,即超过总结点数的一半,才返回写入成功,这种方式为推荐方式;
all:全部节点完成确认,才会返回成功;
在写操作这里还有一个特殊的日志文件journal日志文件,它类似于关系型数据库中的事务日志。它的作用为mongodb数据库由于故障宕机以后的快速恢复。设置参数为j,值为true时代表数据要写入journal日志文件才算成功;false代表写操作记录到内存即可。开启该配置以后,肯定会影响一部分写性能,但是它有利于数据库的快速恢复,建议生产环境进行开启。参考下图:
1。3、复制集的读策略
在复制集中只要节点不是隐藏节点或者仅投票节点,这些节点是都可以向外部提供读取权限的,那么我们在读取的时候,应该从哪个节点进行读取呢?这需要我们根据具体的业务情况进行策略配置,配置参数为readPreference。它有一下几种值可供选择:
primary:只从主节点进行读取,这是mongodb的默认值;
primaryPreferred:优先从主节点读取,如果当前主节点不可用,则选择从节点读取;
secondary:只选择从节点进行读取;
secondaryPreferred:优先选择从节点读取,如果从节点不可用,则从主节点读取;
nearest:选择最近的节点,这里的最近指的是地理位置。
这些策略分别适用的场景为:primaryprimaryPreferred:对数据实时性要求较高的业务场景,因为从节点有一个数据复制的过程,会造成数据延迟;secondarysecondartPreferred:适用查询历史数据的业务场景,其中secondary也适用于报表生成这类需要耗费大量资源的业务场景;nearest:适用于地域性较广的应用,进行就近读取,减少传输时间。
readPreferred的配置方式有三种,如下:在配置文件中的mongdb连接串后添加对应参数mongodb:host1:27107,host2:27107?replicaSetrsreadPreferencesecondary在java代码中MongoCollection。withReadPreference(ReadPreferencereadPref)在xshell控制台中db。collection。find()。readPref(secondary)
上面的这种方式,只能指定一类节点,主从节点。但是在实际生产中可能需要根据硬件配置,业务场景来区分要读取哪些节点。这是我们可以通过给不同的mongodb节点打上不同的tag来区分,方式如下:给mongodb打tagconfrs。conf()conf。members〔1〕。tags{type:a}rs。reconfig(conf)应用程序的mongodb配置文件mongodb:host1:27107,host2:27107?replicaSetrstypea
上面介绍了我们要从哪个节点读取数据的方式。但是节点上的那些数据是可以读取的呢?这就需要用到另一个参数readConcern。这个参数的配置值类似于数据库中的隔离级别。它有一下的策略配置,他们的隔离级别由上到下越来越强:
available:读取所有可用的数据,可以直接查询到写入复制集的数据;
local:读取所有可用且属于当前分片的数据,可以直接查询到该分片的数据;
majority:读取在大多数节点上提交的数据,使用该策略可以有效的避免脏读的问题。majority采用mvcc机制来保证数据的读取。通过维护多个快照来链接不同的版本,每个被大多数节点确认过的版本都将是一个快照,快照持续到不再使用的时候才会进行删除。因为该策略是需要写入操作需要大多数节点完成确认,才能够被读取,当本次写入没有被大多数节点确认,主节点就发生回滚,那么本次写入对其它应用是不可见的;
linearizable:可线性化读取文档;
snapshot:读取最近快照中的数据,即每次读取都是从同一个快照中进行读取,那么也就避不会出现脏读,不可重复读以及幻读。下面看一个可重复的例子:开启事务,并且将readConcern设置为快照varsessiondb。getMongo()。startSession();session。startTransaction({readConcern:{level:snapshot},writeConcern:{w:majority}});varcollsession。getDatabase(test)。getCollection(test1);、读取id为1的数据,获得的文档为id:1coll。findOne({id:1});不通过事务对id为1的数据进行更新db。tx。updateOne({id:1},{set:{name:b}});事务外读取id为1的数据,获得的文档为id:1,name:bdb。tx。findOne({id:1});事务内再次读取id为1的数据,获取的文档为id:1,它与第一次读时用的是同一个快照,所以两次读取是一样的coll。findOne({id:1});session。abortTransaction();2、分片集群
分片集群可以理解为是有多个复制集组成一个可以横向扩展的集群,每个复制集中存储不同范围的数据。分片集群主要是为了解决当下数据量日益增大,当一个节点存储大量数据,在进行数据恢复时需要的时间更大的情况以及解决全球化网站数据的存储以及访问问题。我们先来看一下分片集群的架构图,如下:
路由节点:提供分片集群的统一入口,一个mongodb节点即可满足要求,但是为了高可用,还是部署两台,因为它只是起到一个路由转发的功能,不需要进行存储大量数据,使用低配的服务器即可。
配置节点:配置节点的架构就是一个普通的复制集,提供集群元数据的存储,分片数据分布的映射。我们一般采用hash的方式作为分片依据,如果使用范围作为分片标准,会导致数据分布不均匀。
数据节点:数据节点以复制集作为存储单位,每个数据节点的数据是不重复的。一个数据节点就是1片,最大能达到1024分片。当数据量越来越大时,我们可以通过添加数据节点的方式进行横向扩容。每个分片的大小最好不要超过3T,尽可能保证2T每片。
分片数量可以由一下三个维度来进行计算,然后取其最大值:
1)总存储量单台服务器容量;
2)工作集大小单台服务器硬盘0。6;
3)并发总数单服务器并发数0。7
下面介绍一下分片集群中的一些概念:
shardkey:片键,文档中的一个字段;
doc:文档,包含shardkey的一行数据;
chunk:块,包含多个文档;
shard:分片,包含多个chunk;
cluster:集群,包含多个cluster;
分片集群的一个好处就是可以进行动态扩容,当我们进行动态扩容时,由于分片变多,原有数据的存储位置就需要发生变更,此时就会发生chunk的移动,这就是mongodb的自动均衡机制。四、MongoDB的数据备份【扩展】
mongodb的数据备份方式有两种:延迟节点备份以及全量备份oplog。接下来我们做一个简单的介绍,一般这些工作是由运维完成。1、延迟节点备份
在上面的介绍中,我们知道从节点可以配置延迟同步时间,我们可以利用这一机制进行数据备份,能够让主节点恢复到指定时间之前的状态。这种情况适用于误操作时进行数据恢复。
此时我们可以利用节点2以及oplog,将主节点的数据恢复到安全范围内的任意一个节点的数据状态。2、全量备份oplog
全量备份的方式有以下三种:
复制文件:复制数据库文件,在复制文件的时候需要先关闭或者锁定数据库,该复制过程要在从节点上完成,最好是设置为hidden的节点,这样对整个应用不会产生影响;
文件快照:mongodb支持使用文件快照直接获取数据库某一时刻的镜像,快照过程不用统计,但是必须要要与journal在一起;
mongodump:最灵活的一种备份方式,但是消耗时间长。在t1时间点进行备份,会在t3时间点备份完成,索引mongodunp备份的文件不是某一个时间点的数据,而是一个时间段的数据。
当我们要进行数据恢复时,可以找到离恢复时间点最近的一次全量备份,然后再加上这段时间范围之内的oplog,即可完成数据恢复。如果全量备份的频率较高,并且对数据的完整性要求不高的话,可以只恢复到最近的一次全量备份。五、SpringBoot整合MongoDB【重要】1、环境搭建1)创建项目springbootmongodbdemo
2)导入依赖parentgroupIdorg。springframework。bootgroupIdspringbootstarterparentartifactIdversion2。2。5。RELEASEversionparentdependencies!引入mongodbdependencygroupIdorg。springframework。bootgroupIdspringbootstarterdatamongodbartifactIddependency!引入lombokdependencygroupIdorg。projectlombokgroupIdlombokartifactIddependency!引入webdependencygroupIdorg。springframework。bootgroupIdspringbootstarterwebartifactIddependency!引入单元测试dependencygroupIdorg。springframework。bootgroupIdspringbootstartertestartifactIdscopetestscopedependencydependencies3)创建mongodb用户useadmindb。createUser({user:wuchangyong,pwd:123456,roles:〔{role:readWriteAnyDatabase,db:admin}〕});4)yml配置spring:application:name:springbootmongodbdemodata:mongodb:authenticationdatabase:admin登录用户所在的数据库host:127。0。0。1数据库的ip地址port:27017MongoDB端口号username:wuchangyong用户名password:123456用户密码必须加上双引号database:test指定使用的数据库,若不存在该库会自动创建logging:level:org。springframework。data。mongodb。core:debug控制台输出ahrefhttps:www。bs178。comrizhitargetblankclassinfotextkey日志a记录5)启动类packagecn。itsource。mongodbdemo;importorg。springframework。boot。SpringApplication;importorg。springframework。boot。autoconfigure。SpringBootApplication;SpringBootApplicationpublicclassDemoApplication{publicstaticvoidmain(String〔〕args){SpringApplication。run(DemoApplication。class,args);}}2、代码操作1)domain实体类SpringDataMongoDB会自动以当前domain实体类名称作为数据集合名称,相当于表名称若添加了Document(employee)注解,也可指定其他名称Document表示一个文档,实则是转化为json格式的字符串与SpringDataElasticsearch的文档实体类一致NoArgsConstructorAllArgsConstructorAccessors(chaintrue)链式操作设置对象属性值DataDocument(employee)publicclassEmployee{Id必须指定id列privateLongid;privateStringname;privateIntegerage;privateStringphone;privateDatebirthDay;}2)dao层接口
这里的dao层接口与SpringDataJpa、SpringDataElasticsearch都是一致的写法。这种写法不需要实现类,很显然和我们之前讲的都一样采用了动态代理。两个泛型:实体类和主键字段类型publicinterfaceEmployeeRepositoryextendsMongoRepositoryEmployee,Long{}3、基本CRUD1)测试新增文档SpringBootTestRunWith(SpringRunner。class)publicclassDemoApplicationTests{AutowiredprivateEmployeeRepositoryemployeeRepository;测试新增文档throwsExceptionTestpublicvoidtestSave()throwsException{EmployeeemployeenewEmployee()。setId(1L)。setName(张三丰)。setAge(22)。setBirthDay(newDate())。setPhone(18996157300);employeeRepository。save(employee);}}
2)测试修改文档测试修改文档save方法传入的实体对象其id在mongodb中存在就是修改,不存在就是新增throwsExceptionTestpublicvoidtestSave2()throwsException{EmployeeemployeenewEmployee()。setId(1L)。setName(张三丰123)。setAge(33)。setBirthDay(newDate())。setPhone(18996157301);employeeRepository。save(employee);}3)通过id获取文档通过文档id查询throwsExceptionTestpublicvoidtestFindById()throwsException{System。out。println(employeeRepository。findById(1L)。get());}4)通过id删除文档通过文档id删除throwsExceptionTestpublicvoidtestDeleteById()throwsException{employeeRepository。deleteById(1L);}4、高级查询分页排序1)whereis条件查询where。。。is。。。throwsExceptionTestpublicvoidtestQueryByWhereIs()throwsException{QueryquerynewQuery();where。。。is。。。相当于where??query。addCriteria(Criteria。where(age)。is(12));EmployeeemployeemongoTemplate。findOne(query,Employee。class);System。out。println(employee);}2)wherein条件查询where。。。in。。。throwsExceptionTestpublicvoidtestQueryByWhereIn()throwsException{ListIntegeragesArrays。asList(11,12,22,33);QueryquerynewQuery();where。。。in。。。相当于where?in(?)query。addCriteria(Criteria。where(age)。in(ages));ListEmployeeemployeesmongoTemplate。find(query,Employee。class);System。out。println(employees);}3)字符模糊匹配字符串模糊查询where。。。regex。。。throwsExceptionTestpublicvoidtestQueryByLike()throwsException{QueryquerynewQuery();where。。。regex。。。相当于where?like?query。addCriteria(Criteria。where(name)。regex(张));ListEmployeeemployeesmongoTemplate。find(query,Employee。class);System。out。println(employees);}4)范围查询范围查询where。。。gte。。。lte。。。gte表示大于等于lte表示小于等于gt表示大于lt表示小于throwsExceptionTestpublicvoidtestQueryByRange()throwsException{QueryquerynewQuery();where。。。gte。。。lte相当于where??and??query。addCriteria(Criteria。where(age)。gte(12)。lte(33));ListEmployeeemployeesmongoTemplate。find(query,Employee。class);System。out。println(employees);}5)不返回指定字段指定字段不返回throwsExceptionTestpublicvoidtestQueryExculdeFields()throwsException{QueryquerynewQuery();exclude表示排除query。fields()。exclude(age);ListEmployeeemployeesmongoTemplate。find(query,Employee。class);System。out。println(employees);}6)分页查询排序分页查询排序throwsExceptionTestpublicvoidtestQueryPageAndSort()throwsException{QueryquerynewQuery();分页:当前页码从0开始query。with(PageRequest。of(0,2));排序:Sort。Direction。ASC表示升序,Sort。Direction。DESC表示降序query。with(Sort。by(Sort。Direction。DESC,age));ListEmployeeemployeesmongoTemplate。find(query,Employee。class);System。out。println(employees);}