# 1 MongoDB
## 1.1 MongoDB 概念
### 1.1.1 什么是MongoDB
MongoDB 是在2007年由DoubleClick公司的几位核心成员开发出的一款**分布式文档(数据)**数据库,由C++语言编写。
目的是为了解决数据**大量增长**的时候系统的**可扩展性**和**敏捷性**。MongoDB要比传统的关系型数据库简单很多。
在MongoDB中数据主要的组织结构就是`数据库、集合和文档`,文档存储在集合当中,集合存储在数据库中。
MongoDB中每一条数据记录就是一个文档,`数据结构由键值(key=>value)对组成`。
文档类似于 JSON 对象,它的数据结构被叫做`BSON`(Binary JSON)。

下表将帮助您更容易理解MongoDB中的一些概念:
| RDBMS(MySQL) | MongoDB |
| -------------- | ------------------ |
| 数据库 | 数据库(database) |
| 表 | 集合(Collection) |
| 行 | 文档(Document) |
| 列 | 字段(Field) |
| 表联合 | 嵌入文档 |
| 主键 | _id |
### 1.1.2 MongoDB适用场景
MongoDB不需要去明确指定一张表的具体结构,对字段的管理非常灵活,有很强的可扩展性。
支持高并发、高可用、高可扩展性,自带数据压缩功能,支持海量数据的高效存储和访问(磁盘内存映射技术)。
支持基本的CRUD、数据聚合、文本搜索和地理空间查询功能。
**适用场景:**
- 网站数据:Mongo非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
- 高伸缩性的场景:Mongo非常适合由数十或数百台服务器组成的数据库。
- 大尺寸,低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储。
- 缓存:由于性能很高,Mongo也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo搭建的持久化缓存层可以避免下层的数据源过载。
**例如:**
弹幕、直播间互动信息、朋友圈信息、物流场景等。
- 海量数据
- 频繁增删改
- 数据价值不高(没有强事务要求)
- 业务相对独立
**不适用场合:**
- 高度事务性系统:例如银行系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序。MongoDB4.5后支持事务,局限性(限定某个集合内多文档操作支持事务,跨集合事务管理实效)
## 1.2 安装和启动(docker方式)
### 1.2.1 拉取镜像
```shell
docker pull mongo:7.0.0
```
### 1.2.2 创建和启动容器
需要在宿主机建立文件夹
> rm -rf /opt/mongo
>
> mkdir -p /opt/mongo/data/db
```shell
docker run -d --restart=always -p 27017:27017 --name mongo -v /opt/mongo/data/db:/data/db mongo:7.0.0
```
### 1.2.3 进入容器
```shell
docker exec -it tingshu_mongo mongo
```
### 1.2.4 基本命令
```shell
show dbs
db.version() #当前db版本
db.getMongo() #查看当前db的连接机器地址
db.help() #帮助
quit() #退出命令行
```
## 1.3 客户端远程远程连接
**资料:**`资料>mongodb客户端>mongodb-compass-1.39.3-win32-x64.exe`,安装
**客户端连接:**

## 1.4 数据库操作
### 1.4.1 创建数据库
如果数据库不存在,则创建数据库,否则切换到指定数据库。
```shell
use userdb
```
### 1.4.2 查看当前数据库
```
db.getName()
```
### 1.4.3 显示当前数据库状态
```
db.stats()
```
### 1.4.4 删除当前数据库
```
db.dropDatabase()
```
## 1.5 集合操作
### 1.5.1 创建集合
```shell
db.createCollection("User")
```
### 1.5.2 删除集合
```shell
db.User.drop()
```
## 1.6 文档操作
文档是一组键值(key-value)对
**需要注意的是:**
1、MongoDB区分类型和大小写。
2、MongoDB的文档不能有重复的键。
### 1.6.0 ObjectID
ObjectId 是一个12字节 BSON 类型数据,有以下格式:
- 前4个字节表示时间戳
- 接下来的3个字节是机器标识码
- 紧接的2个字节由进程id组成(PID)
- 最后3个字节是随机数。
MongoDB中存储的文档必须有一个"_id"键。这个键的值可以是任何类型的,默认是个ObjectId对象。
在一个集合里面,每个文档都有唯一的"_id"值,来确保集合里面每个文档都能被唯一标识。
### 1.6.1 insert
向User集合插入一条记录。可以预先使用createCollection方法创建集合,也可以不创建集合,直接插入数据,那么集合会被自动创建
```shell
db.User.insert({name:'zhangsan',age:21,sex:true})
```
### 1.6.2 query
查询当前User集合中所有的记录
```shell
db.User.find()
```
查询当前User集合中name是zhangsan的记录
```shell
db.User.find({name:"zhangsan"})
```

### 1.6.3 update
只更新匹配到的第一条记录 $set修改器不会将已有字段删除
```shell
db.User.update({age:21}, {$set:{name:100}})
```
更新匹配到的所有记录
```shell
db.User.update({age:21}, {$set:{age:99}}, {multi: true})
```
### 1.6.4 remove
移除一个文档
```shell
db.User.remove(id)
```
移除所有文档
```shell
db.User.remove({})
```
### 1.6.5 索引操作
海量数据实现条件高效率查询,全集合扫描性能相对较低,针对于查询字段创建索引(单列索引,复合索引)
查看执行计划
```
db.集合名称.find({条件}).explain()
```

