示例代码
✅ 1. MongoDB 概述
🔷1.1 MongoDB 是什么?
- 文档数据库(Document DB)
- 数据以 BSON 格式存储(类似 JSON)
- 每条数据叫 document
- 多条 document 存储在 collection
- 多个 collection 组成 database
🔷1.2 与 MySQL 的对比
| MySQL | MongoDB |
|---|---|
| 关系型表结构 | 无固定 schema |
| 强类型 | 弱结构,字段可变 |
| JOIN 多 | 无 JOIN,需要聚合 |
| 行式存储 | 文档存储(嵌套结构) |
| 适合强一致 | 适合灵活可扩展场景 |
🔷1.3 文档示例
{
"_id": "123",
"name": "eric",
"tags": ["dev", "java"],
"address": {
"city": "shanghai",
"code": 200000
}
}
✅ 2. Docker 启动 MongoDB
🔷2.1 拉取镜像
docker pull mongo:7.0

🔷2.2 启动单节点 MongoDB(开发环境)
docker run -d \
--name mongodb \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=123456 \
mongo:7.0

🔷2.3 使用图形化界面(强烈推荐)
✅ 方案 1:直接下载安装(最推荐,官方方式)
官网下载:
https://www.mongodb.com/products/tools/compass



✅ 方案 2:当然,用navicat连接也是可以(个人推荐)

✅ 3. Spring Boot 使用 MongoDB

🔷3.1 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.59</version>
</dependency>

🔷3.2 配置 application.yml
spring:
data:
mongodb:
uri: mongodb://admin:123456@localhost:27017/test?authSource=admin

🔷3.3 创建实体
@Data
@Document("user")
public class User {
@Id
private String id;
private String name;
private int age;
}
🔥3.4 对mongo进行操作
🔹3.4.1 对比
MongoRepository = 简单 CRUD 自动化,像 JPA
MongoTemplate = 灵活强大、复杂查询必用,像 MyBatis
| 特性 | MongoRepository | MongoTemplate |
|---|---|---|
| 编程风格 | 声明式(接口) | 命令式(代码) |
| 上手难度 | 极低 | 较高 |
| CRUD 能力 | 简单 CRUD 自动生成 | CRUD 全手写 |
| 复杂查询 | 能力弱(QueryDSL 才稍强) | 能力最强(支持聚合、管道等) |
| 聚合操作(Aggregation) | 不支持 | ✅ 支持 |
| 多条件动态查询 | 不方便 | ✅ 最适合 |
| 分页排序 | 简单支持 | ✅ 全支持 |
| 事务控制 | 支持 | 支持 |
| 适合场景 | 普通 CRUD、快速开发 | 高级查询、复杂条件、聚合分析 |
🔹3.4.1 Repository 使用方式(最简单)
- 继承MongoRepository
public interface UserRepository extends MongoRepository<User, String> {
List<User> findByName(String name);
}
- 编写UserService
package com.zr.mongodemo.service;
import com.zr.mongodemo.entity.User;
import com.zr.mongodemo.repository.UserRepository;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author: zr
* @Date: 2025/11/03/13:59
* @Description:
*/
@Service
public class UserService {
private final UserRepository repo;
public UserService(UserRepository repo) {
this.repo = repo;
}
public User create(User user) {
return repo.save(user);
}
public User get(String id) {
return repo.findById(id).orElse(null);
}
public List<User> list() {
return repo.findAll();
}
public User update(User user) {
return repo.save(user); // save = insert or update
}
public void delete(String id) {
repo.deleteById(id);
}
}
- 编写测试类
package com.zr.mongodemo;
import com.alibaba.fastjson2.JSON;
import com.zr.mongodemo.entity.User;
import com.zr.mongodemo.repository.UserRepository;
import com.zr.mongodemo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
@Slf4j
class MongoDemoApplicationTests {
@Autowired
private UserService userService;
@Test
void create() {
User user = new User();
user.setId("2");
user.setName("zr2");
user.setAge(19);
userService.create(user);
}
@Test
void list() {
List<User> list = userService.list();
log.info("list: {}", JSON.toJSONString(list));
}
@Test
void get() {
User user = userService.get("1");
log.info("user: {}", JSON.toJSONString(user));
}
@Test
void update() {
User user = new User();
user.setId("1");
user.setAge(20);
userService.update(user);
}
@Test
void delete() {
userService.delete("1");
}
}
3.1 测试新增


