# MongoDB学习笔记
### 一、MonogoDB简介
#### 1.NoSQL简介
NoSQL最常见的解释是“non-relational”, “Not Only SQL”也被很多人接受。NoSQL仅仅是一个[概念](https://baike.baidu.com/item/概念/829047),泛指非关系型的数据库,区别于[关系数据库](https://baike.baidu.com/item/关系数据库/1237340),它们不保证关系数据的ACID特性。NoSQL是一项全新的数据库革命性运动,其拥护者们提倡运用非关系型的[数据存储](https://baike.baidu.com/item/数据存储/9827490),相对于铺天盖地的[关系型数据库](https://baike.baidu.com/item/关系型数据库/8999831)运用,这一概念无疑是一种全新的思维的注入。
NoSQL有如下优点:易[扩展](https://baike.baidu.com/item/扩展/2732987),NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。无形之间也在架构的层面上带来了可扩展的能力。大数据量,高性能,NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
#### 2.为什么使用NoSQL?
> 关键原因是:传统关系型数据库遇到了性能瓶颈。回顾历史,可以发现一项新兴技术的诞生和流行,一定是为解决新的问题出现。那么为什么要使用NoSQL,也恰恰是因为传统的关系型数据库满足不了使用需求。
高并发读写、对海量数据的高效率存储和访问以及对数据库的高可扩展性和高可用性成了关系型数据库难以逾越的鸿沟,关系型数据库应对这三大问题显得力不从心,暴露了很多难以克服的问题,例如:
1、High performance - 对数据库高并发读写
web2.0网站要根据用户个性化信息来实时生成动态页面和提供动态信息,所以基本上无法使用动态页面静态化技术,因此数据库并发负载非常高,往往要达到每秒上万次读写请求。关系数据库应付上万次SQL查询还勉强顶得住,但是应付上万次SQL写数据请求,硬盘IO就已经无法承受了。其实对于普通的BBS网站,往往也存在对高并发写请求的需求,例如像JavaEye网站的实时统计在线用户状态,记录热门帖子的点击次数,投票计数等,因此这是一个相当普遍的需求。
2、Huge Storage - 对海量数据的高效率存储和访问
类似Facebook,twitter,Friendfeed这样的SNS网站,每天用户产生海量的用户动态,以Friendfeed为例,一个月就达到了2.5亿条用户动态,对于关系数据库来说,在一张2.5亿条记录的表里面进行SQL查询,效率是极其低下乃至不可忍受的。再例如大型web网站的用户登录系统,例如腾讯,盛大,动辄数以亿计的帐号,关系数据库也很难应付。
3、High Scalability && High Availability- 对数据库的高可扩展性和高可用性的
在基于web的架构当中,数据库是最难进行横向扩展的,当一个应用系统的用户量和访问量与日俱增的时候,你的数据库却没有办法像web server和app server那样简单的通过添加更多的硬件和服务节点来扩展性能和负载能力。对于很多需要提供24小时不间断服务的网站来说,对数据库系统进行升级和扩展是非常痛苦的事情,往往需要停机维护和数据迁移,为什么数据库不能通过不断的添加服务器节点来实现扩展呢?
#### 3.NoSQL优点/缺点
优点:
- \- 高可扩展性
- \- 分布式计算
- \- 低成本
- \- 架构的灵活性,半结构化数据
- \- 没有复杂的关系
缺点:
- \- 没有标准化
- \- 有限的查询功能(到目前为止)
- \- 最终一致是不直观的程序
#### 4.什么是MongoDB?
> MonngoDB数据库就是一种非关系型数据库,是一种键值(Key-Value)存储数据库。
**MongoDB**是一种[面向文档](https://zh.wikipedia.org/wiki/面向文檔的數據庫)的[数据库管理系统](https://zh.wikipedia.org/wiki/数据库管理系统),用[C++](https://zh.wikipedia.org/wiki/C%2B%2B)等语言撰写而成,以解决应用程序开发社区中的大量现实问题。MongoDB由[MongoDB Inc.](https://zh.wikipedia.org/w/index.php?title=MongoDB_Inc.&action=edit&redlink=1)(当时是[10gen](https://zh.wikipedia.org/w/index.php?title=10gen&action=edit&redlink=1)团队)于2007年10月开发,2009年2月首度推出,现以[服务器端公共许可](https://zh.wikipedia.org/w/index.php?title=服务器端公共许可&action=edit&redlink=1)(SSPL)分发。
#### 5.MongoDB存储结构
MongoDB 将数据存储为一个文档,数据结构由**键值(key=>value)对**组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
![image-20220307144518876](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220307144518876.png)
#### 6.特点
- 面向集合存储,易存储对象类型的数据。
- 模式自由。
- 支持动态[查询](https://baike.baidu.com/item/查询)。
- 支持完全索引,包含内部对象。
- 支持查询。
- 支持复制和故障恢复。
- 使用高效的二进制数据存储,包括大型对象(如视频等)。
- 自动处理碎片,以支持云计算层次的扩展性。
- 支持 [Golang](https://baike.baidu.com/item/Golang/2215139),[RUBY](https://baike.baidu.com/item/RUBY),[PYTHON](https://baike.baidu.com/item/PYTHON),[JAVA](https://baike.baidu.com/item/JAVA),[C++](https://baike.baidu.com/item/C%2B%2B),[PHP](https://baike.baidu.com/item/PHP),[C#](https://baike.baidu.com/item/C%23)等多种语言。
- 文件存储格式为BSON(一种JSON的扩展)。
- 可通过[网络](https://baike.baidu.com/item/网络)访问。
#### 7.MongoDB安装
这里主要是介绍在Linux下Docker镜像的MongoDB安装。(这里假定您已经拥有Linux服务器并且安装了Docker)
- 查看可用的MongoDB的版本
访问 MongoDB 镜像库地址: https://hub.docker.com/_/mongo?tab=tags&page=1。可以通过 Sort by 查看其他版本的 MongoDB,默认是最新版本 **mongo:latest**。
![image-20220307212031662](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220307212031662.png)
此外,我们还可以用`docker search mongo`命令来查看可用版本:
```bash
$ docker search mongo
```
![image-20220307212244356](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220307212244356.png)
- 拉取MongoDB镜像
这里拉取最新版本的镜像。
```bash
$ docker pull mongo:latest
```
![image-20220307212925310](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220307212925310.png)
- 查看本地的镜像
查看mongo镜像是否已经安装
```bash
$ docker images
```
![image-20220307213347223](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220307213347223.png)
- 运行mongo容器
```bash
$ docker run -itd --name mongo -p 27017:27017 mongo --auth
```
参数说明:
**-p 27017:27017** :映射容器服务的 27017 端口到宿主机的 27017 端口。外部可以直接通过 宿主机 ip:27017 访问到 mongo 的服务。
**--auth**:需要密码才能访问容器服务。
![image-20220307213924929](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220307213924929.png)
- 查看mongo容器运行情况
最后可以通过 **docker ps** 命令查看容器的运行信息:
```bash
$ docker ps
```
接着使用以下命令为MongoDB数据库添加用户和设置密码,并且尝试连接。
```bash
$ docker exec -it mongo mongo admin
# 创建一个名为 admin,密码为 123456 的用户。
> db.createUser({ user:'admin',pwd:'123456',roles:[ { role:'userAdminAnyDatabase', db: 'admin'},"readWriteAnyDatabase"]});
# 尝试使用上面创建的用户信息进行连接。
> db.auth('admin', '123456')
```
![ ](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220307214738008.png)
### 二、MongoDB概念解析
#### 1.MongoDB与MySQL之间区别
| SQL术语/概念 | **MongoDB术语/概念** | **解释/说明** |
| :----------: | :------------------: | ------------------------------------ |
| database | database | 数据库 |
| table | collection | 数据库表/集合 |
| row | document | 数据记录行/文档 |
| column | field | 数据字段/域 |
| index | index | 索引 |
| table joins | | 表连接,MongoDB不支持 |
| primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
通过实例图,了解MongoDB全部概念
![image-20220308115007779](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220308115007779.png)
以及MongoDB操作命令
1.数据库
```sql
# 创建数据库 (库不存在,则创建数据库,否则切换到指定数据库)
>use test
switched to db test
# 查看当前的数据库
>db
test
# 查看所有的数据库
>show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
# 删除当前数据库
>db.dropDatabaase()
{"droppped":"test","ok",1}
```
2.集合
```sql
# 创建集合 db.createCollection(name, options)
参数说明:
name: 要创建的集合名称
options: 可选参数, 指定有关内存大小及索引的选项
>db.createCollection("MyCol")
>db.createCollection("MyCol_1")
{"ok":2}
# 查看已有集合
>show collections
MyCol
MyCol_1
# 创建有关键参数的集合 例如创建固定集合 mycol,整个集合空间大小 6142800 B, 文档最大个数为 10000 个
>db.createCollection("mycol",{capped:true,autoIndexId:true,size:6142800,max:10000})
{"ok",1}
# 在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。
>db.mycol2.insert({"name":"张三"})
>show collections
mycol2
# 删除结合 例如删除集合mycol2
>db.mycol2.drop()
true
```
3.文档
```sql
# 向集合中插入文档 db.COLLECTION_NAME.insert(document)
# 如 向结合MyCol中插入文档
>db.MyCol.insert({name:'张三',age:'20',hobby:['吃','喝','玩']})
# 查看已插入的文档
>db.MyCol.find()
{name:'张三',age:'20',hobby:['吃','喝','玩']}
# 可以将数据定义为一个变量,如下所示:
> document=({name:'张三',age:'20',hobby:['吃','喝','玩']});
#执行插入操作
db.MyCol.insert(document)
# 方法 db.collection.insertOne() 和 db.collection.insertMany()分别插入一个新文档和多个新文档
>db.collection.insertOne(
<document>,
{
writeConcern: <document>
}
)
>db.collection.insertMany(
[ <document 1> , <document 2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)
# 参数说明
document:要写入的文档。
writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
ordered:指定是否按顺序写入,默认 true,按顺序写入。
# 更新文档 例如将张三改为张三百
>db.MyCol.update({'name':'张三'},{$set:{'name':'张三百'}})
# 删除文档 例如,移除name为'张三百'
>db.MyCol.remove({'name':'张三百'})
```
![image-20220308142445596](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220308142445596.png)
#### 2.MongoDB数据类型
下表为MongoDB中常用的几种数据类型。
| 数据类型 | 描述 |
| :----------------- | :----------------------------------------------------------- |
| String | 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。 |
| Integer | 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。 |
| Boolean | 布尔值。用于存储布尔值(真/假)。 |
| Double | 双精度浮点值。用于存储浮点值。 |
| Min/Max keys | 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。 |
| Array | 用于将数组或列表或多个值存储为一个键。 |
| Timestamp | 时间戳。记录文档修改或添加的具体时间。 |
| Object | 用于内嵌文档。 |
| Null | 用于创建空值。 |
| Symbol | 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。 |
| Date | 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。 |
| Object ID | 对象 ID。用于创建文档的 ID。 |
| Binary Data | 二进制数据。用于存储二进制数据。 |
| Code | 代码类型。用于在文档中存储 JavaScript 代码。 |
| Regular expression | 正则表达式类型。用于存储正则表达式。 |
#### 3.使用场景
对于MongoDB实际应用来讲,是否使用MongoDB需要根据项目的特定特点进行甄别,这就需要我们对MongoDB适用和不适用的场景有一定的了解。
MongoDB 的适用场景如下:
- 网站实时数据:mongoDB非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
- 数据缓存:由于性能很高,MongoDB 也适合作为信息基础设施的缓存层。在系统重启之后,由MongoDB搭建的持久化缓存层可以避免下层的数据源过载。
- 大尺寸、低价值数据存储:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储。
- 高伸缩性场景:MongoDB 非常适合由数十或数百台服务器组成的数据库。MongoDB 的路线图中已经包含对MapReduce 引擎的内置支持。
- 对象或JSON 数据存储:MongoDB 的BSON 数据格式非常适合文档化格式的存储及查询。
##### 不适合场景
- 高度事务性系统:例如银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序。
- 传统的商业智能应用:针对特定问题的BI 数据库会对产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择。
- 需要复杂SQL 查询的问题。
### 三、MongoDB入门
#### 1.常用操作 (CRUD)
Insert
```sql
db.MyCOl.save({name:'lisi',age:'22',hobby:['Skitting','Climbing']})
```
Query
```sql
db.MyCol.find({age:'22'})
```
Update
```sql
db.MyCol.update({'name':'lisi'},{$set:{'name':'李四'}})
```
Remove
```sql
db.MyCol.remove({'name':'张三'})
```
#### 2.aggregate(聚合)
MongoDB中聚合的方法使用aggregate()
MongoDB 中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。
有点类似 **SQL** 语句中的 **count(\*)**。
| 表达式 | 描述 |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| $match | 用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。 | |
| $project | 修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。 |
| $limit | 用来限制MongoDB聚合管道返回的文档数。 |
| $skip | 在聚合管道中跳过指定数量的文档,并返回余下的文档。 |
| $unwind | 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。 |
| $group | 将集合中的文档分组,可用于统计结果。 |
| $sort | 将输入文档排序后输出。 |
| $lookup | 联表查询 |
| $geoNear | 输出接近某一地理位置的有序文档。 |
| $facet/$bucket | 分类搜索(MongoDB 3.4以上支持) |
##### $match
```javascript
//查询条件 以某个分组查询 (可以多个查询条件累加到match里面)
db.getCollection('doc_views').aggregate([
{
$match: { "orgName" : "前端技术部" }
}
])
//查询结果如下
/* 1 */
{
"_id" : ObjectId("5fcb2c6cdfebd2682b2ae01b"),
"__v" : 0,
"time" : "2020-11-11",
"docName" : "testPublishAll",
"orgGroup" : "汽车事业群",
"orgName" : "前端技术部",
"count" : 3
}
...
```
##### $project
```javascript
//删减字段
db.getCollection('doc_views').aggregate([
{
$match: { "orgName" : "前端技术部" }
},
{
$project: {
docName: true,
orgName: true
}
}
])
//查询结果如下 只显示设置为true的字段
/* 1 */
{
"_id" : ObjectId("5fcb2c6cdfebd2682b2ae01b"),
"docName" : "testPublishAll",
"orgName" : "前端技术部"
}
...
//定义别名 把orgGroup指定指定成count字段
db.getCollection('doc_views').aggregate([
{
$match: { "orgName" : "前端技术部" }
},
{
$project: {
docName: true,
orgName: true,
orgGroup: "$count"
}
}
])
//查询结果orgGroup字段展示count的值
/* 1 */
{
"_id" : ObjectId("5fcb2c6cdfebd2682b2ae01b"),
"docName" : "testPublishAll",
"orgName" : "前端技术部",
"orgGroup" : 3
}
```
##### $group
```javascript
//聚合条件 _id :强制必须存在。
db.getCollection('doc_views').aggregate([
{
$group: {
_id: { name: "$docName", orgName: "$orgName" },
total: { $sum: "$count" }
}
}
])
//1、_id: null _id为null的查询结果
/* 1 */
{
"_id" : null,
"total" : 474
}
//2、_id: "$docName" _id为某字段
/* 1 */
{
"_id" : "gch-gitbook",
"total" : 8
}
...
//3、_id: { name: "$docName", orgName: "$orgName" } _id查询条件并为字段定义别名
/* 1 */
{
"_id" : {
"name" : "vuepress-hjw-test",
"orgName" : "前端技术部"
},
"total" : 2
}
```
##### $sort、$skip、$limit
```javascript
//列表分页的时候一般三个联合使用来做数据分页
//sort列表排序 skip指定跳过多少条 limit每次查询条数
//skip的计算方式:(当前页码-1)*每页大小 如:(pageIndex - 1) * pageSize
db.getCollection('doc_views').aggregate([
{$sort: {_id: -1}},
{$skip:Number(100)},
{$limit:Number(10)}
])
//查询结果如下
/* 1 */
{
"_id" : ObjectId("604ecc57b3b65a411b5600de"),
"__v" : 0,
"time" : "2021-02-25",
"docName" : "abgFeedback",
"docType" : "vuepress",
"orgGroup" : "汽车事业群",
"orgName" : "前端技术部",
"count" : 1
}
/* 2 */
{
"_id" : ObjectId("604ecc57b3b65a411b5600dd"),
"__v" : 0,
"time" : "2021-02-24",
"docName" : "testtestdocs",
"docType" : "docsify",
"orgGroup" : "汽车事业群",
"orgName" : "前端技术部",
"count" : 1
}
```
##### $lookup
```javascript
//联表查询
db.getCollection('doc_views').aggregate([
{$sort: {_id: -1}},
{$skip:Number(100)},
{$limit:Number(10)},
{$lookup:{
from: "docs",
localField: "docName",
foreignField: "name",
as: "child"
}}
])
//查询结果
/* 1 */
{
"_id" : ObjectId("604ecc57b3b65a411b5600de"),
"__v" : 0,
"time" : "2021-02-25",
"docName" : "abgFeedback",
"docType" : "vuepress",
"orgGroup" : "汽车事业群",
"orgName" : "前端技术部",
"count" : 1,
"child" : [
{
"_id" : ObjectId("5f6da851a18a783210da7a16"),
"docType" : "vuepress",
"name" : "abgFeedback",
"description" : "二手车问题反馈收集问题",
"owner" : "gongchenghui",
"orgGroup" : "汽车事业群",
"orgName" : "前端技术部",
"newGit" : false,
"toTop" : ISODate("2020-11-20T02:41:33.742Z"),
"realName" : "龚成辉",
"dest" : "abgFeedback",
"opendFiles" : [],
"createTime" : ISODate("2020-09-25T08:20:33.373Z")
}
]
}
```
##### $facet 分类查询
```javascript
//根据条件 分类一次查询出想要的数据和数据的总数
db.getCollection('doc_views').aggregate([
{$match: {"orgName" : "前端技术部"}},
{
$facet: {
respData: [
{$sort: {_id: -1}},
{$skip:Number(100)},
{$limit:Number(1)},
],
total: [
{
$count: "total"
}
]
}
}
//查询结果如下
/* 1 */
{
"respData" : [
{
"_id" : ObjectId("604ecc57b3b65a411b5600de"),
"__v" : 0,
"time" : "2021-02-25",
"docName" : "abgFeedback",
```
#### 3.索引
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
------
##### createIndex() 方法
MongoDB使用 createIndex() 方法来创建索引。
> 注意在 3.0.0 版本前创建索引方法为 db.collection.ensureIndex(),之后的版本使用了 db.collection.createIndex() 方法,ensureIndex() 还能用,但只是 createIndex() 的别名。
##### 语法
createIndex()方法基本语法格式如下所示:
```sql
>db.collection.createIndex(keys, options)
```
语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。
##### 实例
```sql
>db.col.createIndex({"title":1})
>
```
createIndex() 方法中你也可以设置使用多个字段创建索引(关系型数据库中称作复合索引)。
```sql
>db.col.createIndex({"title":1,"description":-1})
>
```
createIndex() 接收可选参数,可选参数列表如下:
| Parameter | Type | Description |
| :----------------- | :------------ | :----------------------------------------------------------- |
| background | Boolean | 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为**false**。 |
| unique | Boolean | 建立的索引是否唯一。指定为true创建唯一索引。默认值为**false**. |
| name | string | 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。 |
| dropDups | Boolean | **3.0+版本已废弃。**在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 **false**. |
| sparse | Boolean | 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 **false**. |
| expireAfterSeconds | integer | 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。 |
| v | index version | 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。 |
| weights | document | 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。 |
| default_language | string | 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语 |
| language_override | string | 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language. |
##### 实例
在后台创建索引:
```sql
db.values.createIndex({open: 1, close: 1}, {background: true})
```
通过在创建索引时加 background:true 的选项,让创建工作在后台执行
### 四、SpringBoot集成 MongoDB
#### 1.集成简介
spring-data-mongodb,提供了MongoTemplate与 MongoRepository,两种方式访问mongodb,MongoRepository,操作简单,MongoTemplate,操作灵活,我们在项目中可以灵活适用这两种方式操作mongodb,MongoRepository的缺点是不够灵活,
MongoTemplate 正好可以弥补不足。
#### 2.搭建开发环境
- 初始化工程
使用Spring Initaalizr 快速初始化一个Spring Boot工程
Group :com.zhoujk
Artifact:mongodb
- 引入依赖
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
```
- 添加配置
```properties
spring.data.mongodb.uri=mongodb://localhost:27017/test
```
#### 3.基于MongoBDTeplate开发
- 添加实体
```java
@Data
@Document("User")
public class User {
@Id
private String id;
private String name;
private Integer age;
private String email;
private String createDate;
}
```
- 实现mongoTemplate
```java
//注入mongoTemplate
@Autowired
private MongoTemplate mongoTemplate;
```
- 添加测试类
```java
//添加操作
@Test
public void create() {
User user = new User();
user.setName("zhangsan");
user.setAge(20);
user.setEmail("1348571853@qq.com");
User user1 = mongoTemplate.insert(user);
System.out.println(user1);
}
//查询所有的数据
@Test
public void findAll() {
List<User> all = mongoTemplate.findAll(User.class);
System.out.println(all);
}
//id查询
@Test
public void findId() {
User user = mongoTemplate.findById("622747932ea3370da71930fd", User.class);
System.out.println(user);
}
//条件查询
@Test
public void findUserList() {
//查询 name=zhangsan and age =20
Criteria criteria = Criteria.where("name").is("zhangsan").and("age").is(20);
Query query = new Query(criteria);
List<User> users = (List<User>) mongoTemplate.find(query, User.class);
System.out.println(users);
}
//模糊查询
@Test
public void findLikeUserList() {
// 查询名字以"zhang"开头的
String name = "zhang";
String regex = String.format("%s%s%s", "^.*", name, ".*$");
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Criteria criteria = Criteria.where("name").regex(pattern);
Query query = new Query(criteria);
List<User> users = (List<User>) mongoTemplate.find(query, User.class);
System.out.println(users);
}
//分页查询
@Test
public void findPageUserList() {
int PageNo = 1;
int PageSize = 2;
String name = "zhang";
String regex = String.format("%s%s%s", "^.*", name, ".*$");
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Criteria criteria = Criteria.where("name").regex(pattern);
Query query = new Query(criteria);
//分页构建
//查询记录数
long count = mongoTemplate.count(query, User.class);
//分页
List<User> users = mongoTemplate.find(query.skip((PageNo - 1) * PageSize).limit(PageSize), User.class);
System.out.println(count);
System.out.println(users);
}
//修改
@Test
public void updateUser() {
//根据Id查询
User user = mongoTemplate.findById("622747932ea3370da71930fd", User.class);
//设置修改值
user.setName("zhangsanfeng");
user.setEmail("123456789@qq.com");
user.setAge(60);
//调用方法实现修改
Criteria criteria = Criteria.where("_id").is(user.getId());
Query query = new Query(criteria);
Update update = new Update();
update.set("name", user.getName());
update.set("age", user.getAge());
update.set("email", user.getEmail());
UpdateResult upsert = mongoTemplate.upsert(query, update, User.class);
long modifiedCount = upsert.getModifiedCount();
System.out.println(modifiedCount);
}
//删除操作
@Test
public void daleteUser() {
Criteria criteria = Criteria.where("_Id").is("622747932ea3370da71930fd");
Query query = new Query(criteria);
DeleteResult remove = mongoTemplate.remove(query, User.class);
long deleteCount = remove.getDeletedCount();
System.out.println(deleteCount);
}
```
#### 4.基于MongoDBRepository开发
- 实体类
```
同上
```
- 添加Repository类
```java
//注入userRepository
@Autowired
private UserRepository userRepository;
```
- 实现类
```java
//返回值为long类型,功能:统计表中的数据条数
@Service
public class UserServiceImpl {
@Autowired
private UserRepository userRepository;
public long getCOuntofUser(){
return userRepository.count();
}
}
//其中count方法的实现源码
public long count() {
return this.mongoOperations.getCollection(this.entityInformation.getCollectionName()).count();
}
```
```java
//返回值是long类型,功能:有条件的统计表中的数据条数
@Service
public class UserServiceImpl {
@Autowired
private UserRepository userRepository;
public long getCOuntofUser(User user){
Example<User> example = Example.of(user);
return userRepository.count();
}
}
//其中count方法的实现源码
public <S extends T> long count(Example<S> example) {
Assert.notNull(example, "Sample must not be null!");
Query q = new Query((new Criteria()).alike(example));
return this.mongoOperations.count(q, example.getProbeType(), this.entityInformation.getCollectionName());
}
```
```java
//返回值为void类型,功能:删除表中一条数据
@Service
public class UserServiceImpl {
@Autowired
private UserRepository userRepository;
public void deleteOneUser(User user){
userRepository.delete(user);
}
}
//其中delete方法的实现源码,底层还是通过id删除
public void delete(T entity) {
Assert.notNull(entity, "The given entity must not be null!");
this.delete(this.entityInformation.getId(entity));
}
```
```java
//返回值是void类型,功能:删除表中一条数据
@Service
public class UserServiceImpl {
@Autowired
private UserRepository userRepository;
public void deleteOneUser(String id){
userRepository.delete(user);
}
}
//其中delete方法的实现源码,底层还是通过id删除,与上一个同名方法作用相同
public void delete(ID id) {
Assert.notNull(id, "The given id must not be null!");
this.mongoOperations.remove(this.getIdQuery(id), this.entityInformation.getJavaType(), this.entityInformation.getCollectionName());
}
```
```java
//返回值是void类型,功能:批量删除
@Service
public class UserServiceImpl {
@Autowired
private UserRepository userRepository;
public void deleteUsers(List<User> users){
userRepository.delete(users);
}
}
//实现方法的源码,批量删除,底层还是通过id删除
public void delete(Iterable<? extends T> entities) {
Assert.notNull(entities, "The given Iterable of entities not be null!");
Iterator var2 = entities.iterator();
while(var2.hasNext()) {
T entity = var2.next();
this.delete(entity);
}
}
```
```java
//返回值是void类型,功能:情空表中所有的数据
@Service
public class UserServiceImpl {
@Autowired
private UserRepository userRepository;
public void deleteAll(){
userRepository.deleteAll();
}
}
//deleteAll方法的实现源码
public void deleteAll() {
this.mongoOperations.remove(new Query(), this.entityInformation.getCollectionName());
}
```
```java
//返回值是boolean 类型,功能:判断数据是否存在
@Service
public class UserServiceImpl {
@Autowired
private UserRepository userRepository;
public boolean isExist(String id){
return userRepository.exists(id);
}
}
//exists方法的实现源码
public boolean exists(ID id) {
Assert.notNull(id, "The given id must not be null!");
return this.mongoOperations.exists(this.getIdQuery(id), this.entityInformation.getJavaType(), this.entityInformation.getCollectionName());
}
```
```java
//返回值是boolean类型,功能:判断某特定数据是否存在
@Service
public class UserServiceImpl {
@Autowired
private UserRepository userRepository;
public boolean isExist(User user){
Example example = Example.of(user);
return userRepository.exists(example);
}
}
//exists方法的实现源码
public <S extends T> boolean exists(Example<S> example) {
Assert.notNull(example, "Sample must not be null!");
Query q = new Query((new Criteria()).alike(example));
return this.mongoOperations.exists(q, example.getProbeType(), this.entityInformation.getCollectionName());
}
```
```java
//返回值是List<User>类型,功能:获取表中所有的数据
@Service
public class UserServiceImpl {
@Autowired
private UserRepository userRepository;
public List<User> findAll(){
return userRepository.findAll();
}
}
//findAll方法的实现源码
public List<T> findAll() {
return this.findAll(new Query());
}
```
# 附录1
SpringDataMongoDB的特点
![image-20220426181130377](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220426181130377.png)
有一点,值得说明,尤其是Java Backend开发者,同SpringDataJpa的特点一样,SpringDataMongoDB支持自动生成自定义查询方法。
例如, 我们需要通过hoscdoe和depcode来查询Department,只需要一行代码就搞定。
```java
@Repository
public interface DepartmentRepository extends MongoRepository<Department, String>
{
//上传科室
Department getDepartmentByHoscodeAndDepcode(String hoscode, String depcode);
}
```
> 我们只需要遵循特定谓词规则,定义好查询方法。SpringDataMongo就会帮我们实现这一过程。
```java
public interface PersonRepository extends Repository<User, Long> {
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enables the distinct flag for the query
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
// Enabling ignoring case for an individual property
List<Person> findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}
```
![image-20220426190652330](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220426190652330.png)
![image-20220426190721713](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220426190721713.png)
总计,SpringDataMongoDB的功能十分强大,一些基本的特性与功能就满足基本开发使用需求。本文关于SpringDataMongoDB还有很多地方都没有介绍,不过我不打算在这里冗余介绍了。网上的相关的介绍也非常多,介绍也比我详细。不过我建议读者,前往参考官网文档进行学习。官网的文档十分全面。
[官网文档](https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#repositories.query-methods.details)
![image-20220426180700126](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220426180700126.png)
# 附录2
**MongoDB数据库GUI软件**
> 喜欢用哪个就用哪个
- MongoDBCompass
![image-20220426191908344](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220426191908344.png)
- Navicat
![image-20220426192018041](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220426192018041.png)
- DataGrip
![image-20220426192817118](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220426192817118.png)
## 参考
[spring-data-mongodb中文文档](https://www.springcloud.cc/spring-data-mongodb.html)
[ spring.io之spring-data-mongodb](https://spring.io/projects/spring-data-mongodb)
[Spring Data Book代码案例](https://github.com/spring-projects/spring-data-book)
[ MongoDB Manual使用手册](https://www.mongodb.com/docs/manual/)
MongoDB学习笔记