对频繁查询字段建立索引
```
db.集合名称.createIndex({字段:1}) #1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1
db.User.createIndex({name:1,age:1})
```


**更多命令参考:**https://www.runoob.com/mongodb/mongodb-tutorial.html
# 2 SpringBoot集成MongoDB
spring-data-mongodb提供了`MongoTemplate`与`MongoRepository`两种方式访问mongodb,MongoRepository操作简单,MongoTemplate操作灵活,我们在项目中可以灵活使用这两种方式操作mongodb。
## 2.1 集成Spring-data-mongoDB
### 2.1.1 搭建项目
1、创建项目:mongo_demo
2、导入pom.xml:
```xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
3.0.5
com.atguigu
mongo_demo
0.0.1-SNAPSHOT
17
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-mongodb
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
```
3、添加配置文件
application.yml
```yaml
spring:
data:
mongodb:
database: user
host: 192.168.200.6
port: 27017
```
4、提供启动类
```java
package com.atguigu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author: atguigu
* @create: 2023-11-24 10:48
*/
@SpringBootApplication
public class MongoDemoApp {
public static void main(String[] args) {
SpringApplication.run(MongoDemoApp.class, args);
}
}
```
### 2.1.2 添加实体
```java
package com.atguigu.model;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* 实体类跟MOngoDB集合对应
* @author: atguigu
* @create: 2024-05-17 11:40
*/
@Data
@Document(collection = "user")
public class UserInfo {
@Id
private String id;
private String name;
private Integer age;
private String address;
}
```
## 2.2 MongoRepository
### 2.2.1 添加Repository类
```java
package com.atguigu.repository;
import com.atguigu.model.UserInfo;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface UserRepository extends MongoRepository {
}
```
### 2.2.2 创建测试类
test目录创建测试类:MongoRepositoryTest
```java
package com.atguigu;
import com.atguigu.model.MyUser;
import com.atguigu.repository.UserRepository;
import org.bson.types.ObjectId;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class MongoDemoAppTest {
@Autowired
private UserRepository userRepository;
@Test
public void test(){
//1.文档新增
//UserInfo userInfo = new UserInfo();
//userInfo.setName("rose");
//userInfo.setAge(22);
//userRepository.save(userInfo);
//2.根据ID查询文档 "6646d293f1f34f7031481285"将字符串自动转为对象ID,通过持久层新增成功
//Optional optional = userRepository.findById("6646cdae9480223895ecdb6c");
//if(optional.isPresent()){
// System.out.println(optional.get());
//}
//3.查询所有
//List all = userRepository.findAll();
//System.out.println(all);
//4.删除记录
//userRepository.deleteById("12");
//5.修改
UserInfo userInfo = new UserInfo();
userInfo.setId("6646d293f1f34f7031481285");
userInfo.setName("mary");
userInfo.setAge(22);
userRepository.save(userInfo);
}
}
```
## 2.3 MongoTemplate
test目录创建测试类:MongoTemplateTest
```java
@Autowired
private MongoTemplate mongoTemplate;
@Test
public void testTempalte() {
//1.新增文档
//MyUser myUser = new MyUser();
//myUser.setName("jack");
//myUser.setAge(16);
//myUser.setEmail("jack@qq.com");
//myUser.setCreateDate(new Date());
//mongoTemplate.save(myUser);
//2.修改文档
//2.1 构建修改条件
//Query query = new Query();
//query.addCriteria(Criteria.where("name").is("jack"));
////2.2 修改内容
//Update update = new Update();
//update.set("age", 60);
//mongoTemplate.updateFirst(query, update, MyUser.class);
//3.查询文档
//MyUser myUser = mongoTemplate.findById(new ObjectId("65810bce15060000eb001595"), MyUser.class);
//System.out.println(myUser);
//List list = mongoTemplate.findAll(MyUser.class);
//System.out.println(list);
//4.删除
//Query query = new Query(Criteria.where("name").is("jack"));
//mongoTemplate.remove(query, "user");
//5.构建查询对象
Query query = new Query();
//5.1 在查询对象中封装分页对象
query.with(PageRequest.of(0, 5));
//5.2 在查询对象中封装排序对象
query.with(Sort.by(Sort.Direction.DESC, "age"));
//5.3 在查询对象中条件
//query.addCriteria(Criteria.where("age").gt(15));
String keyword = "os";
query.addCriteria(Criteria.where("name").regex("^.*"+keyword+".*$"));
List userList = mongoTemplate.find(query, MyUser.class);
System.out.println(userList);
}
/**
* 分页;排序;条件查询
*/
@Test
public void testTemplateOther(){
//1.创建条件查询对象Query 查询年龄大于等于22的用户
Query query = new Query();
//query.addCriteria(Criteria.where("age").gte(15));
//模糊查询
String keyword = "ar";
query.addCriteria(Criteria.where("name").regex("^.*"+keyword+".*$"));
//2.设置分页 SpringData框架规范:页码0代表第一页
Pageable pageable = PageRequest.of(0, 2);
query.with(pageable);
//3.设置排序
Sort sort = Sort.by(Sort.Direction.DESC, "age");
query.with(sort);
List list = mongoTemplate.find(query, UserInfo.class, "user");
System.out.println(list);
}
```