
在本次文档中,我将完全依靠通义千文来进行对答式学习。
第一个问题
MongoDB是什么?
MongoDB 是一种 非关系型数据库(NoSQL) ,它用 "文档" 的形式存储数据,类似 JSON 格式。你可以把它想象成一个"灵活的电子表格",但更强大。它的特点是:
- 灵活:不需要预先定义表结构(比如 MySQL 需要先设计好表格和列),可以直接存储不同结构的数据。例如,用户信息可以有的字段有"地址",有的没有,甚至可以嵌套对象(比如用户地址里包含省、市、街道)。
- 适合复杂数据:比如存储社交媒体的帖子(包含文字、图片、评论、点赞数等混合类型的数据)。
- 扩展性强:能轻松处理大数据量,通过"分片"把数据分散到多台服务器上,适合高并发场景。
其中,我命令通义帮我生成了一个实例:
json
{
"user_id": 1,
"name": "张三",
"age": 28,
"email": "[email protected]",
"addresses": [
{
"type": "home",
"street": "北京市朝阳区XX街道",
"city": "北京"
},
{
"type": "office",
"street": "上海市浦东新区YY大厦",
"city": "上海"
}
],
"orders": [
{
"product": "iPhone 15",
"price": 8999,
"purchase_time": "2023-10-01T14:30:00Z"
},
{
"product": "AirPods Pro",
"price": 1999,
"purchase_time": "2023-10-02T09:15:00Z"
}
],
"preferences": {
"newsletter": true,
"language": "zh-CN"
}
}
MongoDB与mysql和redis的区别是什么?


其中,通义向我举了三个比较形象的例子:
-
MySQL 像是"严格的Excel表格",每一行每一列都必须按规则填写,适合需要精确计算和事务的场景(比如银行系统)。
-
MongoDB 像是"灵活的Word文档",可以自由组合文字、图片、表格,适合存储复杂或变化的数据(比如电商的商品详情页,可能包含不同规格的描述)。
-
Redis 像是"快速记忆的临时笔记本",适合存取频繁但生命周期短的数据(比如用户登录状态、热门商品的访问量统计)。
他在后端开发的工程中充当着一个什么样的角色?
MongoDB 的文档模型就像一个"智能文件夹",允许你将复杂、多变的数据(如用户信息、订单、地址)直接打包存储,而无需预先定义严格的结构或通过复杂的关联查询。
个人理解
也就是说,mongoDB本质上也是一个数据库,它和mysql一样是来存储用户的数据的,但是它们本质上是不同的sql是关系型数据库对数据存储之间的格式要求比较严格,而mongoDB更像是一股脑将所有的数据都存储在一个word中,对格式要求没有那么严格。
如何安装?
在官方网站Download MongoDB Community Server | MongoDB下载smi文件

好吧,我还是根据这个博客安装好了


第二个问题
我让通义千问帮我写了一个学习的流程,如下:
-
环境准备:安装MongoDB,创建Spring项目,添加依赖。
-
配置连接:在application.properties中设置MongoDB的URI。
-
创建实体类:使用@Document、@Id等注解。
-
创建Repository接口:继承MongoRepository。
-
实现基本的CRUD操作:注入Repository,使用save、findById、findAll、delete等方法。
-
自定义查询方法:使用@Query注解或方法名约定。
-
测试:编写测试类或使用main方法测试功能。
首先在springboot配置MongoDB
配置项目依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
连接到MongoDB数据库
创建MongoDB数据库



检查mongodb于springboot是否连接成功
typescript
package com.xyu;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import static org.springframework.test.util.AssertionErrors.assertNotNull;
@SpringBootTest
class MongodbTestApplicationTests {
@Autowired
private MongoTemplate mongoTemplate;
@Test
void contextLoads() {
}
@Test
void testMongoDBConnection() {
// 测试连接是否成功:通过查询数据库名称验证
String dbName = mongoTemplate.getDb().getName();
assertNotNull(dbName, "数据库名称不应为null");
System.out.println("当前连接的数据库: " + dbName);
}
}

