MongoDB

简介


MongoDB 是一个基于分布式文件存储的数据库,由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。是非关系型数据库中功能最丰富,最像关系数据库的。支持数据结构类似 json 的 bson 格式。可以存储比较复杂的数据类型。MongoDB 的查询语言非常强大,类似面向对象的查询语言,几乎可以实现类似数据库单表查询的绝大部分功能,支持对数据建立索引。

高性能(相比关系型数据库),易部署,易使用。

对事务要求不高的业务都能被 MongoDB 取代。分片实现高可用,内部采用轮询的方式进行数据存储。

下载安装


下载安装

数据库结构


数据库存储使用的是集合,集合中存储的是文档,即树状结构数据。

MongoDB 默认端口 27017

在线文档教程

语法


1
2
3
4
5
6
7
8
9
show dbs: 查询所有数据库 
use 数据库名: 创建并且选中数据库,数据库已经存在则直接选中
db: 查询当前选择的数据库
db.dropDatabase(): 删除当前选中的数据库
show collections: 查询当前库中的集合
db.createCollection("集合名"): 创建集合
db.集合名.drop(): 删除集合

注意: db.集合名 == db.getCollection("集合名")

数据类型


支持的数据类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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(正则表达式): 存储正则表达式

新增操作


往集合中新增文档,当集合不存在时,会自动先创建集合,再往集合中添加文档。

1
2
db.集合名.insert(文档)
db.集合名.find() 查询集合中所有文档

操作成功时,会给文档生成一个_id 字段,就是文档的主键。

  • 实例
1
2
3
// 插入一个员工对象
db.users.insert({id: NumberLong(1), name: "小妖", age: NumberInt(18)})
db.users.insert({id: 2, name: "bunny", age: 20})

注意,直接写数字默认是 Double 类型。

更新操作


语法

1
2
3
4
5
6
7
8
9
10
11
12
13
db.集合名.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)

简化方法:
更新1个:db.集合名.updateOne( ... )
更新所有:db.集合名.updateMany( ... )

参数:

  • query: update 查询条件,类似 sql update查询 where 后面的条件
  • update: update 的对象和一些更新的操作符(如$, $inc) 等,也可以理解为 sql update 查询,set 后面的
  • upsert: 可选,这个参数的意思是,如果不存在update 的记录,是否插入 objNew,true 为插入,默认是 false,不插入。
  • multi: 可选,mongodb 默认是 false,只更新找到的第一条记录,如果参数为 true,就把按调价查出来多条记录全部更新。
  • writeConcern: 可选,抛出异常的级别

实例

1
2
3
4
5
//把一个带有name=逍遥的文档,修改其age值为30 
db.employees.updateOne({name: "逍遥"}, {$set: {age: 30}})

//修改所有name=逍遥的文档,修改其name=bunny,age=20
db.employees.updateMany({name: "逍遥"}, {$set: {name: "bunny", age: 30}})

删除操作


语法

1
2
3
4
5
6
7
8
9
10
11
db.集合名.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)

简化方法:
删除1个:db.集合名.deleteOne( ... )
删除所有:db.集合名.deleteMany( ... )
  • query :(可选)删除的文档的条件。
  • justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除 所有匹配条件的文档。
  • writeConcern :(可选)抛出异常的级别

实例

1
2
3
4
5
6
7
8
// 删除 _id=xxx 的文档
db.users.deleteOne({_id: objectId("xxx")})

// 删除所有带有 name=bunny 的文档
db.users.deleteMany({name: "bunny"})

// 删除当前数据库中所有文档
db.users.deleteMany()

查询操作


语法

db.集合名.find(query, projection)

  • query: 可选,使用查询操作符指定查询条件
  • projection: 可选,使用投影操作符指定返回的键。查询时返回文档中所有键值,只需省略该参数即可(默认省略)

实例

1
2
3
4
5
6
7
8
9
10
11
// 查询所有文档
db.users.find()
// 排序查询
db.users.find().sort({字段: 1}) -> 按照字段升序排列
db.users.find().sort({字段: -1}) -> 按照字段降序排列
// 分页查询
sikp(num): 跳过 num 个文档,相当于 start
limit(num): 限制显示 num 个文档,相当于 pageSize

db.users.find().skip(num).limit(num)
需求:按年龄降序排列,查询第二页,每页显示3个

高级查询


等值查询

find({字段: 值})

比较查询

find({字段: {比较操作符: 值, ...}})

  • 大于(>) -> $gt
  • 小于(<) -> $lt
  • 大于等于(>=) -> $gte
  • 小于等于(<=) -> $lte
  • 不等于(!=) -> $ne
  • 集合运算 -> $in, {name: {$in:["xiaoyao", "bunny"]}}
  • 判断存在 -> $exists, {name: {$exists: true}}