可以看到新增了一条数据

3.2 测试列表查询

可以看到返回了多条数据

3.3 测试id获取

可以看到返回了对应id的数据

3.4 测试更新
此处发现一个问题,没有设置的字段会被清空


3.4.1 只更新不为空的字段
回滚数据

创建MongoUpdateUtil使用反射的方式,只对每个非空字段做赋值处理
package com.zr.mongodemo.util;
import org.springframework.data.mongodb.core.query.Update;
import java.lang.reflect.Field;
/**
* @Author: zr
* @Date: 2025/11/03/14:44
* @Description:
*/
public class MongoUpdateUtil {
public static Update convertToUpdate(Object obj) {
Update update = new Update();
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
try {
Object value = field.get(obj);
// 跳过 id,跳过 null
if (value != null && !"id".equals(field.getName())) {
update.set(field.getName(), value);
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
return update;
}
}
此处还是用到了mongoTemplate
public void update2(User user) {
Query query = Query.query(Criteria.where("_id").is(user.getId()));
Update update = MongoUpdateUtil.convertToUpdate(user);
mongoTemplate.updateFirst(query, update, User.class);
}
修改id为1的年龄为20

可以看到没有更新的字段没有被清空,需要更新的字段也更新了

3.5 删除

可以看到对应数据被删除了

🔹3.4.2 Template 使用方式(更灵活)
@Autowired
MongoTemplate mongoTemplate;
public User save(User user) {
return mongoTemplate.save(user);
}
工具类对mongo进行操作
package com.zr.mongodemo.util;
import org.springframework.data.domain.Sort;
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.lang.reflect.Field;
import java.util.List;
import java.util.Map;
/**
* @Author: zr
* @Date: 2025/11/03/15:06
* @Description:
*/
public class MongoTemplateUtil {
private final MongoTemplate mongoTemplate;
public MongoTemplateUtil(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
// ✅ 插入
public <T> T insert(T obj) {
return mongoTemplate.insert(obj);
}
// ✅ 根据 ID 查询
public <T> T findById(String id, Class<T> clazz) {
return mongoTemplate.findById(id, clazz);
}
// ✅ 查询所有
public <T> List<T> findAll(Class<T> clazz) {
return mongoTemplate.findAll(clazz);
}
// ✅ 单字段查询
public <T> List<T> findByField(String field, Object value, Class<T> clazz) {
Query query = Query.query(Criteria.where(field).is(value));
return mongoTemplate.find(query, clazz);
}
// ✅ 多条件精确查询
public <T> List<T> findByFields(Map<String, Object> map, Class<T> clazz) {
Query query = new Query();
map.forEach((k, v) -> query.addCriteria(Criteria.where(k).is(v)));
return mongoTemplate.find(query, clazz);
}
// ✅ 分页查询 + 排序
public <T> List<T> findPage(Query query, int page, int size, String sortField, boolean asc, Class<T> clazz) {
if (sortField != null) {
query.with(Sort.by(asc ? Sort.Direction.ASC : Sort.Direction.DESC, sortField));
}
query.skip((long) (page - 1) * size);
query.limit(size);
return mongoTemplate.find(query, clazz);
}
// ✅ 获取数量
public long count(Query query, Class<?> clazz) {
return mongoTemplate.count(query, clazz);
}
// ✅ 删除
public void deleteById(String id, Class<?> clazz) {
Query query = Query.query(Criteria.where("_id").is(id));
mongoTemplate.remove(query, clazz);
}
// ✅ 按条件批量删除
public void deleteByFields(Map<String, Object> map, Class<?> clazz) {
Query query = new Query();
map.forEach((k, v) -> query.addCriteria(new Criteria(k).is(v)));
mongoTemplate.remove(query, clazz);
}
// ✅ ✅ 动态局部更新(自动忽略 null 字段)
public <T> void updatePartialById(T obj) {
try {
Field idField = obj.getClass().getDeclaredField("id");
idField.setAccessible(true);
Object idValue = idField.get(obj);
if (idValue == null) {
throw new RuntimeException("id 不能为空");
}
Query query = Query.query(Criteria.where("_id").is(idValue));
Update update = buildDynamicUpdate(obj);
mongoTemplate.updateFirst(query, update, obj.getClass());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// ✅ 核心:构建动态 Update,只更新非 null 字段
private <T> Update buildDynamicUpdate(T obj) {
Update update = new Update();
Field[] fields = obj.getClass().getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
try {
Object value = f.get(obj);
String fieldName = f.getName();
if ("id".equals(fieldName) || value == null) {
continue;
}
update.set(fieldName, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return update;
}
// ✅ 条件更新(多条件 WHERE)
public <T> void updatePartialByQuery(Query query, T obj, Class<T> clazz) {
Update update = buildDynamicUpdate(obj);
mongoTemplate.updateMulti(query, update, clazz);
}
}
该工具类支持的能力
| 功能 | 方法 |
|---|---|
| 新增 | insert(obj) |
| 根据 ID 查询 | findById(id) |
| 全查询 | findAll() |
| 单字段查询 | findByField() |
| 多字段查询 | findByFields() |
| 分页 | findPage() |
| 获取数量 | count() |
| 删除 ID | deleteById() |
| 条件批量删除 | deleteByFields() |
| ✅ 动态局部更新(自动忽略 null) | updatePartialById() |
| ✅ 条件更新 | updatePartialByQuery() |
使用示例
package com.zr.mongodemo.service;
import com.zr.mongodemo.entity.User;
import com.zr.mongodemo.repository.UserRepository;
import com.zr.mongodemo.util.MongoTemplateUtil;
import com.zr.mongodemo.util.MongoUpdateUtil;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author: zr
* @Date: 2025/11/03/13:59
* @Description:
*/
@Service
public class UserServiceTemplate {
private final MongoTemplateUtil mongoUtils;
@Autowired
public UserServiceTemplate(MongoTemplate mongoTemplate) {
this.mongoUtils = new MongoTemplateUtil(mongoTemplate);
}
public void add(User user) {
mongoUtils.insert(user);
}
public User get(String id) {
return mongoUtils.findById(id, User.class);
}
public void update(User user) {
mongoUtils.updatePartialById(user); // ✅ 动态局部更新
}
public void delete(String id) {
mongoUtils.deleteById(id, User.class);
}
public List<User> list() {
return mongoUtils.findAll(User.class);
}
}
✅ 4. 高级用法(非常实用)
🔶4.1 建索引
@Document("user")
public class User {
@Indexed
private String name;
}
🔶4.2 聚合查询(类似 SQL group by)
Aggregation agg = Aggregation.newAggregation(
Aggregation.group("age").count().as("count")
);
List<Document> result = mongoTemplate.aggregate(agg, "user", Document.class).getMappedResults();
🔶4.3 分页查询
Query query = new Query().limit(10).skip(20);
List<User> list = mongoTemplate.find(query, User.class);
✅ 5. Docker Compose 部署 MongoDB 副本集(生产环境)
当你需要:
✔ 数据高可用
✔ 支持事务
✔ 副本集复制
使用下面配置:
🔶docker-compose.yml
version: "3"
services:
mongo1:
image: mongo:7.0
container_name: mongo1
ports:
- "27017:27017"
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=123456
- TZ=Asia/Shanghai
command: ["--replSet", "rs0"]
mongo2:
image: mongo:7.0
container_name: mongo2
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=123456
command: ["--replSet", "rs0"]
mongo3:
image: mongo:7.0
container_name: mongo3
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=123456
command: ["--replSet", "rs0"]
✅ 6. 常见错误与解决方案
❗ Invalid BSON field name
出现:
Invalid BSON field name
原因:
✅ MongoDB key 不能包含以下字符:
".""$"
比如错误例子:
{
"status.pass": "ok" // 错误
}
解决方式:
- 替换 “.” → “_”
- 使用 @Field 映射
- 复杂结构用 Document 包装

2447

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



