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 拉取镜像
docker pull mongo:7.0.0
1.2.2 创建和启动容器
需要在宿主机建立文件夹
rm -rf /opt/mongo
mkdir -p /opt/mongo/data/db
docker run -d --restart=always -p 27017:27017 --name mongo -v /opt/mongo/data/db:/data/db mongo:7.0.0
1.2.3 进入容器
docker exec -it tingshu_mongo mongo
1.2.4 基本命令
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 创建数据库
如果数据库不存在,则创建数据库,否则切换到指定数据库。
use userdb
1.4.2 查看当前数据库
db.getName()
1.4.3 显示当前数据库状态
db.stats()
1.4.4 删除当前数据库
db.dropDatabase()
1.5 集合操作
1.5.1 创建集合
db.createCollection("User")
1.5.2 删除集合
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方法创建集合,也可以不创建集合,直接插入数据,那么集合会被自动创建
db.User.insert({name:'zhangsan',age:21,sex:true})
1.6.2 query
查询当前User集合中所有的记录
db.User.find()
查询当前User集合中name是zhangsan的记录
db.User.find({name:"zhangsan"})
1.6.3 update
只更新匹配到的第一条记录 $set修改器不会将已有字段删除
db.User.update({age:21}, {$set:{name:100}})
更新匹配到的所有记录
db.User.update({age:21}, {$set:{age:99}}, {multi: true})
1.6.4 remove
移除一个文档
db.User.remove(id)
移除所有文档
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 version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.atguigu</groupId>
<artifactId>mongo_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3、添加配置文件
application.yml
spring:
data:
mongodb:
database: user
host: 192.168.200.6
port: 27017
4、提供启动类
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 添加实体
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类
package com.atguigu.repository;
import com.atguigu.model.UserInfo;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface UserRepository extends MongoRepository<UserInfo, String> {
}
2.2.2 创建测试类
test目录创建测试类:MongoRepositoryTest
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<UserInfo> optional = userRepository.findById("6646cdae9480223895ecdb6c");
//if(optional.isPresent()){
// System.out.println(optional.get());
//}
//3.查询所有
//List<UserInfo> 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
@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<MyUser> 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<MyUser> 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<UserInfo> list = mongoTemplate.find(query, UserInfo.class, "user");
System.out.println(list);
}
在 MySQL 中,WITH 是用于定义 CTE(Common Table Expression,公共表表达式) 的关键字。它从 MySQL 8.0 版本开始支持,是一种结构化、可重用的查询方式,可以让你将复杂的子查询提取出来,单独命名并重复使用,从而提升 SQL 的可读性和可维护性。
MySQL中的With
✅ 一、基本语法:WITH 和 CTE
WITH cte_name AS (
SELECT ...
)
SELECT * FROM cte_name;
你可以定义多个 CTE,用逗号分隔:
WITH
cte1 AS ( ... ),
cte2 AS ( ... )
SELECT * FROM cte1 JOIN cte2 ON ...;
✅ 二、使用场景
🎯 场景1:简化复杂嵌套查询
原始嵌套写法:
SELECT name, salary
FROM (
SELECT name, salary, department_id
FROM employees
WHERE salary > 5000
) AS subquery
WHERE department_id = 3;
改写为 CTE 写法:
WITH high_salary_employees AS (
SELECT name, salary, department_id
FROM employees
WHERE salary > 5000
)
SELECT name, salary
FROM high_salary_employees
WHERE department_id = 3;
✅ 更清晰,逻辑分离明确。
🎯 场景2:递归查询(Recursive CTE)
MySQL 8.0+ 支持递归 CTE,适用于树形结构查询,如组织架构、分类层级等。
示例:查询所有下属员工(递归)
WITH RECURSIVE employee_hierarchy AS (
-- 初始查询:查找某个经理的直接下属
SELECT employee_id, name, manager_id, 1 AS level
FROM employees
WHERE manager_id = 100 -- 假设经理ID是100
UNION ALL
-- 递归部分:继续查找下级员工
SELECT e.employee_id, e.name, e.manager_id, eh.level + 1
FROM employees e
INNER JOIN employee_hierarchy eh
ON e.manager_id = eh.employee_id
)
SELECT * FROM employee_hierarchy;
这个查询会不断递归查找某位经理的所有下属,直到没有更多数据为止。
✅ 三、CTE 的优点
| 优点 | 描述 |
|---|---|
| ✅ 可读性强 | 将复杂逻辑拆分成模块,易于理解与调试 |
| ✅ 可复用性 | 同一个 CTE 可以在主查询中多次引用 |
| ✅ 结构清晰 | 比嵌套子查询更易维护 |
| ✅ 支持递归 | 能处理树形、层级结构数据 |
⚠️ 注意事项
WITH是 MySQL 8.0 引入的新特性,在 MySQL 5.x 不支持。- CTE 只在当前查询中有效,不能跨语句或会话使用。
- 如果你希望永久保存中间结果,应使用临时表或视图。
- CTE 查询性能与普通子查询相近,但优化器可能会将其内联展开,因此不适合大量数据的缓存用途。
✅ 四、和临时表、视图的对比
| 对比项 | CTE | 临时表 | 视图 |
|---|---|---|---|
| 是否持久 | ❌ 否 | ✅ 是(会话级别) | ✅ 是(元数据) |
| 是否可重复使用 | ✅ 是(当前查询) | ✅ 是(当前会话) | ✅ 是(全局) |
| 是否支持递归 | ✅ 是 | ❌ 否 | ❌ 否 |
| 创建开销 | ✅ 小 | ✅ 较大 | ✅ 小 |
| 使用场景 | 简化复杂查询 | 需要高性能或多次使用的中间结果 | 抽象业务逻辑、封装查询 |
✅ 五、总结
| 功能 | MySQL 是否支持 |
|---|---|
WITH CTE | ✅ 8.0+ 支持 |
| 多个 CTE 定义 | ✅ 支持 |
| 递归 CTE | ✅ 支持 |
| CTE 嵌套 | ✅ 支持(通过多层 WITH) |
| CTE 上建索引 | ❌ 不支持 |
如果你有具体的业务场景或想实现的功能(比如树形菜单、父子结构、报表统计等),我可以帮你写出对应的 WITH 或 WITH RECURSIVE 查询示例 😊
5794

被折叠的 条评论
为什么被折叠?