mongoDB的一些基础概念
1. 数据库 (Database)
-
类比:类似关系型数据库中的 "数据库"。
-
特点:一个 MongoDB 实例可包含多个独立数据库。
-
操作:
javascript
复制
rustuse mydb // 切换到 mydb 数据库(不存在则自动创建) db.dropDatabase() // 删除当前数据库
2. 集合 (Collection)
-
类比:类似关系型数据库中的 "表"。
-
特点:
- 无固定结构:同一集合中的文档可以有不同的字段。
- 动态创建:插入第一个文档时自动创建。
-
操作:
javascript
复制
scssdb.createCollection("users") // 显式创建集合 db.users.drop() // 删除集合
3. 文档 (Document)
-
类比:类似关系型数据库中的 "行"。
-
本质 :一个 BSON 格式(Binary JSON)的数据结构。
-
示例:
json
复制
css{ "_id": ObjectId("507f191e810c19729de860ea"), "name": "Alice", "age": 30, "address": { "city": "Beijing", "street": "Main Street" }, "hobbies": ["reading", "coding"] }
4. _id 字段
- 作用:每个文档必须有的唯一主键。
- 默认类型 :
ObjectId
(12字节的唯一标识符,包含时间戳、机器ID等)。 - 自定义:可以手动指定(如使用数字、字符串等)。
增删改查
mongoDB的集合进行增删改查
csharp
@Test
void contextLoads() {
System.out.println("创建一个集合text1");
mongoTemplate.getDb().createCollection("text1");
System.out.println("查询集合text1是否存在");
System.out.println(mongoTemplate.getDb().listCollectionNames().into(new ArrayList<>()).contains("text1"));
System.out.println("删除集合text1");
mongoTemplate.getDb().getCollection("text1").drop();
System.out.println("查询集合text1是否存在");
System.out.println(mongoTemplate.getDb().listCollectionNames().into(new ArrayList<>()).contains("text1"));
System.out.println("获取集合的统计信息");
System.out.println(mongoTemplate.getDb().getCollection("Spring_boot").countDocuments());
System.out.println("删除所有文档而保留集合");
}

mongoDB的文档进行增删改查

第一个问题:在mongoDB中对文档进行操作时是否需要指定集合?
- 默认情况下,
MongoTemplate
会根据类名来选择集合名称。 - 如果需要,你可以通过
@Document(collection = "customCollectionName")
来显式指定集合名称。
如果使用默认集合名称当一个集合有多个文档时怎么办?
- 如果你希望多个不同的类存储在同一个集合中,你可以通过
@Document
注解显式地指定相同的集合名称。所以我建议大家一直使用指定集合方式
增
- 定义实体类:将 Java 类与 MongoDB 集合映射。
- 创建 Repository 接口 :使用
MongoRepository
来简化数据操作。 - 实现增操作 :使用
save()
方法添加文档。 - 调用增操作:通过服务层和控制层将数据插入 MongoDB 集合。
scala
package com.xyu.po;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Field;
public abstract class Item {
@Id
private String id;
@Field("type")
private String type; // 可以根据类型区分具体是 User 还是 Product
// getter and setter
}
package com.xyu.po;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "Spring_boot")
public class Product extends Item {
private String name;
private double price;
// 构造函数,getter,setter等
public Product(String name, double price) {
super();
this.name = name;
this.price = price;
}
}
package com.xyu.po;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Document(collection = "Spring_boot")
public class User extends Item {
private String name;
private int age;
// 构造函数,getter,setter等
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
kotlin
package com.xyu.dao;
import com.xyu.po.User;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface UserRepository extends MongoRepository<User, String> {
// 可以根据需要定义更多的查询方法
}
java
package com.xyu.service;
import com.xyu.dao.UserRepository;
import com.xyu.po.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void addUser(String name, int age) {
// 创建一个新的 User 对象
User user = new User(name, age);
// 使用 save() 方法插入文档
userRepository.save(user);
}
}
ini
@Test
void text1(){
String name = "李华";
int age = 18;
userService.addUser(name, age);
}

查
1. 单个查询步骤
- 定义实体类:将 Java 类与 MongoDB 集合映射。
- 创建 Repository 接口 :使用
MongoRepository
来简化数据操作。 - 实现查询操作 :使用
save()
方法添加文档。 - 调用查询操作:通过服务层和控制层将数据插入 MongoDB 集合。
csharp
public List<User> getAllUsers() {
return userRepository.findAll(); // 返回集合中所有文档
}
@Test
void text2(){
System.out.println(userService.getAllUsers()); // 调用服务层的查询方法
}
}

2. 多个查询步骤
- 查询独立的集合 :首先分别查询
User
和Order
集合中的数据。 - 合并结果 :然后在服务层将结果合并成一个返回值,通常会使用
Map
或其他容器来封装结果。

