MongoDB从入门到精通:
第一步:MongoDB安装和配置
1.1 下载MongoDB Community Server
Windows系统:
- 访问 https://www.mongodb.com/try/download/community
- 选择版本:MongoDB Community Server
- 平台:Windows x64
- 下载 .msi 安装包
macOS系统:
# 使用Homebrew安装
brew tap mongodb/brew
brew install mongodb-community
Linux系统(Ubuntu):
# 导入公钥
wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -# 添加MongoDB源
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list# 更新包列表并安装
sudo apt-get update
sudo apt-get install -y mongodb-org
1.2 启动MongoDB服务
Windows:
# 启动服务(以管理员身份运行)
net start MongoDB# 或者直接运行
mongod --dbpath "C:\data\db"
macOS/Linux:
# 启动MongoDB服务
sudo systemctl start mongod# 设置开机自启
sudo systemctl enable mongod# 检查服务状态
sudo systemctl status mongod
1.3 连接MongoDB
# 打开MongoDB Shell
mongosh# 或者老版本使用
mongo
第一次连接验证:
// 显示所有数据库
show dbs// 创建并切换到新数据库
use testdb// 插入测试数据
db.users.insertOne({name: "测试用户", age: 25})// 查询数据
db.users.find()
第二步:MongoDB基础操作
2.1 数据库和集合操作
// 1. 数据库操作
show dbs // 显示所有数据库
use myapp // 创建/切换数据库
db.getName() // 获取当前数据库名
db.dropDatabase() // 删除当前数据库// 2. 集合操作
show collections // 显示当前数据库的所有集合
db.createCollection("users") // 创建集合
db.users.drop() // 删除集合
2.2 基础CRUD操作详解
插入操作:
// 插入单个文档
db.users.insertOne({name: "张三",age: 25,email: "zhangsan@example.com",address: {city: "北京",district: "朝阳区"},hobbies: ["读书", "游泳", "编程"],createTime: new Date()
})// 插入多个文档
db.users.insertMany([{name: "李四", age: 30, email: "lisi@example.com"},{name: "王五", age: 28, email: "wangwu@example.com"},{name: "赵六", age: 35, email: "zhaoliu@example.com"}
])
查询操作:
// 基础查询
db.users.find() // 查询所有
db.users.find({age: 25}) // 条件查询
db.users.find({age: {$gte: 30}}) // 年龄大于等于30
db.users.find({name: /张/}) // 正则表达式查询// 复杂查询条件
db.users.find({$and: [{age: {$gte: 25}},{age: {$lte: 35}}]
})// 数组查询
db.users.find({hobbies: "编程"}) // 数组包含元素
db.users.find({hobbies: {$in: ["读书", "游泳"]}}) // 数组包含任一元素// 嵌套文档查询
db.users.find({"address.city": "北京"})// 投影(只返回指定字段)
db.users.find({}, {name: 1, age: 1, _id: 0})// 排序和分页
db.users.find().sort({age: -1}) // 按年龄降序
db.users.find().limit(5) // 限制5条
db.users.find().skip(5).limit(5) // 跳过5条,返回5条
更新操作:
// 更新单个文档
db.users.updateOne({name: "张三"}, // 查询条件{$set: {age: 26, email: "new@example.com"}} // 更新内容
)// 更新多个文档
db.users.updateMany({age: {$gte: 30}},{$set: {status: "senior"}}
)// 更新操作符详解
db.users.updateOne({name: "张三"},{$set: {email: "newemail@example.com"}, // 设置字段值$inc: {age: 1}, // 数值增加$push: {hobbies: "旅游"}, // 向数组添加元素$pull: {hobbies: "游泳"}, // 从数组移除元素$currentDate: {lastModified: true} // 设置当前时间}
)// Upsert操作(不存在则插入)
db.users.updateOne({name: "新用户"},{$set: {age: 20, email: "newuser@example.com"}},{upsert: true}
)
删除操作:
// 删除单个文档
db.users.deleteOne({name: "张三"})// 删除多个文档
db.users.deleteMany({age: {$lt: 25}})// 删除所有文档(保留集合)
db.users.deleteMany({})
2.3 索引操作
// 创建索引
db.users.createIndex({email: 1}) // 单字段索引(1升序,-1降序)
db.users.createIndex({name: 1, age: -1}) // 复合索引
db.users.createIndex({email: 1}, {unique: true}) // 唯一索引// 查看索引
db.users.getIndexes()// 删除索引
db.users.dropIndex({email: 1})// 查看查询执行计划
db.users.find({email: "test@example.com"}).explain("executionStats")
2.4 聚合框架基础
// 基础聚合管道
db.users.aggregate([{$match: {age: {$gte: 25}}}, // 过滤{$group: { // 分组_id: null,avgAge: {$avg: "$age"},totalUsers: {$sum: 1}}}
])// 复杂聚合示例
db.orders.aggregate([{$match: {status: "completed"}}, // 筛选已完成订单{$group: { // 按客户分组_id: "$customerId",totalAmount: {$sum: "$amount"},orderCount: {$sum: 1}}},{$sort: {totalAmount: -1}}, // 按总金额降序{$limit: 10} // 取前10名
])
第三步:Spring Boot集成MongoDB
3.1 项目初始化
创建Spring Boot项目:
<!-- pom.xml依赖配置 -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>mongodb-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>mongodb-demo</name><properties><java.version>17</java.version></properties><dependencies><!-- Spring Boot Web Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Boot MongoDB Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency><!-- 验证框架 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!-- 测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
</project>
3.2 配置文件
application.yml配置:
server:port: 8080spring:data:mongodb:# 单节点配置uri: mongodb://localhost:27017/myapp# 或者详细配置# host: localhost# port: 27017# database: myapp# username: username # 如果需要认证# password: password # 如果需要认证jackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8logging:level:org.springframework.data.mongodb: DEBUG # 开启MongoDB日志
3.3 实体类定义
package com.example.model;import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.index.Indexed;import jakarta.validation.constraints.*;
import java.time.LocalDateTime;
import java.util.List;/*** 用户实体类* @Document 注解指定集合名称,如不指定则使用类名小写*/
@Document(collection = "users")
public class User {@Id // MongoDB主键,自动生成ObjectIdprivate String id;@NotBlank(message = "姓名不能为空")@Size(min = 2, max = 20, message = "姓名长度必须在2-20之间")@Field("name") // 指定字段名,可选private String name;@NotNull(message = "年龄不能为空")@Min(value = 1, message = "年龄必须大于0")@Max(value = 150, message = "年龄必须小于150")private Integer age;@Email(message = "邮箱格式不正确")@NotBlank(message = "邮箱不能为空")@Indexed(unique = true) // 创建唯一索引private String email;private String phone;private Address address; // 嵌套文档private List<String> hobbies; // 数组字段@Field("create_time")private LocalDateTime createTime;@Field("update_time")private LocalDateTime updateTime;private String status = "active"; // 默认值// 构造函数public User() {this.createTime = LocalDateTime.now();this.updateTime = LocalDateTime.now();}public User(String name, Integer age, String email) {this();this.name = name;this.age = age;this.email = email;}// Getter和Setter方法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;this.updateTime = LocalDateTime.now();}public Integer getAge() { return age; }public void setAge(Integer age) { this.age = age;this.updateTime = LocalDateTime.now();}public String getEmail() { return email; }public void setEmail(String email) { this.email = email;this.updateTime = LocalDateTime.now();}public String getPhone() { return phone; }public void setPhone(String phone) { this.phone = phone;this.updateTime = LocalDateTime.now();}public Address getAddress() { return address; }public void setAddress(Address address) { this.address = address;this.updateTime = LocalDateTime.now();}public List<String> getHobbies() { return hobbies; }public void setHobbies(List<String> hobbies) { this.hobbies = hobbies;this.updateTime = LocalDateTime.now();}public LocalDateTime getCreateTime() { return createTime; }public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }public LocalDateTime getUpdateTime() { return updateTime; }public void setUpdateTime(LocalDateTime updateTime) { this.updateTime = updateTime; }public String getStatus() { return status; }public void setStatus(String status) { this.status = status;this.updateTime = LocalDateTime.now();}@Overridepublic String toString() {return "User{" +"id='" + id + '\'' +", name='" + name + '\'' +", age=" + age +", email='" + email + '\'' +", phone='" + phone + '\'' +", address=" + address +", hobbies=" + hobbies +", createTime=" + createTime +", updateTime=" + updateTime +", status='" + status + '\'' +'}';}
}/*** 地址嵌套文档类*/
class Address {private String province;private String city;private String district;private String street;private String zipCode;public Address() {}public Address(String province, String city, String district, String street) {this.province = province;this.city = city;this.district = district;this.street = street;}// Getter和Setter方法public String getProvince() { return province; }public void setProvince(String province) { this.province = province; }public String getCity() { return city; }public void setCity(String city) { this.city = city; }public String getDistrict() { return district; }public void setDistrict(String district) { this.district = district; }public String getStreet() { return street; }public void setStreet(String street) { this.street = street; }public String getZipCode() { return zipCode; }public void setZipCode(String zipCode) { this.zipCode = zipCode; }@Overridepublic String toString() {return "Address{" +"province='" + province + '\'' +", city='" + city + '\'' +", district='" + district + '\'' +", street='" + street + '\'' +", zipCode='" + zipCode + '\'' +'}';}
}
3.4 Repository接口
package com.example.repository;import com.example.model.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;/*** 用户Repository接口* 继承MongoRepository获得基础CRUD操作*/
@Repository
public interface UserRepository extends MongoRepository<User, String> {// 1. 方法命名规则查询(Spring Data自动实现)// 根据姓名查找用户Optional<User> findByName(String name);// 根据邮箱查找用户Optional<User> findByEmail(String email);// 根据年龄范围查找用户List<User> findByAgeBetween(Integer minAge, Integer maxAge);// 根据年龄大于等于查找用户List<User> findByAgeGreaterThanEqual(Integer age);// 根据姓名包含关键字查找(忽略大小写)List<User> findByNameContainingIgnoreCase(String keyword);// 根据爱好查找用户List<User> findByHobbiesIn(List<String> hobbies);// 根据城市查找用户(嵌套文档查询)List<User> findByAddressCity(String city);// 根据状态和创建时间查找List<User> findByStatusAndCreateTimeBetween(String status, LocalDateTime startTime, LocalDateTime endTime);// 分页查询Page<User> findByStatus(String status, Pageable pageable);// 2. 自定义查询(使用@Query注解)/*** 根据年龄和城市查询用户* @param minAge 最小年龄* @param city 城市* @return 用户列表*/@Query("{'age': {$gte: ?0}, 'address.city': ?1}")List<User> findUsersByAgeAndCity(Integer minAge, String city);/*** 查询年龄在指定范围内的用户数量* @param minAge 最小年龄* @param maxAge 最大年龄* @return 用户数量*/@Query(value = "{'age': {$gte: ?0, $lte: ?1}}", count = true)long countUsersByAgeRange(Integer minAge, Integer maxAge);/*** 查询包含指定爱好的用户* @param hobby 爱好* @return 用户列表*/@Query("{'hobbies': {$in: [?0]}}")List<User> findUsersByHobby(String hobby);/*** 查询用户,只返回指定字段* @param status 状态* @return 用户列表(只包含name, age, email字段)*/@Query(value = "{'status': ?0}", fields = "{'name': 1, 'age': 1, 'email': 1}")List<User> findUsersWithSelectedFields(String status);/*** 删除指定状态的用户* @param status 状态* @return 删除的数量*/@Query(delete = true)long deleteByStatus(String status);
}
3.5 Service业务逻辑层
package com.example.service;import com.example.model.User;
import com.example.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
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.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;/*** 用户服务类* 包含基础CRUD和复杂数据操作*/
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Autowiredprivate MongoTemplate mongoTemplate; // 用于复杂操作// ==================== 基础CRUD操作 ====================/*** 创建用户* @param user 用户对象* @return 保存后的用户*/public User createUser(User user) {// 业务逻辑验证if (userRepository.findByEmail(user.getEmail()).isPresent()) {throw new RuntimeException("邮箱已存在: " + user.getEmail());}user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());return userRepository.save(user);}/*** 根据ID查找用户* @param id 用户ID* @return 用户对象*/public Optional<User> findUserById(String id) {return userRepository.findById(id);}/*** 根据邮箱查找用户* @param email 邮箱* @return 用户对象*/public Optional<User> findUserByEmail(String email) {return userRepository.findByEmail(email);}/*** 获取所有用户* @return 用户列表*/public List<User> findAllUsers() {return userRepository.findAll();}/*** 分页查询用户* @param page 页码(从0开始)* @param size 每页大小* @param sortBy 排序字段* @param sortDirection 排序方向(ASC/DESC)* @return 分页结果*/public Page<User> findUsersWithPagination(int page, int size, String sortBy, String sortDirection) {Sort sort = Sort.by(Sort.Direction.fromString(sortDirection), sortBy);Pageable pageable = PageRequest.of(page, size, sort);return userRepository.findAll(pageable);}/*** 更新用户信息* @param id 用户ID* @param user 更新的用户信息* @return 更新后的用户*/public User updateUser(String id, User user) {Optional<User> existingUser = userRepository.findById(id);if (existingUser.isEmpty()) {throw new RuntimeException("用户不存在: " + id);}User userToUpdate = existingUser.get();userToUpdate.setName(user.getName());userToUpdate.setAge(user.getAge());userToUpdate.setPhone(user.getPhone());userToUpdate.setAddress(user.getAddress());userToUpdate.setHobbies(user.getHobbies());userToUpdate.setUpdateTime(LocalDateTime.now());return userRepository.save(userToUpdate);}/*** 删除用户* @param id 用户ID*/public void deleteUser(String id) {if (!userRepository.existsById(id)) {throw new RuntimeException("用户不存在: " + id);}userRepository.deleteById(id);}// ==================== 复杂查询操作 ====================/*** 根据多个条件查询用户* @param name 姓名(可为空)* @param minAge 最小年龄(可为空)* @param maxAge 最大年龄(可为空)* @param city 城市(可为空)* @param status 状态(可为空)* @return 符合条件的用户列表*/public List<User> findUsersByMultipleConditions(String name, Integer minAge, Integer maxAge, String city, String status) {Query query = new Query();// 动态构建查询条件if (name != null && !name.trim().isEmpty()) {query.addCriteria(Criteria.where("name").regex(name, "i")); // 忽略大小写的正则匹配}if (minAge != null && maxAge != null) {query.addCriteria(Criteria.where("age").gte(minAge).lte(maxAge));} else if (minAge != null) {query.addCriteria(Criteria.where("age").gte(minAge));} else if (maxAge != null) {query.addCriteria(Criteria.where("age").lte(maxAge));}if (city != null && !city.trim().isEmpty()) {query.addCriteria(Criteria.where("address.city").is(city));}if (status != null && !status.trim().isEmpty()) {query.addCriteria(Criteria.where("status").is(status));}return mongoTemplate.find(query, User.class);}/*** 批量更新用户状态* @param userIds 用户ID列表* @param newStatus 新状态* @return 更新的数量*/public long batchUpdateUserStatus(List<String> userIds, String newStatus) {Query query = Query.query(Criteria.where("id").in(userIds));Update update = Update.update("status", newStatus).set("updateTime", LocalDateTime.now());return mongoTemplate.updateMulti(query, update, User.class).getModifiedCount();}/*** 为用户添加爱好* @param userId 用户ID* @param hobby 新爱好* @return 是否成功*/public boolean addHobbyToUser(String userId, String hobby) {Query query = Query.query(Criteria.where("id").is(userId));Update update = Update.update("updateTime", LocalDateTime.now()).addToSet("hobbies", hobby); // addToSet确保不重复添加return mongoTemplate.updateFirst(query, update, User.class).getModifiedCount() > 0;}/*** 从用户爱好中移除指定爱好* @param userId 用户ID* @param hobby 要移除的爱好* @return 是否成功*/public boolean removeHobbyFromUser(String userId, String hobby) {Query query = Query.query(Criteria.where("id").is(userId));Update update = Update.update("updateTime", LocalDateTime.now()).pull("hobbies", hobby);return mongoTemplate.updateFirst(query, update, User.class).getModifiedCount() > 0;}// ==================== 聚合操作 ====================/*** 获取用户年龄统计信息* @return 包含最小年龄、最大年龄、平均年龄的Map*/public Map<String, Object> getUserAgeStatistics() {Aggregation aggregation = Aggregation.newAggregation(Aggregation.group().min("age").as("minAge").max("age").as("maxAge").avg("age").as("avgAge").count().as("totalCount"));AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);return results.getMappedResults();}/*** 按年龄段分组统计用户数量* @return 年龄段用户统计*/public List<Map> getUserCountByAgeGroup() {Aggregation aggregation = Aggregation.newAggregation(Aggregation.project().and("name").as("name").and("age").as("age").and(Aggregation.when(Criteria.where("age").lt(18)).then("未成年").when(Criteria.where("age").gte(18).and("age").lt(30)).then("青年").when(Criteria.where("age").gte(30).and("age").lt(50)).then("中年").otherwise("老年")).as("ageGroup"),Aggregation.group("ageGroup").count().as("count"),Aggregation.project("count").and("_id").as("ageGroup"),Aggregation.sort(Sort.by("ageGroup")));AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);return results.getMappedResults();}/*** 查找最受欢迎的爱好(Top 10)* @return 爱好统计列表*/public List<Map> getTopHobbies(int limit) {Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(Criteria.where("hobbies").exists(true).ne(null)),Aggregation.unwind("hobbies"), // 展开数组Aggregation.group("hobbies").count().as("count"),Aggregation.project("count").and("_id").as("hobby"),Aggregation.sort(Sort.by(Sort.Direction.DESC, "count")),Aggregation.limit(limit));AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);return results.getMappedResults();}
}
3.6 Controller控制器
package com.example.controller;import com.example.model.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import java.util.List;
import java.util.Map;
import java.util.Optional;/*** 用户控制器* 提供RESTful API接口*/
@RestController
@RequestMapping("/api/users")
@Validated
@CrossOrigin(origins = "*") // 允许跨域请求
public class UserController {@Autowiredprivate UserService userService;// ==================== 基础CRUD接口 ====================/*** 创建用户* POST /api/users*/@PostMappingpublic ResponseEntity<?> createUser(@Valid @RequestBody User user) {try {User createdUser = userService.createUser(user);return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);} catch (Exception e) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("error", e.getMessage()));}}/*** 根据ID获取用户* GET /api/users/{id}*/@GetMapping("/{id}")public ResponseEntity<?> getUserById(@PathVariable String id) {Optional<User> user = userService.findUserById(id);if (user.isPresent()) {return ResponseEntity.ok(user.get());} else {return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("error", "用户不存在"));}}/*** 获取所有用户* GET /api/users*/@GetMappingpublic ResponseEntity<List<User>> getAllUsers() {List<User> users = userService.findAllUsers();return ResponseEntity.ok(users);}/*** 分页查询用户* GET /api/users/page?page=0&size=10&sortBy=name&sortDirection=ASC*/@GetMapping("/page")public ResponseEntity<Page<User>> getUsersWithPagination(@RequestParam(defaultValue = "0") @Min(0) int page,@RequestParam(defaultValue = "10") @Min(1) int size,@RequestParam(defaultValue = "createTime") String sortBy,@RequestParam(defaultValue = "DESC") String sortDirection) {Page<User> users = userService.findUsersWithPagination(page, size, sortBy, sortDirection);return ResponseEntity.ok(users);}/*** 更新用户信息* PUT /api/users/{id}*/@PutMapping("/{id}")public ResponseEntity<?> updateUser(@PathVariable String id, @Valid @RequestBody User user) {try {User updatedUser = userService.updateUser(id, user);return ResponseEntity.ok(updatedUser);} catch (Exception e) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("error", e.getMessage()));}}/*** 删除用户* DELETE /api/users/{id}*/@DeleteMapping("/{id}")public ResponseEntity<?> deleteUser(@PathVariable String id) {try {userService.deleteUser(id);return ResponseEntity.ok(Map.of("message", "用户删除成功"));} catch (Exception e) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("error", e.getMessage()));}}// ==================== 复杂查询接口 ====================/*** 多条件查询用户* GET /api/users/search?name=张&minAge=20&maxAge=40&city=北京&status=active*/@GetMapping("/search")public ResponseEntity<List<User>> searchUsers(@RequestParam(required = false) String name,@RequestParam(required = false) Integer minAge,@RequestParam(required = false) Integer maxAge,@RequestParam(required = false) String city,@RequestParam(required = false) String status) {List<User> users = userService.findUsersByMultipleConditions(name, minAge, maxAge, city, status);return ResponseEntity.ok(users);}/*** 根据邮箱查找用户* GET /api/users/by-email?email=test@example.com*/@GetMapping("/by-email")public ResponseEntity<?> getUserByEmail(@RequestParam String email) {Optional<User> user = userService.findUserByEmail(email);if (user.isPresent()) {return ResponseEntity.ok(user.get());} else {return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("error", "用户不存在"));}}// ==================== 批量操作接口 ====================/*** 批量更新用户状态* PATCH /api/users/batch/status*/@PatchMapping("/batch/status")public ResponseEntity<?> batchUpdateUserStatus(@RequestBody Map<String, Object> request) {@SuppressWarnings("unchecked")List<String> userIds = (List<String>) request.get("userIds");String newStatus = (String) request.get("status");long updatedCount = userService.batchUpdateUserStatus(userIds, newStatus);return ResponseEntity.ok(Map.of("message", "批量更新完成","updatedCount", updatedCount));}/*** 为用户添加爱好* POST /api/users/{id}/hobbies*/@PostMapping("/{id}/hobbies")public ResponseEntity<?> addHobby(@PathVariable String id,@RequestBody Map<String, String> request) {String hobby = request.get("hobby");boolean success = userService.addHobbyToUser(id, hobby);if (success) {return ResponseEntity.ok(Map.of("message", "爱好添加成功"));} else {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("error", "添加爱好失败"));}}/*** 移除用户爱好* DELETE /api/users/{id}/hobbies/{hobby}*/@DeleteMapping("/{id}/hobbies/{hobby}")public ResponseEntity<?> removeHobby(@PathVariable String id,@PathVariable String hobby) {boolean success = userService.removeHobbyFromUser(id, hobby);if (success) {return ResponseEntity.ok(Map.of("message", "爱好移除成功"));} else {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("error", "移除爱好失败"));}}// ==================== 统计分析接口 ====================/*** 获取用户年龄统计* GET /api/users/statistics/age*/@GetMapping("/statistics/age")public ResponseEntity<Map<String, Object>> getAgeStatistics() {Map<String, Object> statistics = userService.getUserAgeStatistics();return ResponseEntity.ok(statistics);}/*** 按城市统计用户数量* GET /api/users/statistics/by-city*/@GetMapping("/statistics/by-city")public ResponseEntity<List<Map>> getUserStatisticsByCity() {List<Map> statistics = userService.getUserCountByCity();return ResponseEntity.ok(statistics);}/*** 按年龄段统计用户数量* GET /api/users/statistics/by-age-group*/@GetMapping("/statistics/by-age-group")public ResponseEntity<List<Map>> getUserStatisticsByAgeGroup() {List<Map> statistics = userService.getUserCountByAgeGroup();return ResponseEntity.ok(statistics);}/*** 获取最受欢迎的爱好* GET /api/users/statistics/top-hobbies?limit=10*/@GetMapping("/statistics/top-hobbies")public ResponseEntity<List<Map>> getTopHobbies(@RequestParam(defaultValue = "10") int limit) {List<Map> hobbies = userService.getTopHobbies(limit);return ResponseEntity.ok(hobbies);}
}
3.7 全局异常处理
package com.example.exception;import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;import java.util.HashMap;
import java.util.Map;/*** 全局异常处理器*/
@RestControllerAdvice
public class GlobalExceptionHandler {/*** 处理参数验证异常*/@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity<Map<String, Object>> handleValidationExceptions(MethodArgumentNotValidException ex) {Map<String, Object> response = new HashMap<>();Map<String, String> errors = new HashMap<>();ex.getBindingResult().getAllErrors().forEach(error -> {String fieldName = ((FieldError) error).getField();String errorMessage = error.getDefaultMessage();errors.put(fieldName, errorMessage);});response.put("error", "参数验证失败");response.put("details", errors);return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);}/*** 处理运行时异常*/@ExceptionHandler(RuntimeException.class)public ResponseEntity<Map<String, String>> handleRuntimeException(RuntimeException ex) {Map<String, String> response = new HashMap<>();response.put("error", ex.getMessage());return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);}/*** 处理其他异常*/@ExceptionHandler(Exception.class)public ResponseEntity<Map<String, String>> handleGenericException(Exception ex) {Map<String, String> response = new HashMap<>();response.put("error", "服务器内部错误");response.put("details", ex.getMessage());return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);}
}
3.8 启动类
package com.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.config.EnableMongoAuditing;/*** Spring Boot应用启动类*/
@SpringBootApplication
@EnableMongoAuditing // 启用MongoDB审计功能(自动设置创建时间、更新时间)
public class MongodbDemoApplication {public static void main(String[] args) {SpringApplication.run(MongodbDemoApplication.class, args);System.out.println("MongoDB Spring Boot 应用启动成功!");System.out.println("访问 http://localhost:8080/api/users 测试API");}
}
第四步:测试和验证
4.1 使用Postman测试API
1. 创建用户 (POST /api/users)
{"name": "张三","age": 25,"email": "zhangsan@example.com","phone": "13800138000","address": {"province": "北京市","city": "北京","district": "朝阳区","street": "三里屯街道","zipCode": "100027"},"hobbies": ["读书", "游泳", "编程"]
}
2. 查询所有用户 (GET /api/users)
3. 根据ID查询用户 (GET /api/users/{id})
4. 多条件搜索 (GET /api/users/search?name=张&minAge=20&city=北京)
5. 分页查询 (GET /api/users/page?page=0&size=5&sortBy=age&sortDirection=DESC)
6. 更新用户 (PUT /api/users/{id})
7. 删除用户 (DELETE /api/users/{id})
4.2 高级操作测试
1. 批量更新状态 (PATCH /api/users/batch/status)
{"userIds": ["userId1", "userId2", "userId3"],"status": "inactive"
}
2. 添加爱好 (POST /api/users/{id}/hobbies)
{"hobby": "旅游"
}
3. 获取统计数据 (GET /api/users/statistics/age)
4.3 单元测试示例
package com.example.service;import com.example.model.User;
import com.example.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;import java.util.Optional;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
@TestPropertySource(properties = {"spring.data.mongodb.database=test_db"
})
class UserServiceTest {@Autowiredprivate UserService userService;@Autowiredprivate UserRepository userRepository;@Testvoid testCreateUser() {// 创建测试用户User user = new User("测试用户", 25, "test@example.com");// 保存用户User savedUser = userService.createUser(user);// 验证结果assertNotNull(savedUser.getId());assertEquals("测试用户", savedUser.getName());assertEquals(25, savedUser.getAge());assertEquals("test@example.com", savedUser.getEmail());// 清理测试数据userRepository.deleteById(savedUser.getId());}@Testvoid testFindUserByEmail() {// 创建并保存测试用户User user = new User("查询测试", 30, "search@example.com");User savedUser = userRepository.save(user);// 查询用户Optional<User> foundUser = userService.findUserByEmail("search@example.com");// 验证结果assertTrue(foundUser.isPresent());assertEquals("查询测试", foundUser.get().getName());// 清理测试数据userRepository.deleteById(savedUser.getId());}
}
第五步:高级MongoDB操作
5.1 复杂聚合查询示例
/*** 复杂聚合查询服务*/
@Service
public class AdvancedQueryService {@Autowiredprivate MongoTemplate mongoTemplate;/*** 用户活跃度分析* 分析用户在不同时间段的活跃情况*/public List<Map> analyzeUserActivity() {Aggregation aggregation = Aggregation.newAggregation(// 1. 按月份分组Aggregation.project().and(DateOperators.DateToString.dateOf("createTime").toString("%Y-%m")).as("month").andInclude("name", "age", "status"),// 2. 按月份和状态分组统计Aggregation.group("month", "status").count().as("count"),// 3. 重新组织数据结构Aggregation.project("count").and("_id.month").as("month").and("_id.status").as("status"),// 4. 按月份排序Aggregation.sort(Sort.by("month")));AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);return results.getMappedResults();}/*** 地区用户画像分析*/public List<Map> analyzeUserProfileByRegion() {Aggregation aggregation = Aggregation.newAggregation(// 1. 匹配有地址信息的用户Aggregation.match(Criteria.where("address.city").exists(true)),// 2. 按城市分组,计算各项统计指标Aggregation.group("address.city").count().as("totalUsers").avg("age").as("avgAge").min("age").as("minAge").max("age").as("maxAge").addToSet("hobbies").as("allHobbies"),// 3. 处理爱好数据Aggregation.project("totalUsers", "avgAge", "minAge", "maxAge").and("_id").as("city").and(ArrayOperators.Size.lengthOfArray(ArrayOperators.Reduce.arrayOf("allHobbies").withInitialValue(new Object[0]).withReduceExpression(SetOperators.SetUnion.arrayAsSet("$value").union("$this")))).as("uniqueHobbiesCount"),// 4. 按用户数量排序Aggregation.sort(Sort.by(Sort.Direction.DESC, "totalUsers")));AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, "users", Map.class);return results.getMappedResults();}
}
5.2 索引优化策略
/*** MongoDB索引管理服务*/
@Service
public class IndexManagementService {@Autowiredprivate MongoTemplate mongoTemplate;/*** 创建用户集合的索引*/@PostConstructpublic void createUserIndexes() {MongoCollection<Document> collection = mongoTemplate.getCollection("users");// 1. 邮箱唯一索引collection.createIndex(Indexes.ascending("email"),new IndexOptions().unique(true));// 2. 复合索引:状态+创建时间collection.createIndex(Indexes.compound(Indexes.ascending("status"),Indexes.descending("createTime")));// 3. 地理位置索引(如果有坐标数据)collection.createIndex(Indexes.geo2dsphere("location"));// 4. 文本搜索索引collection.createIndex(Indexes.compound(Indexes.text("name"),Indexes.text("hobbies")),new IndexOptions().defaultLanguage("chinese"));// 5. 部分索引(只为活跃用户创建索引)collection.createIndex(Indexes.ascending("email"),new IndexOptions().partialFilterExpression(Filters.eq("status", "active")));}/*** 分析查询性能*/public String analyzeQueryPerformance(Query query) {query.with(Sort.by(Sort.Direction.DESC, "createTime"));// 获取查询执行计划AggregationExplainDocument explainResult = mongoTemplate.execute(User.class, collection -> {FindIterable<Document> findIterable = collection.find(query.getQueryObject()).sort(query.getSortObject());return findIterable.explain(ExplainVerbosity.EXECUTION_STATS);});return explainResult.toJson();}
}
总结
这个完整的教程涵盖了:
- MongoDB安装配置 - 从下载到启动的完整步骤
- 基础操作 - CRUD、索引、聚合等核心功能
- Spring Boot集成 - 完整的项目结构和代码实现
- RESTful API - 全面的接口设计和实现
- 高级特性 - 复杂查询、聚合分析、性能优化
学习建议:
- 先理解MongoDB的文档型存储概念
- 熟练掌握基础CRUD操作
- 重点学习聚合框架,这是MongoDB的强项
- 在实际项目中多实践,理论结合实际
- 关注性能优化,合理设计索引
每个代码段都有详细注释,你可以根据需要逐步实现和测试。有任何具体问题都可以继续问我!c