MongoDB讲义
为0 何要学习MongoDB
灵活的数据模型
MongoDB的文档数据模型使开发人员和数据科学家能够轻松地在数据库中存储和合并任何结构的数据,而无需放弃复杂的验证规则来保障数据质量。
丰富的编程和查询模型
MongoDB查询语言和丰富的二级索引能使开发人员以多种方式来构建查询和分析数据的应用程序。数据可以通过单键,范围,文本搜索,图形和地理空间以及复杂的管道聚集和MapReduce作业访问到,且能以毫秒为单位返回响应。
什么是MongoDB
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解 决方案。 MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系 数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大 的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查 询的绝大部分功能,而且还支持对数据建立索引。
MongoDB使用原理
所谓“面向集合”(Collection-Oriented),意思是数据被分组存储在数据集中,被称为一个集合(Collection)。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库(RDBMS)里的表(table),不同的是它不需要定义任何模式(schema)。Nytro MegaRAID技术中的闪存高速缓存算法,能够快速识别数据库内大数据集中的热数据,提供一致的性能改进。模式自由(schema-free),意味着对于存储在mongodb数据库中的文件,我们不需要知道它的任何结构定义。如果需要的话,你完全可以把不同结构的文件存储在同一个数据库里。存储在集合中的文档,被存储为键-值对的形式。键用于唯一标识一个文档,为字符串类型,而值则可以是各种复杂的文件类型。我们称这种存储形式为BSON(Binary Serialized Document Format)。
MongoDB持久化原理
mongodb与mysql不同,mysql的每一次更新操作都会直接写入硬盘,但是mongo不会,做为内存型数据库,数据操作会先写入内存,然后再会持久化到硬盘中去,那么mongo是如何持久化的呢
mongodb在启动时,专门初始化一个线程不断循环(除非应用crash掉),用于在一定时间周期内来从defer队列中获取要持久化的数据并写入到磁盘的journal(日志)和mongofile(数据)处,当然因为它不是在用户添加记录时就写到磁盘上,所以按mongodb开发者说,它不会造成性能上的损耗,因为看过代码发现,当进行CUD操作时,记录(Record类型)都被放入到defer队列中以供延时批量(groupcommit)提交写入,但相信其中时间周期参数是个要认真考量的参数,系统为90毫秒,如果该值更低的话,可能会造成频繁磁盘操作,过高又会造成系统宕机时数据丢失过。
MongoDB 适用于以下场景
MongoDB 的主要目标是在键/值存储方式(提供了高性能和高度伸缩性)和传统的RDBMS 系统(具有丰富的功能)之间架起
MongoDB的应用:
● 网站数据:Mongo 非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
● 缓存:由于性能很高,Mongo 也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo 搭建的持久化缓存层可以避免下层的数据源过载。
● 大尺寸、低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储。
● 高伸缩性的场景:Mongo 非常适合由数十或数百台服务器组成的数据库,Mongo 的路线图中已经包含对MapReduce 引擎的内置支持。
● 用于对象及JSON 数据的存储:Mongo 的BSON 数据格式非常适合文档化格式的存储及查询。
MongoDB 的使用也会有一些限制,例如,它不适合于以下几个地方。
● 高度事务性的系统:例如,银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序。
● 传统的商业智能应用:针对特定问题的BI 数据库会产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择。
● 需要SQL 的问题。
Mongo不适用的场景如下:
要求高度事务性的系统。
传统的商业智能应用。
复杂的跨文档(表)级联查询。
如何安装MongoDB
MongoDB和Redis的区别
MongoDB 更类似 MySQL,支持字段索引、游标操作,其优势在于查询功能比较强大,擅长查询 JSON 数据,能存储海量数据,但是不支持事务。
MongoDB的特点
(1)面向文档(2)高性能(3)高可用(4)易扩展(5)丰富的查询语言
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构
支持持久化操作,可以进行aof及rdb数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。
Redis和MongoDB的区别:
- 性能
都比较高,性能对我们来说应该都不是瓶颈
总体来讲,TPS方面redis要大于mongodb
- 操作的便利性
redis丰富一些,数据操作方面,redis更好一些,较少的网络IO次数
mongodb支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富
- 3、内存空间的大小和数据量的大小
redis在2.0版本后增加了自己的VM特性,突破物理内存的限制;可以对key value设置过期时间(类似memcache)
mongoDB适合大数据量的存储,依赖操作系统VM做内存管理,吃内存也比较厉害,服务不要和别的服务在一起
- 4、可用性(单点问题)
对于单点问题,
redis,依赖客户端来实现分布式读写;主从复制时,每次从节点重新连接主节点都要依赖整个快照,无增量复制,因性能和效率问题,所以单点问题比较复杂;不支持自动sharding,需要依赖程序设定一致hash 机制。
一种替代方案是,不用redis本身的复制机制,采用自己做主动复制(多份存储),或者改成增量复制的方式(需要自己实现),一致性问题和性能的权衡
mongoDB支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制。
- 5、可靠性(持久化)
对于数据持久化和数据恢复,
redis支持(快照、AOF):依赖快照进行持久化,aof增强了可靠性的同时,对性能有所影响
MongoDB从1.8版本开始采用binlog方式支持持久化的可靠性
- 6、数据一致性(事务支持)
redis事务支持比较弱,只能保证事务中的每个操作连续执行
mongoDB不支持事务
- 7、数据分析
mongoDB内置了数据分析的功能(mapreduce),其他不支持
- 8、应用场景
redis:数据量较小的更性能操作和运算上
MongoDB:主要解决海量数据的访问效率问题
MongoDB的语法
数据库名称命名规范
不能是空字符串("")。
不得含有' '(空格)、.、$、/、\和\0 (空字符)。
应全部小写。
最多64字节。
文档名称命名规范
键不能含有\0 (空字符)。这个字符用来表示键的结尾。
.和$有特别的意义,只有在特定环境下才能使用。
以下划线"_"开头的键是保留的(不是严格要求的)。
需要注意的是:
文档中的键/值对是有序的。
文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
MongoDB区分类型和大小写。
MongoDB的文档不能有重复的键。
文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
集合名称命名规范
集合名不能是空字符串""。
集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
集合名不能以"system."开头,这是为系统集合保留的前缀。
用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。
MongoDB的基本操作命令
show dbs: 查询所有数据库
自带三个数据库
admin: 从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
config: 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。
use 数据库名: 创建并且选中数据库,数据库已经存在则直接选中
db: 查询当前选择的数据库
db.createCollection("集合名"): 创建集合
show collections: 查询当前库中的集合
db.集合名.drop(): 删除集合
db.dropDatabase(): 删除当前选中的数据库
MongoDB支持的常见类型
String(字符串): mongodb中的字符串是UTF-8有效的
Integer(整数): 存储数值。整数可以是32位或64位,具体取决于您的服务器
Double(双精度): 存储浮点值
Boolean(布尔): 存储布尔(true/false)值
Arrays(数组): 将数组或列表或多个值存储到一个键中
Timestamp(时间戳): 存储时间戳
Date(日期): 以UNIX时间格式存储当前日期或时间
Object ID(对象ID) : 存储文档ID
Object(对象): 嵌入式文档
Null (空值): 存储Null值
新增操作
当操作成功时,集合会给文档生成一个_id字段,该字段就是文档的主键,也能在插入数据时自己指定该字段的值, 但是不建议这样做
语法:
db.集合名.insert( 文档 ) : 往集合中插入文档(可以是单个文档,也可以是多个)
如:
插入单个文档
需求:往users集合中插入一个文档,id=1001,name=hello,age=18
db.users.insert({"id": NumberLong(1001), "name": "hello", age: NumberInt(18)})
插入多个文档
需求:往users集合中插入两个文档,{id=2,name=tony,age=20}
{id=3,name=lili,age=24}
db.users.insert([{id: 2, name: "tony", age: 20},{id: 3, name: "lili", age: 24}])
通过数组也可以一次性插入多条数据。
步骤:
1、先创建数组
2、将数据放在数组中
3、一次 insert 到集合中
如:
var arr = [];
for(var i=1 ; i<=20000 ; i++){
arr.push({num:i});
}
db.numbers.insert(arr);
db.集合名.insertOne():向指定集合中插入一条文档数据
如:
db.users.insertOne({"date":new Date(),"user":{"id":33,"name":"meinv"}})
db.users.insertOne([{"id":2},{"id":3}])
db.集合名.insertMany():向指定集合中插入多条文档数据
如:
db.users.insertMany([{"date":new Date(),"user":{"id":89,"name":"yo"}}])
db.users.insertMany([
{"date":new Date(),"user":{"id":90,"name":"haha"}},
{"date":new Date(),"user":{"id":91,"name":"heihei"}},
{"date":new Date(),"user":{"id":92,"name":"hehe"}}
])
更新操作
db.集合名.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
参数说明:
query : update的查询条件,类似sql
update查询内where后面的。
update : update的对象和一些更新的操作符(如inc...)等,也可以理解为sql update查询内set后面的
upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是 false,不插入。
multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多 条记录全部更新。
writeConcern :可选,抛出异常的级别
db.集合名.update(...):更新集合中的数据(一条或者多条)
如:
更新单个文档
需求:修改name=tony的文档信息,把age改为19,如果没有符合条件的文档,就不操作,如果有很多符合条件的,只修改第一个文档信息。
db.users.update({name:"tony"},{$set:{age:19}},false,false)
更新多个文档
需求:修改users集合中所有符合条件name=tony的文档信息,把age改为22,如果没有符合条件的文档,就不操作,如果有很多符合条件的,全部修改
db.users.update({name:"tony"},{$set:{age:22}},false,true)
针对更新操作的语法,我们还有对应的简写方式:
只能修改单个文档:
db.集合名.updateOne( ... ):更新集合中的一条数据
需求:把users集合中符合条件name=tony的文档,把文档中的age修改为30。如果符合条件的文档有很多,该方法只会修改第一个文档。
db.users.updateOne({name: "tony"}, {$set: {age: 30}})
可以修改多个文档:
db.集合名.updateMany( ... ):更新集合中的多条数据
需求:把users集合中符合条件name=tony的文档,把文档中的age修改为50。如果符合条件的文档有很多,该方法会修改所有符合条件的文档。
db.users.updateMany({name: "tony"}, {$set: {age: 50}})
删除操作
db.集合名.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
} )
参数说明:
query :(可选)删除的文档的条件。
justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除 所有匹配条件的文档。
writeConcern :(可选)抛出异常的级别
db.集合名.remove():根据条件删除集合中的数据(一条或者多条)
删除单个文档
需求:删除users集合中id为11的文档信息
删除多个文档
需求:删除users集合中name=tony的文档信息。
只能根据条件删除单个文档:
db.集合名.deleteOne( ... ):根据条件只删除符合条件的数据中的第一个
需求:删除users集合中name=tony的文档信息。如果匹配到很多文档信息,只会删除第一个文档信息。
可以根据条件删除多个文档:
db.集合名.deleteMany( ... ):根据条件删除所有符合条件的数据
需求: 删除users集合中,符合name=tony的所有文档信息。
查询操作
基本查询
查询所有
db.集合名.find()
db.集合名.find().pretty()
需求:查询所有文档
db.users.find()
需求:查询所有文档,并且格式化打印
db.users.find().pretty()
根据条件查询
db.集合名.find(query, projection)
query :可选,使用查询操作符指定查询条件
projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
需求:查询所有name=tony的文档信息
db.users.find({name:"tony"})
排序查询
db.集合名.find().sort({字段: 1}) 按照字段升序排列
db.集合名.find().sort({字段: -1}) 按照字段降序排列
需求:查询users集合的所有数据,并按照年龄升序进行排序
db.users.find().sort({age: 1})
需求:查询users集合中的所有数据,先按照年龄升序排序,然后再按照id降序排序
db.users.find().sort({age:1,id:-1})
分页查询
sikp(num) 跳过num个文档,相当于start
limit(num) 限制显示num个文档,相当于pageSize
需求:按照年龄降序排列,查询第2页,每页显示3个
db.users.find().sort({age:-1}).skip(3).limit(3)
高级查询
等值查询
find({字段: 值}) 查询集合中符合字段和值都相等的文档信息
需求:查询users集合中年龄为29岁的文档信息
db.users.find({age:29})
比较查询
db.集合名.find({字段:{比较运算符:值....}})
(>) 大于 - $gt
需求:查询users集合中年龄大于29岁的文档信息
db.users.find({age:{$gt:29}})
(<) 小于 - $lt
需求:查询users集合中年龄大于26岁,小于29岁的文档信息
db.users.find({age:{lt:29}})
(>=) 大于等于 - $gte
需求:查询users集合中年龄大于等于26岁的文档信息
db.users.find({age:{$gte:26}})
(<= ) 小于等于 - $lte
需求:查询users集合中年龄大于等于26岁,并且小于等于29岁的文档信息。
db.users.find({age:{gte:26}})
(!=) 不等 - $ne
需求:查询users集合中name不是tony的文档信息
db.users.find({name:{$ne:"tony"}})
集合运算 - $in 如:{name: {$in: ["xiaoyao","bunny"]}}
需求:查询users集合中id为 3,4,6的文档信息
db.users.find({id:{$in:[3,4,6]}})
判断存在 - $exists 如:{name: {$exists:true}}
需求:返回users集合中符合name字段存不存在的数据文档信息
db.users.find({name:{$exists:true}})
如果设置成true, 返回含有name字段的文档信息
db.users.find({name:{$exists:false}})
如果设置成false,返回不含有name字段的文档信息
逻辑查询
find({逻辑操作符: [条件1, 条件2, ...]})
(&&) 与 - $and
需求:获取users集合中name为tony且age大于等于20岁的文档信息
db.users.find({
$and:[
{name:"tony"},
{age:{$gte:20}}
]
})
(||) 或 - $or
需求:获取users集合中name为tony或者age大于等于29岁的文档信息
db.users.find({
$or:[
{name:"tony"},
{age:{$gte:29}}
]
})
(!) 非 - $not
需求:获取users集合中age小于等于20,或者age字段不存在的文档信息
db.users.find({age:{gt:20}}})
模糊查询
{name: {$regex: /^.*keyword.*$/}} keyword作为关键字,进行模糊匹配
MongoDB的模糊查询使用的是正则表达式的语法 如:{name: {/}}
实际上MongoDB也是不擅长执行模糊查询的,在实际开发中也是不使用的,该功能了解即可
需求:获取users集合中,name字段中包含tony的文档信息
db.users.find({name:{/}})
设置MongoDB的用户和密码
以下操作必须在cmd命令行中操作,执行以下命令
//1.选中admin数据库
use admin
//2.往里面添加一个超级管理员账号
db.createUser({user:"root", pwd: "admin", roles:["root"]})
//user:账号 pwd:密码 roles:角色->root超级管理员
修改MongoDB的配置文件:安装目录/Server/bin/mongod.cfg
约在29行位置,配置开启权限认证
security:
authorization: enabled
完成上面配置后重启服务器
SpringBoot整合MongoDB
数据:
db.users.insert({"id":NumberLong(1),"name":"tony","age":NumberInt(18)})
db.users.insert({"id":NumberLong(2),"name":"tony","age":NumberInt(20)})
db.users.insert({"id":NumberLong(3),"name":"zhang quan dan","age":NumberInt(33)})
db.users.insert({"id":NumberLong(4),"name":"zhang kun","age":NumberInt(26)})
db.users.insert({"id":NumberLong(6),"name":"cai xv kun","age":NumberInt(29)})
db.users.insert({"id":NumberLong(7),"name":"jia nai liang","age":NumberInt(25)})
db.users.insert({"id":NumberLong(8),"name":"fu rong wang","age":NumberInt(28)})
db.users.insert({"id":NumberLong(9),"name":"wang da","age":NumberInt(31)})
db.users.insert({"id":NumberLong(10),"name":"wang tony","age":NumberInt(32)})
db.users.insert({"id":NumberLong(11),"name":"tony","age":NumberInt(26)})
pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--spring boot data mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
User:
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
@Document("users")//设置文档所在的集合
public class User {
@Id
private ObjectId _id; //文档的id使用ObjectId类型来封装,并且贴上@Id注解
private Long id;
private String name;
private Integer age;
}
UserMongoRepository:
/**
* 自定义一个接口继承MongoRepository,
* 泛型1:domain类型
* 泛型2:主键类型
* 贴上@Repository注解,底层会创建出动态代理对象,交给Spring管理
*/
@Repository
public interface UserMongoRepository extends MongoRepository<User, ObjectId> {
}
使用已经提供的API方法,进行DML操作:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class MongoDBTest {
@Autowired
private UserMongoRepository userMongoRepository;
//保存单个对象
@Test
public void testSave() throws Exception {
User user = new User();
user.setAge(184);
user.setName("ruhuaQ");
userMongoRepository.save(user);
}
//保存多个对象
@Test
public void testSaveMore() throws Exception {
Iterable<User> iterable = new ArrayList<>();
User user = new User();
user.setAge(18);
user.setName("heihei");
User user1 = new User();
user1.setAge(19);
user1.setName("haha");
((ArrayList<User>) iterable).add(user);
((ArrayList<User>) iterable).add(user1);
userMongoRepository.saveAll(iterable);
}
// 修改
@Test
public void testUpdate() throws Exception {
User user = new User();
user.setId(14L);
user.setName("haha");
user.setAge(4);
user.set_id(new ObjectId("5d614d91783671106f16d9f7"));
userMongoRepository.save(user);
}
// 删除
// 根据对象删除 必须要设置 _id
@Test
public void testDeleteByObject() throws Exception {
User user = new User();
user.setAge(184);
user.setName("ruhuaQ");
user.set_id(new ObjectId("5d614cc978367110673a5b3c"));
userMongoRepository.delete(user);
}
// 根据id删除
@Test
public void testDeleteById() throws Exception {
userMongoRepository.deleteById(new ObjectId("5d6145bb783671100737b424"));
}
}
使用已经提供的API方法,进行DQL操作:
//查询所有数据
@Test
public void testFindAll() throws Exception {
List<User> userList = userMongoRepository.findAll();
userList.forEach(System.out::println);
}
// 查询所有数据并按照age降序排序
@Test
public void testFindAllAndSortDesc() throws Exception {
// List<User> users = userMongoRepository.findAll(Sort.by(Sort.Order.desc("age")));//方式一
List<User> users = userMongoRepository.findAll(Sort.by(Sort.Direction.DESC, "age"));//方式二
users.forEach(System.out::println);
}
// 查询所有数据并按照age升序排序
@Test
public void testFindAllSortAsc() throws Exception {
// List<User> usersAsc = userMongoRepository.findAll(Sort.by("age")); // 方式一
// List<User> usersAsc = userMongoRepository.findAll(Sort.by(Sort.Order.asc("age")));//方式二
List<User> usersAsc = userMongoRepository.findAll(Sort.by(Sort.Direction.ASC, "age"));//方式三
usersAsc.forEach(System.out::println);
}
// 根据ObjectId 查询对应的user信息
@Test
public void testFindById() throws Exception {
Optional<User> userOptional = userMongoRepository.findById(new ObjectId("5d614d91783671106f16d9f7"));
System.out.println(userOptional);
}
// 根据多个ObjectId 查询对应的user信息
@Test
public void testByIds() throws Exception {
Iterable<ObjectId> objectIds = new ArrayList<>();
((ArrayList<ObjectId>) objectIds).add(new ObjectId("5d614d91783671106f16d9f7"));
((ArrayList<ObjectId>) objectIds).add(new ObjectId("5d610458ab75afa90a18e82b"));
((ArrayList<ObjectId>) objectIds).add(new ObjectId("5d610458ab75afa90a18e82d"));
Iterable<User> usersByIds = userMongoRepository.findAllById(objectIds);
usersByIds.forEach(System.out::println);
}
// 根据自定义条件查询所有符合条件的user的信息
@Test
public void testFindAllByCondition() throws Exception {
User user = new User();
user.setAge(26);
List<User> list = userMongoRepository.findAll(Example.of(user));
list.forEach(System.out::println);
}
// 根据自定义的条件查询符合条件的user的信息但是只获取第一个作为结果
@Test
public void testFindOne() throws Exception {
User user = new User();
user.setAge(26);
user.setName("zhang kun");
Optional<User> user1 = userMongoRepository.findOne(Example.of(user));
System.out.println("user1 = " + user1);
}
// 获取第一页的数据,每页显示3条
@Test
public void testFindPage() throws Exception {
// 第一个参数表示获取第几页,页码是从0开始的
// 第二个参数表示一页显示多少条数据。
Page<User> users = userMongoRepository.findAll(PageRequest.of(0, 3));
users.forEach(System.out::println);
}
// 先排序,然后再获取第一页的数据,每页显示3条
@Test
public void testFindPageAndSort() throws Exception {
// 第一个参数表示获取第几页,页码是从0开始的
// 第二个参数表示一页显示多少条数据。
// 第三个参数表示排序
Page<User> users = userMongoRepository.findAll(PageRequest.of(0, 3, Sort.by(Sort.Order.desc("age"))));
users.forEach(System.out::println);
}
自定义查询方法:
/**
* 自定义一个接口继承MongoRepository,
* 泛型1:domain类型
* 泛型2:主键类型
* 贴上@Repository注解,底层会创建出动态代理对象,交给Spring管理
*/
@Repository
public interface UserMongoRepository extends MongoRepository<User, ObjectId> {
// 根据name 和 age 进行查找 SQL:where name=? and age=?
User findByNameAndAge(String name, Integer age);
// 根据name 或者 age 进行匹配 SQL:where name=? or age=?
List<User> findByNameOrAge(String name, Integer age);
// 根据name进行查找 SQL:where name = ?
List<User> findByName(String name);
// 根据name进行查找 SQL:where name = ? (忽略大小写)
List<User> findByNameIgnoreCase(String name);
// 根据age的范围进行查找 SQL:where age between ? and ?
List<User> findByAgeBetween(Integer min, Integer max);
// 根据age小于指定值的数据 SQL:where age < ?
List<User> findByAgeLessThan(Integer age);
List<User> findByAgeBefore(Integer age);
// 根据age小于等于指定值的数据 SQL:where age <= ?
List<User> findByAgeLessThanEqual(Integer age);
// 根据age大于指定值的数据 SQL:where age > ?
List<User> findByAgeGreaterThan(Integer age);
List<User> findByAgeAfter(Integer age);
// 根据age大于等于指定值的数据 SQL:where age >= ?
List<User> findByAgeGreaterThanEqual(Integer age);
// 查找name为null的数据 SQL:where name is null
List<User> findByNameIsNull();
// 查找name不为null的数据 SQL:where name is not null
List<User> findByNameIsNotNull();
// 根据name字段进行模糊查询 SQL:where name like ?
List<User> findByNameLike(String name);
//根据name字段进行模糊查询(取反) SQL:where name not like ?
List<User> findByNameNotLike(String name);
// 查询name中以指定条件开头的数据 SQL:where name like '?%'
List<User> findByNameStartingWith(String name);
// 查询name中以指定条件结尾的数据 SQL:where name like '%?'
List<User> findByNameEndingWith(String name);
// 查询name中包含了指定条件的数据 SQL:where name like '%?%'
List<User> findByNameContaining(String name);
// 查询符合指定年龄的数据,并按照id的值升序排序 SQL:where id = ? order by Id
List<User> findByAgeOrderById(Integer age);
// 查询符合指定年龄的数据,并按照id的值降序排序 SQL:where id = ? order by Id [desc]
List<User> findByAgeOrderByIdDesc(Integer age);
// 查询name不等于指定条件的数据 SQL:where name != ?
List<User> findByNameNot(String name);
// 查询指定id的数据 SQL:where id in ( ... )
List<User> findByIdIn(List<Long> ids);
// 查询指定id的数据(取反) SQL:where id not in ( ... )
List<User> findByIdNotIn(List<Long> ids);
// 查询指定条件为true的数据 SQL:where Xx = true
//List<User> findByXxTrue();
// 查询指定条件为false的数据 SQL:where Xx = false
// List<User> findByXxFalse();
}
测试自定义查询的方法:
// 查找name=tony,age=18的user信息
@Test
public void test1() throws Exception {
User user = userMongoRepository.findByNameAndAge("tony", 18);
System.out.println("user = " + user);
}
// 查询name=tony 或者 age=33的user信息
@Test
public void test2() throws Exception {
List<User> list = userMongoRepository.findByNameOrAge("tony", 33);
list.forEach(System.out::println);
}
// 查询name=tony的user信息
@Test
public void test3() throws Exception {
List<User> list = userMongoRepository.findByName("tony");
list.forEach(System.out::println);
}
// 查询name=tony的user信息(tony忽略大小写)
@Test
public void test4() throws Exception {
List<User> list = userMongoRepository.findByNameIgnoreCase("tony");
list.forEach(System.out::println);
}
// 查询age 在28 到 33 的user信息(注意不包括28和33)
@Test
public void test5() throws Exception {
List<User> list = userMongoRepository.findByAgeBetween(28, 33);
list.forEach(System.out::println);
}
// 查询 age 小于 28 的user信息
@Test
public void test6() throws Exception {
List<User> list = userMongoRepository.findByAgeLessThan(28);
list.forEach(System.out::println);
}
// 查询 age 小于 28 的user的信息
@Test
public void test7() throws Exception {
List<User> list = userMongoRepository.findByAgeBefore(28);
list.forEach(System.out::println);
}
// 查询age 小于等于 26 的user信息
@Test
public void test8() throws Exception {
List<User> list = userMongoRepository.findByAgeLessThanEqual(26);
list.forEach(System.out::println);
}
// 查询age 大于 26的user信息
@Test
public void test9() throws Exception {
List<User> list = userMongoRepository.findByAgeGreaterThan(26);
list.forEach(System.out::println);
}
// 查询 age 大于 26的user信息
@Test
public void test10() throws Exception {
List<User> list = userMongoRepository.findByAgeAfter(26);
list.forEach(System.out::println);
}
// 查询age 大于等于26的user信息
@Test
public void test11() throws Exception {
List<User> list = userMongoRepository.findByAgeGreaterThanEqual(26);
list.forEach(System.out::println);
}
// 查询name=null的user信息
@Test
public void test12() throws Exception {
List<User> list = userMongoRepository.findByNameIsNull();
list.forEach(System.out::println);
}
// 查询name!=null的user信息
@Test
public void test13() throws Exception {
List<User> list = userMongoRepository.findByNameIsNotNull();
list.forEach(System.out::println);
}
// 查询 name 中包含tony的user信息
@Test
public void test14() throws Exception {
List<User> list = userMongoRepository.findByNameLike("tony");
list.forEach(System.out::println);
}
// 查询name中没有包含tony的user信息
@Test
public void test15() throws Exception {
List<User> list = userMongoRepository.findByNameNotLike("tony");
list.forEach(System.out::println);
}
//查询name以to字母开头的user信息
@Test
public void test16() throws Exception {
List<User> list = userMongoRepository.findByNameStartingWith("to");
list.forEach(System.out::println);
}
// 查询name以n字母结尾的user信息
@Test
public void test17() throws Exception {
List<User> list = userMongoRepository.findByNameEndingWith("n");
list.forEach(System.out::println);
}
// 查询name中包含tony的user信息
@Test
public void test18() throws Exception {
List<User> list = userMongoRepository.findByNameContaining("tony");
list.forEach(System.out::println);
}
// 查询年龄为26的user信息并以id的值升序排序
@Test
public void test19() throws Exception {
// List<User> list = userMongoRepository.findByAgeOrderById(26);
List<User> list = userMongoRepository.findByAgeOrderByIdDesc(26);
list.forEach(System.out::println);
}
// 查询name 不为tony的user 信息
@Test
public void test20() throws Exception {
List<User> list = userMongoRepository.findByNameNot("tony");
list.forEach(System.out::println);
}
// 查询id为1,2,3,4的user信息
@Test
public void test21() throws Exception {
List<User> list = userMongoRepository.findByIdIn(Arrays.asList(1L, 2L, 3L, 4L));
list.forEach(System.out::println);
}
// 查询id不为 2,3,4,5的user信息
@Test
public void test22() throws Exception {
List<User> list = userMongoRepository.findByIdNotIn(Arrays.asList(2L, 3L, 4L, 5L));
list.forEach(System.out::println);
}
使用MongoTemplate灵活的操作MongoDB
该对象有SpringBoot完成了自动配置,存入Spring容器中,我们直接注入就可以使用了,依靠该对象能完成任何的
MongoDB操作,一般和MongoRepository分工合作,多数用于复杂的高级查询以及底层操作
Query对象用于封装查询条件,配合Criteria一起使用,来完成各种条件的描述
使用MongoTemplate完成DML操作。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class MongoDBTest {
@Autowired
private MongoTemplate mongoTemplate;
// 保存操作
@Test
public void testTemplate_1() throws Exception {
User user = new User();
user.setId(15L);
user.setName("tmplate");
user.setAge(12);
// 保存数据到当前集合中
mongoTemplate.save(user);
// 保存数据到指定的temp集合中
mongoTemplate.save(user, "temp");
User user1 = new User();
user1.setId(17L);
user1.setName("tmplateBatch2");
user1.setAge(100);
List<User> list = new ArrayList<>();
User user2 = new User();
user2.setId(16L);
user2.setName("tmplateBatch1");
user2.setAge(120);
list.add(user1);
list.add(user2);
// 批量保存数据到当前集合
mongoTemplate.insertAll(list);
// 批量保存数据到指定集合
mongoTemplate.insert(list, "users");
}
// 删除操作
@Test
public void testTemplate_2() throws Exception {
User user = new User();
user.setName("tonny");
user.setAge(66);
user.set_id(new ObjectId("5d610458ab75afa90a18e82b"));
// 删除指定对象,必须要有ObjectId这个条件
mongoTemplate.remove(user);
Query query = new Query();
query.addCriteria(Criteria.where("_id").in(Arrays.asList("5d610458ab75afa90a18e82c", "5d610458ab75afa90a18e82d")));
// 根据条件删除
mongoTemplate.remove(query);
// 根据条件删除指定集合中的数据
mongoTemplate.remove(query, "users");
// 删除指定名称(temp)的集合
mongoTemplate.dropCollection("temp");
}
// 修改操作
@Test
public void testTemplate_3() throws Exception {
Query query = new Query();
//query.addCriteria(Criteria.where("_id").is("5d610458ab75afa90a18e82b"));
query.addCriteria(Criteria.where("name").is("tonny"));
Update update = Update.update("name", "tony").set("age", 46);
// 更新单个文档
// 查找name=tony的user信息,把查找的结果中第一个结果进行修改,把name改为tonny,age 改为6
mongoTemplate.updateFirst(query, update, "users");
// 更新多个文档
Query query1 = new Query();
query1.addCriteria(Criteria.where("name").is("tony"));
Update update1 = Update.update("age","11");
mongoTemplate.updateMulti(query1,update1,"users");
}
}
使用MongoTemplate完成DQL操作。
// 根据 Object ID 查询user 信息
@Test
public void testTemplate_4() throws Exception {
// 根据条件,从当前集合中获取
User user1 = mongoTemplate.findById("5d64b2c0c892a90c08f4f848", User.class);
// 根据条件,从指定集合中获取
User user2 = mongoTemplate.findById("5d64b2c0c892a90c08f4f848", User.class, "users");
System.out.println("user1 = " + user1);
System.out.println("user2 = " + user2);
}
// 查询所有user信息
@Test
public void testTemplate_5() throws Exception {
// 获取当前集合中所有数据
List<User> list1 = mongoTemplate.findAll(User.class);
// 获取指定集合中的所有数据
List<User> list2 = mongoTemplate.findAll(User.class, "users");
list1.forEach(System.out::println);
list2.forEach(System.out::println);
}
// 分页查询文档,显示第2页,每页显示3个,按照id升序排列
@Test
public void testTemplate_6() throws Exception {
// 方式一
Query query = new Query();
query.with(PageRequest.of(1, 3, Sort.by("id")));
List<User> userList = mongoTemplate.find(query, User.class);
userList.forEach(System.out::println);
// 方式二
Query query1 = new Query();
query1.limit(3).skip(3).with(Sort.by("id"));
List<User> userList1 = mongoTemplate.find(query1, User.class);
userList1.forEach(System.out::println);
}
//查询所有name为tony或者age<30的文档
@Test
public void testTemplate_7() throws Exception {
Query query = new Query();
Criteria criteria = new Criteria();
criteria.orOperator(Criteria.where("name").is("tony"),
Criteria.where("age").lt(30));
query.addCriteria(criteria);
List<User> list = mongoTemplate.find(query, User.class);
list.forEach(System.out::println);
}
// 查询所有name含有wang并且30<=age<=32的文档
@Test
public void testTemplate_8() throws Exception {
Criteria criteria = new Criteria();
criteria.andOperator(
Criteria.where("name").regex("wang"),
Criteria.where("age").lte(32).gte(30)
);
Query query = new Query();
query.addCriteria(criteria);
List<User> list = mongoTemplate.find(query, User.class);
list.forEach(System.out::println);
}
//需求:查询所有name=tony的文档信息
@Test
public void testTemplate_9() throws Exception {
Query query = new Query();
query.addCriteria(Criteria.where("name").is("tony"));
List<User> list = mongoTemplate.find(query, User.class);
list.forEach(System.out::println);
}
//需求:查询users集合中的所有数据,先按照年龄升序排序,然后再按照id降序排序
@Test
public void testTemplate_10() throws Exception {
Query query = new Query();
// 方式一
//query.with(Sort.by(Sort.Order.asc("age"), Sort.Order.desc("id")));
// 方式二
query.with(Sort.by("age")).with(Sort.by(Sort.Order.desc("id")));
List<User> userList = mongoTemplate.find(query, User.class);
userList.forEach(System.out::println);
}
// 需求:查询users集合中年龄大于26岁,小于29岁的文档信息
@Test
public void testTemplate_11() throws Exception {
Query query = new Query();
query.addCriteria(Criteria.where("age").gt(26).lt(29));
List<User> list = mongoTemplate.find(query, User.class);
list.forEach(System.out::println);
}
//需求:查询users集合中name不是tony的文档信息
@Test
public void testTemplate_12() throws Exception {
Query query = new Query();
query.addCriteria(Criteria.where("name").ne("tony"));
List<User> list = mongoTemplate.find(query,User.class);
list.forEach(System.out::println);
}
// 需求:查询users集合中id为 3,4,6的文档信息
@Test
public void testTemplate_13() throws Exception {
Query query = new Query();
query.addCriteria(Criteria.where("id").in(3,4,6));
List<User> list = mongoTemplate.find(query,User.class);
list.forEach(System.out::println);
}