Spring Boot 整合 MongoDB 学习笔记 (新手入门)
目录
- 引言
- 环境准备
- 步骤 1: 添加 Maven/Gradle 依赖
- 步骤 2: 配置 MongoDB 连接
- 步骤 3: 创建实体类 (Domain Model)
- 步骤 4: 创建 Repository 接口
- 步骤 5: 实现基础 CRUD 操作
- 创建 (Create) / 更新 (Update)
- 读取 (Read)
- 删除 (Delete)
- 步骤 6: 进阶查询 - 使用
MongoTemplate
- 什么是
MongoTemplate
? - 使用
Query
和Criteria
- 什么是
Query
? - 什么是
Criteria
? - 常用
Criteria
方法 - 代码示例
- 什么是
- 使用
Aggregation
- 什么是
Aggregation
? - 常用聚合阶段 (Stages)
- 代码示例
- 什么是
- 什么是
- 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
):
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId><!-- 通常不需要指定版本,Spring Boot会管理 -->
</dependency>
Gradle (build.gradle
):
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
格式:
# 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
格式:
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)。
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(); // 调用默认构造函数设置 createdAtthis.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; }@Overridepublic 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 操作方法。我们只需要定义一个接口继承它即可。
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 操作。
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 对象有有效的 iduserRepository.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 来执行各种数据库操作。你需要在使用它的类中注入它:
import org.springframework.data.mongodb.core.MongoTemplate;@Service
public class AdvancedUserService {@Autowiredprivate 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
)
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 {@Autowiredprivate MongoTemplate mongoTemplate;/*** 查找年龄大于指定值,并且按姓名升序排序的用户* @param minAge 最小年龄* @return 用户列表*/public List<User> findUsersOlderThanAndSortByName(int minAge) {// 1. 创建查询条件 (Criteria)Criteria ageCriteria = Criteria.where("age").gt(minAge); // 年龄大于 minAge// 2. 创建 Query 对象,并应用 CriteriaQuery 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);// 创建 QueryQuery 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
)
假设我们想按年龄分组,统计每个年龄段的用户数量,并按年龄排序。
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.ageprivate long count; // 对应 $group 的 count// Getters and Setterspublic 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; }@Overridepublic String toString() {return "AgeGroupResult{" + "age=" + age + ", count=" + count + '}';}
}@Service
public class AggregationService {@Autowiredprivate 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 (如果需要存储大文件)。
希望这篇笔记对你的学习之旅有所帮助!