Spring Boot 整合 MongoDB 学习笔记 (新手入门)
目录
- 引言
- 环境准备
- [步骤 1: 添加 Maven/Gradle 依赖](#步骤 1: 添加 Maven/Gradle 依赖)
- [步骤 2: 配置 MongoDB 连接](#步骤 2: 配置 MongoDB 连接)
- [步骤 3: 创建实体类 (Domain Model)](#步骤 3: 创建实体类 (Domain Model))
- [步骤 4: 创建 Repository 接口](#步骤 4: 创建 Repository 接口)
- [步骤 5: 实现基础 CRUD 操作](#步骤 5: 实现基础 CRUD 操作)
- [创建 (Create) / 更新 (Update)](#创建 (Create) / 更新 (Update))
- 读取 (Read)
- 删除 (Delete)
- [步骤 6: 进阶查询 - 使用
MongoTemplate
](#步骤 6: 进阶查询 - 使用 MongoTemplate)- [什么是
MongoTemplate
?](#什么是 MongoTemplate?) - [使用
Query
和Criteria
](#使用 Query 和 Criteria)- [什么是
Query
?](#什么是 Query?) - [什么是
Criteria
?](#什么是 Criteria?) - [常用
Criteria
方法](#常用 Criteria 方法) - 代码示例
- [什么是
- [使用
Aggregation
](#使用 Aggregation)- [什么是
Aggregation
?](#什么是 Aggregation?) - 常用聚合阶段 (Stages)
- 代码示例
- [什么是
- [什么是
- [MongoDB 操作与 SQL (MySQL) 对比](#MongoDB 操作与 SQL (MySQL) 对比)
- 重点内容总结
- 结语
1. 引言
本笔记旨在帮助初学者理解如何在 Spring Boot 项目中集成和使用 MongoDB 数据库。我们将从最基础的配置开始,逐步深入到常用的增删改查 (CRUD) 操作,以及更复杂的查询方式,如使用 Query
, Criteria
和 Aggregation
。Spring Data MongoDB 极大地简化了 Java 应用与 MongoDB 的交互。
2. 环境准备
- JDK: 确保已安装 Java 开发工具包 (推荐 JDK 8 或更高版本)。
- Maven 或 Gradle: Spring Boot 项目构建工具。
- IDE: 如 IntelliJ IDEA, Eclipse, VS Code 等。
- MongoDB: 需要一个正在运行的 MongoDB 实例 (可以是本地安装,也可以是 Docker 容器,或者是云服务如 MongoDB Atlas)。
3. 步骤 1: 添加 Maven/Gradle 依赖
要在 Spring Boot 项目中使用 MongoDB,首先需要添加 spring-boot-starter-data-mongodb
依赖。
Maven (pom.xml
):
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<!-- 通常不需要指定版本,Spring Boot会管理 -->
</dependency>
Gradle (build.gradle
):
groovy
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
// 其他依赖...
}
注释: 这个 Starter 会自动引入 Spring Data MongoDB 以及所需的 MongoDB Java 驱动程序。
4. 步骤 2: 配置 MongoDB 连接
在 src/main/resources
目录下的 application.properties
或 application.yml
文件中配置 MongoDB 的连接信息。
application.properties
格式:
properties
# MongoDB 连接 URI (推荐方式,包含了所有信息)
# 格式: mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[database][?options]]
spring.data.mongodb.uri=mongodb://localhost:27017/mydatabase
# 或者分开配置 (如果不用URI)
# spring.data.mongodb.host=localhost
# spring.data.mongodb.port=27017
# spring.data.mongodb.database=mydatabase
# 如果有认证
# spring.data.mongodb.username=your_username
# spring.data.mongodb.password=your_password
# spring.data.mongodb.authentication-database=admin # 通常是 admin 或你的数据库名
application.yml
格式:
yaml
spring:
data:
mongodb:
# URI 方式 (推荐)
uri: mongodb://localhost:27017/mydatabase
# 分开配置方式
# host: localhost
# port: 27017
# database: mydatabase
# username: your_username
# password: your_password
# authentication-database: admin
重点:
spring.data.mongodb.uri
是最常用且推荐的配置方式,简洁明了。mydatabase
是你要连接的数据库名称,如果不存在,MongoDB 通常会在第一次写入数据时自动创建。- 确保主机名 (
localhost
) 和端口 (27017
是默认端口) 正确。 - 如果 MongoDB 设置了用户认证,务必提供
username
和password
。
5. 步骤 3: 创建实体类 (Domain Model)
实体类是 Java 对象,用于映射 MongoDB 中的文档 (Document)。
java
package com.example.yourproject.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field; // 可选,用于指定字段名
import java.util.Date;
/**
* 用户实体类,映射到 MongoDB 中的 "users" 集合。
*/
@Document(collection = "users") // 指定映射的 MongoDB 集合名称,如果省略,默认为类名的小写形式 (user)
public class User {
@Id // 标记这个字段作为文档的主键 (_id)
private String id; // MongoDB 的 _id 通常是 ObjectId 类型,但映射为 String 更方便
@Field("user_name") // 可选:如果 Java 字段名与 MongoDB 字段名不同,用 @Field 指定
private String name;
private int age;
private String email;
@Field("created_at") // 示例:指定字段名
private Date createdAt;
// Standard Constructors, Getters, Setters, toString() ...
public User() {
this.createdAt = new Date(); // 可以在构造函数中设置默认值
}
public User(String name, int age, String email) {
this(); // 调用默认构造函数设置 createdAt
this.name = name;
this.age = age;
this.email = email;
}
// --- Getters and Setters ---
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Date getCreatedAt() { return createdAt; }
public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; }
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
", createdAt=" + createdAt +
'}';
}
}
重点:
@Document
: 标记这是一个映射到 MongoDB 文档的类。collection
属性指定了集合名称。@Id
: 标记主键字段,对应 MongoDB 中的_id
。Spring Data MongoDB 会自动处理String
类型和 MongoDBObjectId
之间的转换。如果字段名为id
,@Id
注解甚至可以省略(但不推荐省略以保证清晰)。@Field
: 当 Java 字段名和 MongoDB 文档中的字段名不一致时使用。如果一致,则可以省略。
6. 步骤 4: 创建 Repository 接口
Spring Data MongoDB 提供了一个 MongoRepository
接口,它内置了许多标准的 CRUD 操作方法。我们只需要定义一个接口继承它即可。
java
package com.example.yourproject.repository;
import com.example.yourproject.model.User;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* User 数据访问层接口
* 继承 MongoRepository<实体类类型, 主键类型>
*/
@Repository // 标记这是一个 Spring管理的 Repository Bean
public interface UserRepository extends MongoRepository<User, String> {
// Spring Data MongoDB 会根据方法名自动生成查询实现!
// 例如:根据 name 查找用户
List<User> findByName(String name);
// 例如:根据 email 查找单个用户 (假设 email 是唯一的)
User findByEmail(String email);
// 例如:查找年龄大于某个值的用户
List<User> findByAgeGreaterThan(int age);
// 更多查询方法可以参照 Spring Data JPA 的命名规范
// https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#repositories.query-methods.query-creation
}
重点:
- 继承
MongoRepository<User, String>
,其中User
是实体类,String
是主键id
的类型。 @Repository
注解使得 Spring Boot 能自动扫描并创建这个 Bean。- 最强大的功能之一 :通过定义符合特定命名规范的方法(如
findByName
,findByAgeGreaterThan
),Spring Data MongoDB 会自动为你实现这些查询,无需编写任何具体代码!这被称为方法名衍生查询 (Query Methods)。
7. 步骤 5: 实现基础 CRUD 操作
现在可以在你的 Service 或 Controller 中注入 UserRepository
,并调用其方法进行 CRUD 操作。
java
package com.example.yourproject.service;
import com.example.yourproject.model.User;
import com.example.yourproject.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class UserService {
@Autowired // 自动注入 UserRepository 的实例
private UserRepository userRepository;
/**
* 创建或更新用户
* 如果 user 对象有 id 且数据库中存在该 id,则执行更新操作。
* 如果 user 对象没有 id 或 id 在数据库中不存在,则执行插入操作。
*/
public User saveOrUpdateUser(User user) {
// save() 方法兼具插入和更新功能 (upsert)
User savedUser = userRepository.save(user);
System.out.println("Saved/Updated User: " + savedUser);
return savedUser;
}
/**
* 根据 ID 查找用户
*/
public Optional<User> findUserById(String id) {
// findById 返回一个 Optional<User>,可以更好地处理空结果
Optional<User> userOptional = userRepository.findById(id);
if (userOptional.isPresent()) {
System.out.println("Found User by ID " + id + ": " + userOptional.get());
} else {
System.out.println("User with ID " + id + " not found.");
}
return userOptional;
}
/**
* 查找所有用户
*/
public List<User> findAllUsers() {
List<User> users = userRepository.findAll();
System.out.println("Found all users: Count = " + users.size());
return users;
}
/**
* 使用方法名衍生查询:根据姓名查找用户
*/
public List<User> findUsersByName(String name) {
List<User> users = userRepository.findByName(name);
System.out.println("Found users by name '" + name + "': " + users);
return users;
}
/**
* 根据 ID 删除用户
*/
public void deleteUserById(String id) {
// 先检查是否存在 (可选,但更安全)
if (userRepository.existsById(id)) {
userRepository.deleteById(id);
System.out.println("Deleted User with ID: " + id);
} else {
System.out.println("User with ID " + id + " not found, cannot delete.");
}
}
/**
* 删除指定用户对象
*/
public void deleteUser(User user) {
// 需要 user 对象有有效的 id
userRepository.delete(user);
System.out.println("Deleted User: " + user);
}
/**
* 删除所有用户 (谨慎使用!)
*/
public void deleteAllUsers() {
userRepository.deleteAll();
System.out.println("Deleted all users.");
}
}
重点:
@Autowired
用于依赖注入UserRepository
。save(entity)
: 核心方法,既能插入新文档(如果实体没有 ID 或 ID 不存在),也能更新现有文档(如果实体有 ID 且数据库中存在)。findById(id)
: 返回Optional<T>
,推荐使用isPresent()
判断和get()
获取。findAll()
: 返回所有文档的列表。deleteById(id)
/delete(entity)
/deleteAll()
: 执行删除操作。existsById(id)
: 检查文档是否存在。MongoRepository
提供的默认方法和自定义的方法名衍生查询,覆盖了大部分常见的简单查询场景。
8. 步骤 6: 进阶查询 - 使用 MongoTemplate
当 MongoRepository
提供的方法名衍生查询或 @Query
注解(此处未详细介绍,但也可用于定义 JPQL 或原生查询)无法满足复杂的查询需求时,可以使用 MongoTemplate
。MongoTemplate
提供了更底层、更灵活的 MongoDB 操作方式,包括复杂的查询、更新和聚合操作。
什么是 MongoTemplate
?
MongoTemplate
是 Spring Data MongoDB 提供的核心类,它封装了 MongoDB Java 驱动的操作,提供了方便的 API 来执行各种数据库操作。你需要在使用它的类中注入它:
java
import org.springframework.data.mongodb.core.MongoTemplate;
@Service
public class AdvancedUserService {
@Autowired
private MongoTemplate mongoTemplate; // 注入 MongoTemplate
// ... 方法将在这里实现 ...
}
使用 Query
和 Criteria
对于复杂的查询条件,我们通常使用 Query
和 Criteria
对象来构建。
什么是 Query
?
Query
对象封装了 MongoDB 查询的所有方面,包括查询条件 (Criteria
)、字段投影 (选择返回哪些字段)、排序 (Sort
) 和分页 (limit
, skip
)。
什么是 Criteria
?
Criteria
对象用于构建查询条件,类似于 SQL 中的 WHERE
子句。它提供了一系列方法 (如 is
, gt
, lt
, regex
, in
, and
, or
等) 来定义文档需要匹配的规则。
常用 Criteria
方法
Criteria 方法 | 含义 | MongoDB 操作符 | SQL (MySQL) 类似 |
---|---|---|---|
where("field").is(value) |
字段等于特定值 | $eq |
field = value |
where("field").ne(value) |
字段不等于特定值 | $ne |
field != value |
where("field").lt(value) |
字段小于特定值 | $lt |
field < value |
where("field").lte(value) |
字段小于等于特定值 | $lte |
field <= value |
where("field").gt(value) |
字段大于特定值 | $gt |
field > value |
where("field").gte(value) |
字段大于等于特定值 | $gte |
field >= value |
where("field").in(list) |
字段值在列表/数组中 | $in |
field IN (...) |
where("field").nin(list) |
字段值不在列表/数组中 | $nin |
field NOT IN (...) |
where("field").exists(true/false) |
字段存在/不存在 | $exists |
IS NOT NULL / IS NULL (概念类似) |
where("field").regex(pattern) |
字段匹配正则表达式 | $regex |
field REGEXP 'pattern' |
where("field").regex(pattern, "i") |
正则表达式 (忽略大小写) | $options: 'i' |
(取决于具体实现) |
and(criteria) |
链式添加 AND 条件 | (隐式AND) | AND |
new Criteria().orOperator(c1, c2, ...) |
创建 OR 条件组合 | $or |
OR |
new Criteria().andOperator(c1, c2, ...) |
创建 AND 条件组合 | $and |
AND |
代码示例 (Query
& Criteria
)
java
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.domain.Sort; // 用于排序
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.yourproject.model.User;
import java.util.List;
@Service
public class AdvancedUserService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 查找年龄大于指定值,并且按姓名升序排序的用户
* @param minAge 最小年龄
* @return 用户列表
*/
public List<User> findUsersOlderThanAndSortByName(int minAge) {
// 1. 创建查询条件 (Criteria)
Criteria ageCriteria = Criteria.where("age").gt(minAge); // 年龄大于 minAge
// 2. 创建 Query 对象,并应用 Criteria
Query query = new Query(ageCriteria);
// 3. 添加排序 (Sort)
query.with(Sort.by(Sort.Direction.ASC, "user_name")); // 按 user_name 升序排序
// 4. (可选) 添加分页
// query.limit(10); // 限制返回最多 10 条
// query.skip(20); // 跳过前 20 条 (用于分页)
// 5. (可选) 添加字段投影 (只返回需要的字段)
// query.fields().include("user_name", "email").exclude("_id"); // 只包含 name 和 email, 排除 _id
// 6. 执行查询
// find() 方法需要 Query 对象和结果映射的实体类类型
List<User> users = mongoTemplate.find(query, User.class);
// 如果查询的是特定集合,也可以指定集合名称: mongoTemplate.find(query, User.class, "users");
System.out.println("Found users older than " + minAge + " (sorted by name): " + users);
return users;
}
/**
* 查找姓名包含 "John" (不区分大小写) 或者 邮箱以 ".com" 结尾的用户
* @return 用户列表
*/
public List<User> findUsersByNameRegexOrEmailSuffix() {
// 条件1: 姓名包含 "John" (忽略大小写)
Criteria nameCriteria = Criteria.where("user_name").regex("John", "i"); // "i" 表示忽略大小写
// 条件2: 邮箱以 .com 结尾
Criteria emailCriteria = Criteria.where("email").regex("\\.com$", ""); // $ 表示结尾, "." 需要转义
// 组合 OR 条件
Criteria orCriteria = new Criteria().orOperator(nameCriteria, emailCriteria);
// 创建 Query
Query query = new Query(orCriteria);
// 执行查询
List<User> users = mongoTemplate.find(query, User.class);
System.out.println("Found users with name regex 'John' (i) or email ending in '.com': " + users);
return users;
}
}
注释:
Criteria.where("fieldName")
开始定义一个字段的条件。- 可以链式调用
.is()
,.gt()
,.lt()
,.regex()
等方法。 new Criteria().orOperator(criteria1, criteria2)
用于组合OR
条件。默认情况下,链式调用或多个Criteria
放入Query
是AND
关系。Query
对象可以设置排序 (.with(Sort.by(...))
)、分页 (limit()
,skip()
) 和字段投影 (fields().include()/exclude()
)。mongoTemplate.find(query, EntityClass.class)
执行查询。
使用 Aggregation
MongoDB 的聚合框架 (Aggregation Framework) 是一个强大的数据处理管道,用于对集合中的文档进行多阶段处理,例如分组、计算、转换等,非常适合数据分析和报表生成。
什么是 Aggregation
?
在 Spring Data MongoDB 中,Aggregation
API 用于构建和执行 MongoDB 的聚合管道。管道由一系列阶段 (Stages) 组成,每个阶段对输入的文档进行处理,并将结果传递给下一个阶段。
常用聚合阶段 (Stages)
Spring Data 方法 | MongoDB 阶段 | 描述 | SQL (MySQL) 类似 |
---|---|---|---|
match(criteria) |
$match |
过滤文档,类似于 find 的条件 |
WHERE / HAVING (概念上) |
group(fields...) |
$group |
按指定字段分组,并进行聚合运算 (sum, avg等) | GROUP BY |
project(fields...) |
$project |
重塑文档结构 (选择、重命名、添加计算字段) | SELECT field1, field2, calculation |
sort(sort) |
$sort |
按指定字段排序 | ORDER BY |
limit(n) |
$limit |
限制输出的文档数量 | LIMIT n |
skip(n) |
$skip |
跳过指定数量的文档 | OFFSET n |
unwind("field") |
$unwind |
将数组字段的每个元素拆分成独立的文档 | (无直接对应,类似 JOIN 或规范化) |
lookup(...) |
$lookup |
执行类似 SQL 的左外连接 (Left Outer Join) | LEFT JOIN |
count("fieldName") |
$count |
计算输入文档的数量,并输出到指定字段 | SELECT COUNT(*)... |
addFields(...) |
$addFields |
向文档添加新字段 | (无直接对应) |
sortByCount("field") |
$sortByCount |
按字段值的频率分组和排序 (组合了 $group 和 $sort ) |
SELECT field, COUNT(*) GROUP BY field ORDER BY COUNT(*) DESC |
代码示例 (Aggregation
)
假设我们想按年龄分组,统计每个年龄段的用户数量,并按年龄排序。
java
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.GroupOperation;
import org.springframework.data.mongodb.core.aggregation.MatchOperation;
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation;
import org.springframework.data.mongodb.core.aggregation.SortOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.domain.Sort;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.yourproject.model.User; // 假设有 User 类
import java.util.List;
// 定义一个类来接收聚合结果 (字段名需要匹配 $project 或 $group 的输出)
class AgeGroupResult {
private int age; // 对应 $group 的 _id.age
private long count; // 对应 $group 的 count
// Getters and Setters
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public long getCount() { return count; }
public void setCount(long count) { this.count = count; }
@Override
public String toString() {
return "AgeGroupResult{" + "age=" + age + ", count=" + count + '}';
}
}
@Service
public class AggregationService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 按年龄分组统计用户数量,只统计年龄大于 18 的用户,并按年龄排序
* @return 每个年龄及其对应的用户数量列表
*/
public List<AgeGroupResult> groupUsersByAge() {
// 1. $match 阶段: 过滤年龄大于 18 的用户 (可选)
MatchOperation matchStage = Aggregation.match(Criteria.where("age").gt(18));
// 2. $group 阶段: 按 age 字段分组,并计算每个分组的数量 (count)
// 将分组的键 (age) 放入 _id 字段中
GroupOperation groupStage = Aggregation.group("age") // 按 "age" 字段分组
.count().as("count"); // 计算数量,并将结果命名为 "count"
// 3. $project 阶段: 重塑输出,将 _id (即 age) 映射到 age 字段,并包含 count 字段
// 默认的 _id 是分组依据,我们想让它叫 "age"
ProjectionOperation projectStage = Aggregation.project("count") // 选择 "count" 字段
.and("_id").as("age") // 将分组产生的 "_id" (即年龄) 重命名为 "age"
.andExclude("_id"); // 排除掉原始的 "_id" 字段
// 4. $sort 阶段: 按 age 字段升序排序
SortOperation sortStage = Aggregation.sort(Sort.Direction.ASC, "age");
// 5. 构建聚合管道 (按顺序添加阶段)
Aggregation aggregation = Aggregation.newAggregation(
matchStage, // 先过滤
groupStage, // 再分组
projectStage, // 然后重塑输出
sortStage // 最后排序
);
// 6. 执行聚合查询
// aggregate() 方法需要 Aggregation 对象、输入集合的名称、输出结果映射的类类型
AggregationResults<AgeGroupResult> results = mongoTemplate.aggregate(
aggregation,
"users", // 指定要聚合的集合名称 ("users" 对应 @Document(collection = "users"))
AgeGroupResult.class // 指定结果映射的类
);
// 7. 获取映射后的结果列表
List<AgeGroupResult> mappedResults = results.getMappedResults();
System.out.println("Aggregation Results (Users grouped by age > 18): " + mappedResults);
return mappedResults;
}
}
重点:
- 聚合是按顺序执行的多个阶段,每个阶段的输出是下一个阶段的输入。
Aggregation.newAggregation(...)
用于构建整个管道。match()
,group()
,project()
,sort()
等静态方法创建对应的聚合阶段。group("field")
指定分组依据的字段。.count().as("fieldName")
,.sum("field").as("total")
,.avg("field").as("average")
等用于在分组内进行计算。project()
非常重要,用于调整输出文档的结构,使其匹配你的结果类 (AgeGroupResult
)。and("_id").as("newName")
常用于重命名分组产生的_id
。- 需要创建一个结果类 (如
AgeGroupResult
)来映射聚合操作的输出。字段名必须与$project
或$group
阶段最后输出的字段名匹配。 mongoTemplate.aggregate(aggregation, inputCollectionName, OutputClass.class)
执行聚合。
9. MongoDB 操作与 SQL (MySQL) 对比
下表简要对比了 MongoDB (概念/Shell语法) 和 Spring Data MongoDB (Java API) 与 SQL (以 MySQL 为例) 的常用操作:
操作类别 | MongoDB (概念 / Shell 示例) | Spring Data MongoDB (MongoRepository / MongoTemplate ) |
SQL (MySQL 示例) |
---|---|---|---|
数据库/集合 | use mydatabase / db.createCollection("users") |
配置 spring.data.mongodb.database / @Document(collection="users") (自动创建) |
CREATE DATABASE mydatabase; / USE mydatabase; CREATE TABLE users (...); |
插入数据 | db.users.insertOne({name:"A", age:30}) |
userRepository.save(new User("A", 30)) |
INSERT INTO users (name, age) VALUES ('A', 30); |
查询所有数据 | db.users.find({}) |
userRepository.findAll() / mongoTemplate.findAll(User.class) |
SELECT * FROM users; |
条件查询 | db.users.find({age: {$gt: 25}}) |
userRepository.findByAgeGreaterThan(25) / mongoTemplate.find(Query.query(Criteria.where("age").gt(25)), User.class) |
SELECT * FROM users WHERE age > 25; |
AND 条件 | db.users.find({name:"A", age:30}) |
mongoTemplate.find(Query.query(Criteria.where("name").is("A").and("age").is(30)), User.class) / userRepository.findByNameAndAge("A", 30) (衍生查询) |
SELECT * FROM users WHERE name = 'A' AND age = 30; |
OR 条件 | db.users.find({$or: [{name:"A"}, {age:30}]}) |
mongoTemplate.find(Query.query(new Criteria().orOperator(Criteria.where("name").is("A"), Criteria.where("age").is(30))), User.class) |
SELECT * FROM users WHERE name = 'A' OR age = 30; |
更新数据 | db.users.updateOne({name:"A"}, {$set:{age:31}}) |
User u = userRepository.findByName("A"); if(u!=null){ u.setAge(31); userRepository.save(u); } (更灵活更新用 mongoTemplate.updateFirst/updateMulti ) |
UPDATE users SET age = 31 WHERE name = 'A'; |
删除数据 | db.users.deleteOne({name:"A"}) |
User u = userRepository.findByName("A"); if(u!=null){ userRepository.delete(u); } / userRepository.deleteById(id) |
DELETE FROM users WHERE name = 'A'; |
排序 | db.users.find().sort({age: 1}) |
userRepository.findAll(Sort.by("age")) / query.with(Sort.by(Sort.Direction.ASC, "age")) |
SELECT * FROM users ORDER BY age ASC; |
限制数量/分页 | db.users.find().limit(10).skip(20) |
Pageable p = PageRequest.of(2, 10); userRepository.findAll(p) / query.limit(10).skip(20) |
SELECT * FROM users LIMIT 10 OFFSET 20; |
分组统计 | db.users.aggregate([{$group:{_id:"$age", count:{$sum:1}}}]) |
(见上方 Aggregation 示例) | SELECT age, COUNT(*) FROM users GROUP BY age; |
选择特定字段 | db.users.find({}, {name: 1, email: 1, _id: 0}) |
query.fields().include("name", "email").excludeId() |
SELECT name, email FROM users; |
关键差异:
- Schema: MongoDB 是 Schema-less (或 Schema-flexible),集合中的文档可以有不同的结构。MySQL 是 Schema-based,表结构需要预先定义。
- 数据模型: MongoDB 是面向文档的 (BSON/JSON 格式),支持嵌套文档和数组。MySQL 是关系型的,数据存储在行和列组成的表中。
- 查询语言: MongoDB 使用基于 JSON 的查询语言。MySQL 使用 SQL。
- 事务: MongoDB 从 4.0 版本开始支持多文档 ACID 事务,但使用场景和复杂性与关系型数据库有所不同。传统上关系型数据库的事务支持更成熟和广泛。
- Join : MongoDB 通过
$lookup
(聚合) 实现类似 Join 的功能,但通常鼓励通过嵌入文档 或引用来设计数据模型以避免复杂的 Join。MySQL 的 Join 是核心功能。
10. 重点内容总结
- 核心依赖 :
spring-boot-starter-data-mongodb
是整合的关键。 - 配置 : 通过
application.properties
或application.yml
配置数据库连接,spring.data.mongodb.uri
是首选方式。 - 实体映射 : 使用
@Document
标记类,@Id
标记主键。@Field
用于字段名映射。 - 基础 CRUD : 继承
MongoRepository<Entity, IdType>
接口,可以获得大量开箱即用的 CRUD 方法和强大的方法名衍生查询功能,极大简化开发。 save()
方法 : 同时处理插入 和更新操作 (Upsert)。MongoTemplate
: 当需要更复杂、灵活的查询、更新或聚合操作时使用。它是执行底层 MongoDB 命令的主要入口。Query
和Criteria
: 使用MongoTemplate
进行查询时,用Criteria
构建查询条件 (类似 SQLWHERE
),用Query
封装条件、排序、分页和投影。Aggregation
: 用于执行多阶段的数据处理管道(分组、计算、转换),非常适合数据分析和报表。需要定义阶段 (match
,group
,project
,sort
等) 并按顺序组合。通常需要创建结果类来映射聚合输出。- MongoDB vs SQL: 理解两者在数据模型、查询方式、事务处理和 Join 上的核心差异很重要。Spring Data MongoDB 在一定程度上抽象了这些差异,但底层概念仍然不同。
11. 结语
恭喜你!通过这篇笔记,你应该对如何在 Spring Boot 中使用 MongoDB 有了基本的了解。从简单的配置、实体映射、MongoRepository
的便捷 CRUD,到 MongoTemplate
提供的更强大查询能力 (Query
, Criteria
) 和数据处理能力 (Aggregation
),你已经掌握了核心的使用方法。
下一步建议:
- 动手实践:创建自己的 Spring Boot 项目,连接 MongoDB,尝试笔记中的所有示例。
- 深入学习
Query
和Criteria
的更多操作符。 - 探索更复杂的
Aggregation
管道应用。 - 了解 MongoDB 的索引 (
@Indexed
注解) 对查询性能的重要性。 - 研究 MongoDB 的事务 (如果你的应用场景需要)。
- 了解 GridFS (如果需要存储大文件)。
希望这篇笔记对你的学习之旅有所帮助!