逻辑查询:

  • && -> $and
  • || -> $or
  • ! -> $not

模糊查询

MongoDB 的模糊查询使用的是正则表达式语法 {name: {$regex:/^.*keyword.*$/}},实际上 MongoDB 也是不擅长执行模糊查询的,在实际开发中不使用。

设置用户


用户设置

在 MongoDB 的配置文件:安装目录/Server/bin/mongod.cfg 中,如果修改了以下配置

1
2
3
# 在29行位置,配置开启权限认证
security:
authorization: enabled

如果完成了上述配置,重启服务器后,登录时需要输入密码,否则不能执行任何命令。

Spring Data MongoDB


准备工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<packaging>jar</packaging>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>

<dependencies>
<!-- lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--spring boot data mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>

<!-- pom.xml中添加插件 -->
<build>
<plugins>
<!-- SpringBoot打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

配置连接参数

application.properties

1
2
3
4
5
6
7
# application.properties 
# 配置数据库连接
#格式: mongodb://账号:密码@ip:端口/数据库?认证数据库
spring.data.mongodb.uri=mongodb://root:admin@localhost/mongotest?authSource=admin

# 配置MongoTemplate的执行日志
logging.level.org.springframework.data.mongodb.core=debug

操作数据库方案

使用,在 MongoDB 中提供了2个方案操作数据库。

高度封装的方案: MongoRepository
可以自定义配置方案:MongoTemplate

MongoRepository


该接口对 MongoDB 数据的常用操作进行了封装,只需要写个接口继承该接口就能直接拥有 CRUD 的基本操作,学习 JPA 的方法命名规范,可以完成各种复杂的高级操作,底层会调用 MongoTemplate 方法。

1
2
3
4
5
6
7
8
9
10
11
/** 
* 自定义一个接口继承 MongoRepository,
* 泛型1:domain类型
* 泛型2:文档主键类型
* 贴上@Repository注解,底层会创建出动态代理对象,交给Spring管理
*/
@Repository
public interface UserMongoRepository extends MongoRepository<User, ObjectId> {
// 使用Spring Data命名规范做高级查询
List<User> findByName(String name);
}

创建接口,放在 respository 包下。使用 Spring-data 的技术,都推荐放在 respository 包下。

需要配置 domain 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@NoArgsConstructor
@Setter
@Getter
@ToString
@Document("users") //设置文档所在的集合
public class User extends BaseDomain {
@Id //文档的id使用ObjectId类型来封装,并且贴上@Id注解
private ObjectId _id;

private String name;
private Integer age;
@DateTimeFormat
private Date bornDate;
private String headImg;
}

增删改

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testSaveOrUpdate() {
System.out.println(repository);
User user = new User();
user.setName("lz");
user.setAge(19);
System.out.println(user);
// User(_id=null, name=lz, age=19, bornDate=null, headImg=null)
repository.save(user); // 插入数据库
System.out.println(user);
// User(_id=5d9c4cde8670510a18497809, name=lz, age=19, bornDate=null, headImg=null)
}

如果配置了 id,调用 save 方法就是修改操作。
日志如下

1
2
3
4
5
6
2019-10-08 16:46:22.062 DEBUG 2584 --- [           main] o.s.data.mongodb.core.MongoTemplate      : Inserting Document containing fields: [name, age, _class] in collection: users
2019-10-08 16:46:22.107 INFO 2584 --- [ main] org.mongodb.driver.connection : Opened connection [connectionId{localValue:2, serverValue:10}] to 127.0.0.1:27017


2019-10-08 16:54:58.307 DEBUG 2636 --- [ main] o.s.data.mongodb.core.MongoTemplate : Saving Document containing fields: [_id, name, age, _class]
2019-10-08 16:54:58.342 INFO 2636 --- [ main] org.mongodb.driver.connection : Opened connection [connectionId{localValue:2, serverValue:12}] to 127.0.0.1:27017

inserting 新增,saving 更新,removing 删除

查询:查多个

1
2
3
4
5
@Test
public void testQuery() {
List<User> list = repository.findAll();
list.forEach(System.out::println);
}
1
2
3
4
5
6
7
8
/**
* PageRequest.of(page, size, sort)
* 参数1:页数
* 参数2:页面容量
* 参数3:Sort.By(排序字段)
*/
Page<User> page = repository.findAll(PageRequest.of(0, 3));
page.getContent().forEach(System.out::println);
1
2
3
4
5
@Test
public void testget() {
Optional<User> byId = repository.findById(new ObjectId("5d9c4cde8670510a18497809"));
byId.ifPresent(System.out::println);
}