可以看到数据库中存这两条信息,我们怎么查询不同文档的信息
关键代码:
java
package com.xyu.dao;
import com.xyu.po.Item;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface ItemRepository extends MongoRepository<Item, String> {
List<Item> findByType(String type); // 根据 type 字段查询
}
java
package com.xyu.dao;
import com.xyu.po.Item;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface ItemRepository extends MongoRepository<Item, String> {
List<Item> findByType(String type); // 根据 type 字段查询
}
typescript
@Service
public class ItemService {
@Autowired
private ItemRepository itemRepository;
// 根据类型查询所有 Item(例如查询所有 Product 或 User)
public List<Item> getItemsByType(String type) {
return itemRepository.findByType(type); // 按 type 查询
}
}
ini
@Test
void text2(){
List<Item> products = itemService.getItemsByType("Product");
System.out.println("Products: " + products);
List<Item> users = itemService.getItemsByType("User");
System.out.println("Users: " + users);
}
}
重点 :合并两个文档类型
ini
List<Item> allItems = new ArrayList<>();
allItems.addAll(products);
allItems.addAll(users);
System.out.println(allItems);

3. 聚合查询步骤
- 筛选阶段($match) - 像筛子一样先过滤掉不要的数据
- 例子:只查2023年的订单
- 分组阶段($group) - 把相似的数据归为一组
- 例子:按客户ID分组,计算每个客户的总消费
- 排序阶段($sort) - 把结果按规则排队
- 例子:按销售额从高到低排序
- 投影阶段($project) - 决定最终显示哪些字段
- 例子:只显示客户姓名和总金额,隐藏其他信息
springboot使用聚合查询的筛选阶段($match)选出Product类
python
// 通过聚合查询筛选 type 为 "Product" 的文档
public List<Product> getProductsByType(String type) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("type").is(type)) // 使用 $match 筛选 type 字段为 "Product"
);
// 执行聚合查询并返回结果
return mongoTemplate.aggregate(aggregation, "Spring_boot", Product.class).getMappedResults();}
springboot使用分组阶段($group)求和
csharp
public double getTotalSpent(String name) {
// 聚合查询:按产品名称分组,并计算每个产品的总消费金额
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("name").sum("price").as("totalSpent") // 按name分组,并计算每个产品的总消费
);
// 执行聚合查询并获取结果
List<Map> results = mongoTemplate.aggregate(aggregation, "Spring_boot", Map.class).getMappedResults();
System.out.println(results);
double totalSpent = 0;
// 遍历查询结果,找出对应名称的消费总金额
for (Map result : results) {
if (result.containsKey("_id") && result.get("_id").equals(name)) {
totalSpent = (double) result.get("totalSpent");
break; // 找到匹配的名称后退出循环
}
}
return totalSpent; // 返回对应名称的总消费金额
排序阶段($sort) - 把结果按规则排队
csharp
public List<Map> getTotalSpent1() {
// 聚合查询:按产品名称分组,并计算每个产品的总消费金额
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("name").sum("price").as("totalSpent"), // 按name分组,并计算每个产品的总消费
Aggregation.sort(Sort.by(Sort.Order.desc("totalSpent"))) // 按照总消费金额降序排序
);
// 执行聚合查询并获取结果
List<Map> results = mongoTemplate.aggregate(aggregation, "Spring_boot", Map.class).getMappedResults();
System.out.println(results);
return results;
}
投影阶段($project) - 决定最终显示哪些字段
csharp
public List<Map> getTotalSpent2() {
// 聚合查询:按产品名称分组,并计算每个产品的总消费金额
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("name").sum("price").as("totalSpent"), // 按name分组,并计算每个产品的总消费
Aggregation.project( "_id") // 只投影 _id 和 totalSpent 字段
);
// 执行聚合查询并获取结果
List<Map> results = mongoTemplate.aggregate(aggregation, "Spring_boot", Map.class).getMappedResults();
System.out.println(results);
return results;
}
改
csharp
public void updateUserAgeByName(String name, int newAge) {
// 创建查询条件,查找指定名字的用户
Query query = new Query();
query.addCriteria(Criteria.where("name").is(name));
// 创建更新操作,设置新的年龄
Update update = new Update();
update.set("age", newAge);
// 执行更新操作
mongoTemplate.updateFirst(query, update, User.class);
}
删除
csharp
public void deleteUserByName(String name) {
// 创建查询条件,查找 name 为 "李华" 的用户
Query query = new Query();
query.addCriteria(Criteria.where("name").is(name));
// 执行删除操作
mongoTemplate.remove(query, User.class);
}