MongoDB介绍/安装/使用/Spring操作
MongoDB
MongoDB介绍
简介
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
特点
- 高性能
- 易部署
- 易使用
- 非常方便的存储数据
对比MySQL
MongoDB | Mysql | |
---|---|---|
数据库模型 | 非关系型 | 关系型 |
存储方式 | 虚拟内存 + 持久化 | 不同存储引擎有不同的存储方式 |
查询语句 | 独特的MongoDB查询方式 | 传统SQL语句 |
架构特点 | 可以通过副本集,以及分片来实现高可用 | 常见也有单点,M-S,MHA,MMM,Cluster等架构 |
数据处理方式 | 基于内存,将热数据存在物理内存中,从而达到高速读写 | 不同的引擎拥有其自己的特点 |
成熟度 | 新兴数据库,成熟度低 | 拥有较为成熟的体系,成熟度较高 |
广泛度 | NoSQL数据库中,MongoDB是较为完善的DB之一,使用人群也在不断增长 | 开源数据库的份额在不断增加,MySQL的份额也在不断增长 |
目前环境下,只要对事务要求不高的业务都能被MongoDB所取代,属于及其热门的NoSQL数据库
数据库结构
MongoDB属于NoSQL数据库,自然也是没有表相关的概念的,该数据库存储使用的是集合,集合中存储的是文档(树状结构数据)
语法
show dbs --查询所有数据库
use 数据库名 --创建并且选中数据库,数据库已经存在则直接选中
db --查询当前选择的数据库
db.dropDatabase() --删除当前选中的数据库
show collections --查询当前库中的集合
db.createCollection("集合名") --创建集合
db.集合名.drop() --删除集合
注意:
db.集合名 == db.getCollection("集合名")
数据类型
String(字符串): mongodb中的字符串是UTF-8有效的
Integer(整数): 存储数值。整数可以是32位或64位,具体取决于您的服务器
Boolean(布尔): 存储布尔(true/false)值
Double(双精度): 存储浮点值
Arrays(数组): 将数组或列表或多个值存储到⼀个键中
Timestamp(时间戳): 存储时间戳
Object(对象): 嵌⼊式⽂档
Null (空值): 存储Null值
Symbol(符号): 与字符串相同,⽤于具有特定符号类型的语⾔
Date(⽇期): 以UNIX时间格式存储当前⽇期或时间
Object ID(对象ID) : 存储⽂档ID
Binary data(⼆进制数据): 存储⼆进制数据
Code(代码): 将JavaScript代码存储到⽂档中
Regular expression(正则表达式): 存储正则表达式
安装和连接
懒得重新安一遍,直接找个安装教程
MongoDB安装配置教程 - 小周sri的码农 - 博客园 (cnblogs.com)
创建操作
创建或插入操作会将新文档添加到集合中,如果该集合当前不存在,则插入操作将创建该集合
db.集合名.insert();
db.集合名.insertOne();
db.集合名.insertMany([]);
- db.集合名.insertOne(…)3.2版本中的新功能
- db.集合名.insertMany(…)3.2版本中的新功能
当操作成功时,集合会给文档生成一个_id
字段,该字段就是文档的主键,也能在插入数据时自己指定该字段的值,但是不建议这样做
例:
db.users.insert(
{
id:1,
name:"czh",
age:21
}
);
db.users.insertmany(
[
{id:3, name:"ly", age:23},
{id:4, name:"wgq", age:21}
]
);
删除操作
删除操作从集合中删除文档。MongDB提供一下删除集合文档的方法:
- db.collection.deleteOne(…)3.2版中的新功能
- db.collection.deleteMany(…)3.2版中的新功能
在MongoDB中,删除操作的目标是单个collection。MongoDB中的所有写操作都是单个文档级别的原子操作
db.集合名.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)
- query :(可选)删除的文档的条件
- justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值false,则删除所有匹配条件的文档
- writeConcern :(可选)抛出异常的级别
db.集合名.delete();
db.集合名.deleteOne();
db.集合名.deleteMany({});
db.集合名.remove()
注意:
deleteOne() – 删除第一条满足条件的数据
remove() – 删除所有满足条件的数据,和
deleteMany()
等价要使用自动生成的
_id
进行删除时,需要封装成ObjectId类db.users.deleteOne({_id:ObjectId("6224308aa569000058005895")});
例:
db.users.deleteMany({}); //传个空删除全部记录
db.users.deleteMant({age:23}); //删除年龄为23的用户
db.users.remove({age: 23}) //删除所有年龄为23的用户
更新操作
修改集合中已经存在的文档。MongDB提供了一下方法来更新集合的文档:
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(...);
db.集合名.updateOne(...);
db.集合名.updateMany(...);
注意:
把 $ set 改成 $ unset,在把字段设为空值,表示删除该字段,而不是修改该字段为空值
例:
db.users.update(
{age:21},
{$set{sex:"男"}}
);
db.users.update(
{
{age:21},
${unset{sex:""}} //删除sex字段
);
db.users.updateOne(
{name:"czh"},
{$set:{name:"lenovo"}}
);
读取操作
读取操作从 集合中检索文档; 即查询集合以获取文档。MongoDB提供了以下方法来从集合中读取文档:
正常查询
db.集合名.find({},{});
注意:
第一个
{ }
,中填条件,第二个{ }
填显示的列 –1(显示),0(不显示)
分页查询
db.集合名.find().skip().limit();
注意:
skip相当于MySQL中分页查询的**(currentPage-1)pageSize*
limit相当于MySQL中的每页查询几条数据
db.users.find().skip(0).limit(3); //第一页 db.users.find().skip(3).limit(3); //第二页 db.users.find().skip(6).limit(3); //第三页
例:
db.users.find(); //查询全部
db.users.find({name:"czh", age:21});
排序
db.集合名.find().sort();
注意:
sort中的参数1为升序,-1为降序
db.users.find().sort({age:1, id:-1})
高级查询
比较查询
比较操作符
>
大于 – $gt<
小于 – $lt>=
大于等于 – $gte<=
小于等于 – $lte!=
不等于 – $ne- 集合运算符 – $ in ,如:
{name: {$in: ["czh", "ly"]}}
- 判断存在 – $ exists,如:
{name: {$exists:true}}
大于/大于等于
db.集合名.find({字段名:{$gt:数字}}); //大于
db.集合名.find({字段名:{$gte:数字}}); //大于等于
例:
db.users.find({age:{$gt:20}}) //查询年龄大于20的用户
db.users.find({age:{$gte:20}}) //查询年龄大于等于20的用户
小于/小于等于
db.集合名.find({字段名:{$lt:数字}}); //小于
db.集合名.find({字段名:{$lte:数字}}); //小于等于
例:
db.users.find({age:{$lt:20}}) //查询年龄小于20的用户
db.users.find({age:{$lte:20}}) //查询年龄小于等于20的用户
不等于
db.集合名.find({字段名:{$ne:数字}}); //不等于
例:
db.users.find({age:{$ne:20}}) //查询年龄不等于20的用户
集合运算:in
和MySQL中的in效果一样
db.集合名.find({字段名:{$in:[元素1, 元素2 ...]}}); //查询在这个集合中的所有元素
例:
db.users.find({name:{$in:["czh", "ly"]}})
逻辑查询
&&
与 – $and||
或 – $or!
非 – $not
$and
普通sql语句:select * from users where age >= 20 and age <= 30
比较查询:db.users.find({age:{$gte:20, $lte:30}})
逻辑查询:db.users.find({$and:[{age:{$gte:20}}, {age:{$lte:30}}]})
$or
语法与and相同
db.users.find({$or:[{age:{$gte:20}}, {age:{$lte:30}}]})
db.users.find({$or:[{age:20}, {age:21}]})
模糊查询
对应SQL语句:
-
regex
—> like -
{name:/xx/}
–> %xx% -
{name:/^xx/}
—> xx% -
{name:/xx$/}
—> %xx -
{name:/xx/i}
—> 忽略大小写
例:
db.users.find({name:{$regex:/z/}})
db.users.find({name:{$regex:/^cz/}})
db.users.find({name:{$regex:/zh$/}})
db.users.find({name:{$regex:/w/i}});
练习:查询名字中带有”治“,并且年龄在20到30岁之间的用户
db.users.find({$and:[{name:{$regex:/治/}}, {age:{$gte:20, $lte:30}}]})
数组 – $push
添加元素
$push --> 可以重复添加
$addToSet --> 不重复
db.users.update({name:"qwe"}, {$push:{hobby:"java"}}) //添加一个元素
db.users.update({name:"qwe"}, {$push:{hobby:{$each:["vue", "c", "c++"]}}}) //循环添加多个元素
db.users.update({name:"qwe"}, {$addToSet:{hobby:"java"}}) //若存在,不添加
删除元素
$pop --> 类似弹栈
1 – 删除最后一个元素
-1 – 删除第一个元素
$pull --> 删除指定的元素
db.users.find({name:"qwe"}, {$pop:{hobby:1}}) //删除最后一个元素
db.users.find({name:"qwe"}, {$pop:{hobby:-1}}) //删除第一个元素
db.users.find({name:"qwe"}, {$pull:{hobby:"c"}}) //删除数组中”c“元素
修改元素
通过索引修改
//把用户名为qwe的hobby的第三个元素修改为java
db.users.update({name:"qwe"}, {$set:{
"hobby.2":"java"}})
通过元素修改
//把用户名为qwe的hobby中的vue改为linux
db.users.update({name:"qwe", hobby:"vue"}, {$set:{
"hobby.$":"linux"}})
这里的
$
表示前面的vue
Spring Data MongoDB
准备环境
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.1.3.RELEASE</version>
</parent>
<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>
</dependency>
<!-- lombok -->
<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>
配置连接参数
# application.properties
# 配置数据库连接
spring.data.mongodb.uri=mongodb://127.0.0.1:27017/数据库名
# 配置MongoTemplate的执行日志
logging.level.org.springframework.data.mongodb.core=DEBUG
domain
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document("users") //设置文档所在的集合
public class Users {
@Id
private ObjectId _id;
private Long id;
private String name;
private Integer age;
}
MongoRepository
该接口对MongoDB数据的常用操作进行了封装,我们只需要写个接口去继承该接口就能直接拥有CRUD等基本操作,再学习一下JPA的方法命名规范,可以毫不费力的完成各种高级操作
/**
* 自定义一个接口继承MongoRepository
* 泛型1:domain类型
* 泛型2:文档主键类型
* 贴上@Repository注解,底层回创建出动态代理对象,交给Spring管理
*/
@Repository
public interface UserMongoRepository extends MongoRepository<Users, ObjectId> {
//使用Spring Data命名规范做高级查询
Users findByName(String name);
}
Spring Data方法命名规范
关键字 | 例子 | JPQL |
---|---|---|
And | findByNameAndAge(String name, Integer age) | where name=? and age=? |
Or | findByNameOrAge(String name, String age) | where name=? or age=? |
Is | findByName(String name) | where name=? |
Between | findByAgeBetween(Integer min, Integer max) | where age between ? and ? |
LessThan | findByAgeLessThan(Integer age) | where age<? |
GreaterThan | findByAgeGreaterThan(Integer age) | where age>? |
LessThanEqual | findByAgeLessThanEqual(Integer age) | where age<=? |
GreaterThanEqual | findByAgeGreaterThanEqual(Integer age) | where age>=? |
After | 等同于GreaterThan | |
Before | 等同于LessThan | |
IsNull | findByNameIsNull() | where name is null |
IsNotNull | findByNameIsNotNull() | where name is not null |
Like | findByNameLike(String name) | where name like ? |
NotLike | findByNameNotLike(String name) | where name not like ? |
StartingWith | findByNameStartingWith(String name) | where name like ‘?%’ |
EndingWith | findByNameEndingWith(String name) | where name like ‘%?’ |
Containing | findByNameContaining(String name) | where name like ‘%?%’ |
OrderByXx[desc] | findByIdOrderByXx[Desc](Long id) | where id=? order by Xx [Desc] |
Not | findByNameNot(String name) | where name !=? |
In | findByIdIn(List ids) | where id in (…) |
NotIn | findByIdNotIn(List ids) | where id not in (…) |
True | findByXxTrue() | where Xx = true |
False | findByXxFalse() | where Xx = false |
IgnoreCase | findByNameIgnoreCase(String name) | where name=?(忽略大小写) |
Test
在测试类中注入UserMongoRepository
@Autowired
private UserMongoRepository repository;
-
插入/更新一个文档
@Test public void testSaveOrUpdate() { Users users = new Users(); users.set_id(null); users.setId(5L); users.setName("珊迪"); users.setAge(18); repository.save(users); }
日志:
Inserting Document containing fields: [id, name, age, _class] in collection: users
-
删除一个文档
@Test public void testDelete() { repository.deleteById(new ObjectId("63b05590920e000023000c27")); }
日志:
Remove using query: { "_id" : { "$oid" : "63b05590920e000023000c27" } } in collection: users
-
查询一个文档
@Test public void testGet() { Optional<Users> optional = repository.findById(new ObjectId("63b05590920e000023000c25")); optional.ifPresent(System.out::println); }
日志:
findOne using query: { "_id" : { "$oid" : "63b05590920e000023000c25" } } fields: { } in db.collection: czh20.users
-
根据姓名查询一个文档
@Test public void testByName() { Users u = repository.findByName("海绵宝宝"); System.out.println(u); }
日志:
find using query: { "name" : "海绵宝宝" } fields: Document{ { }} for class: class cn.czh20.mongodb.domain.Users in collection: users
-
查询所有文档
@Test public void testList() { List<Users> list = repository.findAll(); list.forEach(System.out::println); }
日志:
find using query: { } fields: Document{ { }} for class: class cn.czh20.mongodb.domain.Users in collection: users
MongoTemplate
该对象有Spring Boot完成了自动装配,存入Spring容器中,我们直接注入就可以使用了,依靠该对象能完成任何MongoDB的操作,一般和MongoRepository分工合作,多数用于复杂的高级查询以及底层操作
条件查询
Query对象用于封装查询条件,配合Criteria一起使用,来完成各种条件的描述
//一个Criteria对象可以理解为是一个限定条件
Criteria.where(String key).is(Object val); //设置一个等值条件
Criteria.orOperator(Criteria ...); //设置一组或的逻辑条件
//模糊查询
Criteria.where(String key).regex(String regex); //使用正则表达式匹配查询
注意:Criteria对象可以由其静态方法和构造器获取
Criteria封装了所有对条件的描述,常见的方法有:
lt / lte / gt / gte / ne / ...
最后通过Query对象的addCriteria把条件封装到Query对象中
Query对象.addCriteria(Criteria criteria); //添加查询条件
Query对象.skip(start).limit(pageSize); //分页查询
API方法
//根据条件查询集合中的文档
List<T> mongoTemplate.find(Query query, Class<T> type, String collectionName);
API使用
在测试类中注入MongoTemplate
@Autowired
private MongoTemplate template;
-
分页查询文档,显示第二页,每页显示2条数据,按照id升序排列
@Test public void testQuery1() { //创建查询对象 Query query = new Query(); //设置分页信息 query.skip(2).limit(2); //设置排序规则 query.with(new Sort(Sort.Direction.ASC, "id")); List<Users> users = template.find(query, Users.class, "users"); users.forEach(System.out::println); }
-
查询所有name为派大星或者age<20的文档
@Test public void testQuery2() { //构建限制条件 {"$or": [{"name": "派大星"}, {"age": {"$lt":20}}]} Criteria criteria = new Criteria().orOperator( Criteria.where("name").is("派大星"), Criteria.where("age").lt(20) ); //创建查询对象 Query query = new Query(); //添加限制条件 query.addCriteria(criteria); List<Users> list = template.find(query, Users.class, "users"); list.forEach(System.out::println); }
-
查询所有name含有"海"并且 1<=age<=20的文档
@Test public void testQuery3() { //构建查询条件 // db.users.find({"$and": [{"name": {"$regex": ".*海.*"}},{"age": {$gte": 1,"$lte": 20}}]}) Criteria criteria = new Criteria().andOperator( Criteria.where("name").regex(".*海.*"), Criteria.where("age").gte(1).lte(20) ); //创建查询对象 Query query = new Query(); //添加限制条件 query.addCriteria(criteria); List<Users> list = template.find(query, Users.class, "users"); list.forEach(System.out::println); }