JPA 方法命名规范


只要符合 JPA 方法命名规范,不需要写方法实现。

关键字 例子 JPQL
And findByNameAndAge(String name, Integer age) where name = ? and age = ?
Or findByNameOrAge(String name, Integer 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 < ?
LessThanEqual findByIdLessThanEqual where id <= ?
GreaterThan findByIdGreaterThan where id > ?
GreaterThanEqual findByIdGreaterThanEqual where id > = ?
After findByIdAfter where id > ?
Before findByIdBefore where id < ?
IsNull findByNameIsNull where name is null
isNotNull,NotNull findByNameNotNull where name is not null
Like findByNameLike where name like ?
NotLike findByNameNotLike where name not like ?
StartingWith findByNameStartingWith where name like ‘?%’
EndingWith findByNameEndingWith where name like ‘%?’
Containing findByNameContaining where name like ‘%?%’
OrderBy findByIdOrderByXDesc where id=? order by x desc
Not findByNameNot where name <> ?
In findByIdIn(Collection<?> c) where id in (?)
NotIn findByIdNotIn(Collection<?> c) where id not in (?)
True findByAaaTue where aaa = true
False findByAaaFalse where aaa = false
IgnoreCase findByNameIgnoreCase where UPPER(name)=UPPER(?)

MongoTemplate


SpringBoot 完成了自动配置,直接注入即可。依靠该对象可以完成任何 MongoDB 操作,一般和 MongoRepository 分工合作,多数用于复杂的高级查询以及底层操作。

1
2
@Autowired
private MongoTemplate template;
1
2
// 根据条件查询集合中的文档
List<T> mongoTemplate.find(Query query, Class<T> type, String collectionName);

条件封装 Query

1
2
3
4
5
6
7
// 一个 Ctriteria 对象,可以理解为一个条件
Criteria.where(String key).is(Object val); // 设置一个等值条件
Criteria.orOperator(Criteria...); // 设置一组条件

// 模糊查询
Criteria.where(String key).regex(String regex); // 使用正则表达式查询
// Criteria 对象静态方法和构造器获取,封装了所有的条件描述,lt / lte / gt / gte / ne /...

最后通过 addCriteria 把条件封装到 Query 对象中

1
2
Query对象.addCriteria(Criteria criteria); // 添加查询条件
Query对象.skip(start).limit(pageSize); // 分页查询

实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@Autowired
private MongoTemplate mongoTemplate;

// 分页查询文档,显示第2页,每页显示3个,按照id升序排列 @Test
public void testQuery1() throws Exception {

// 创建查询对象
Query query = new Query();
// 设置分页信息
query.skip(3).limit(3);
// 设置排序规则
query.with(new Sort(Sort.Direction.ASC, "id"));

List<User> list = mongoTemplate.find(query, User.class, "users");
list.forEach(System.err::println);

}

// 查询所有name为bunny的文档
@Test
public void testQuery2() throws Exception {

// 构建限制条件 {"name": "bunny"}
Criteria criteria = Criteria.where("name").is("bunny");
// 创建查询对象
Query query = new Query(); // 添加限制条件
query.addCriteria(criteria);

List<User> list = mongoTemplate.find(query, User.class, "users");
list.forEach(System.err::println);

}

// 查询所有name为bunny或者age<30的文档
@Test
public void testQuery3() throws Exception {

// 构建限制条件 { "$or": [{"name": "bunny"}, {"age": {"$lt": 30}}] }
Criteria criteria = new Criteria().orOperator(

Criteria.where("name").is("bunny"),

Criteria.where("age").lt(30));

// 创建查询对象
Query query = new Query();
// 添加限制条件
query.addCriteria(criteria);

List<User> list = mongoTemplate.find(query, User.class, "users");
list.forEach(System.err::println);

}

// 查询所有name含有wang并且30<=age<=32的文档
@Test
public void testQuery4() throws Exception {
// 构建限制条件 { "$and" : [{"name": {"$regex": ".*wang.*"} }, {"age": {"$gte": 30, "$lte": 32 } }] }
Criteria criteria = new Criteria().andOperator(Criteria.where("name").regex(".*wang.*"), Criteria.where("age").gte(30).lte(32));
// 创建查询对象
Query query = new Query();
// 添加限制条件
query.addCriteria(criteria);

List<User> list = mongoTemplate.find(query, User.class, "users");
list.forEach(System.err::println);
}
文章作者: Ammar
文章链接: http://lizhaoloveit.cn/2019/09/25/MongoDB/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Ammar's Blog
打赏
  • 微信
  • 支付宝

评论