大家好,我是小悟。
一、MongoDB简介
MongoDB是一个基于分布式文件存储的NoSQL数据库,具有以下特点:
- 文档型数据库:数据以BSON(Binary JSON)格式存储,结构灵活
- 无模式设计:集合中的文档可以有不同的字段,无需预先定义表结构
- 高性能:支持索引、聚合管道、丰富的查询语言
- 高可用性:通过副本集实现数据冗余和故障转移
- 水平扩展:通过分片集群实现数据水平拆分
- 丰富的查询功能:支持丰富的查询操作符和聚合框架
二、整合步骤与代码实现
1. 环境准备
pom.xml 依赖配置:
xml
<dependencies>
<!-- Spring Boot Starter Data MongoDB -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- Spring Boot Starter Web (可选,用于REST API) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok (简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
application.yml 配置:
yaml
spring:
data:
mongodb:
# 连接字符串格式
uri: mongodb://localhost:27017/testdb
# 或者分开配置
# host: localhost
# port: 27017
# database: testdb
# username: user
# password: pass
# 连接池配置
auto-index-creation: true
# 自定义配置
mongodb:
collection:
user: users
product: products
2. 实体类定义
kotlin
package com.example.mongodemo.entity;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Builder;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.time.LocalDateTime;
import java.util.List;
/**
* 用户实体类
* @Document 指定MongoDB集合名称
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "users") // 对应集合名
public class User {
@Id // MongoDB主键
private String id;
@Field("username") // 指定字段名,可选
private String username;
private String email;
private Integer age;
private Address address; // 嵌套文档
private List<String> hobbies; // 数组类型
private LocalDateTime createTime;
@Field("is_active")
private Boolean isActive;
/**
* 嵌套文档示例
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Address {
private String city;
private String street;
private String zipCode;
}
}
3. Repository接口
java
package com.example.mongodemo.repository;
import com.example.mongodemo.entity.User;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* 用户Repository
* 继承MongoRepository获得CRUD基本操作
*/
@Repository
public interface UserRepository extends MongoRepository<User, String> {
// 自定义查询方法 - 根据方法名自动生成查询
List<User> findByUsername(String username);
List<User> findByAgeGreaterThan(int age);
List<User> findByEmailContaining(String domain);
Optional<User> findByUsernameAndEmail(String username, String email);
long countByIsActiveTrue();
// 使用@Query注解自定义查询
@Query("{ 'age' : { $gt: ?0, $lt: ?1 } }")
List<User> findUsersByAgeBetween(int min, int max);
@Query("{ 'address.city' : ?0 }")
List<User> findByCity(String city);
@Query("{ 'hobbies' : { $in: ?0 } }")
List<User> findByHobbiesIn(List<String> hobbies);
// 使用正则表达式查询
@Query("{ 'email' : { $regex: ?0 } }")
List<User> findByEmailPattern(String pattern);
}
4. 自定义Repository实现
java
package com.example.mongodemo.repository.custom;
import com.example.mongodemo.entity.User;
import java.util.List;
public interface CustomUserRepository {
List<User> findActiveUsersWithCustomQuery();
void updateUserEmail(String userId, String newEmail);
}
package com.example.mongodemo.repository.custom.impl;
import com.example.mongodemo.entity.User;
import com.example.mongodemo.repository.custom.CustomUserRepository;
import lombok.RequiredArgsConstructor;
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.Repository;
import java.util.List;
@Repository
@RequiredArgsConstructor
public class CustomUserRepositoryImpl implements CustomUserRepository {
private final MongoTemplate mongoTemplate;
@Override
public List<User> findActiveUsersWithCustomQuery() {
Query query = new Query();
query.addCriteria(Criteria.where("isActive").is(true))
.addCriteria(Criteria.where("age").gte(18));
return mongoTemplate.find(query, User.class);
}
@Override
public void updateUserEmail(String userId, String newEmail) {
Query query = new Query(Criteria.where("id").is(userId));
Update update = new Update();
update.set("email", newEmail);
mongoTemplate.updateFirst(query, update, User.class);
}
}
5. 更新主Repository接口
kotlin
package com.example.mongodemo.repository;
import com.example.mongodemo.entity.User;
import com.example.mongodemo.repository.custom.CustomUserRepository;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends MongoRepository<User, String>, CustomUserRepository {
// 方法继承自两个接口
}
6. Service层实现
arduino
package com.example.mongodemo.service;
import com.example.mongodemo.entity.User;
import com.example.mongodemo.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
@Slf4j
public class UserService {
private final UserRepository userRepository;
/**
* 创建用户
*/
public User createUser(User user) {
user.setCreateTime(LocalDateTime.now());
if (user.getIsActive() == null) {
user.setIsActive(true);
}
return userRepository.save(user);
}
/**
* 批量插入用户
*/
public List<User> batchCreateUsers(List<User> users) {
users.forEach(user -> {
user.setCreateTime(LocalDateTime.now());
if (user.getIsActive() == null) {
user.setIsActive(true);
}
});
return userRepository.saveAll(users);
}
/**
* 根据ID查询用户
*/
public Optional<User> getUserById(String id) {
return userRepository.findById(id);
}
/**
* 查询所有活跃用户
*/
public List<User> getActiveUsers() {
return userRepository.findByIsActiveTrue();
}
/**
* 分页查询用户
*/
public Page<User> getUsersByPage(Pageable pageable) {
return userRepository.findAll(pageable);
}
/**
* 根据用户名和邮箱查询
*/
public Optional<User> getUserByUsernameAndEmail(String username, String email) {
return userRepository.findByUsernameAndEmail(username, email);
}
/**
* 更新用户邮箱
*/
public boolean updateUserEmail(String userId, String newEmail) {
Optional<User> userOpt = userRepository.findById(userId);
if (userOpt.isPresent()) {
User user = userOpt.get();
user.setEmail(newEmail);
userRepository.save(user);
return true;
}
return false;
}
/**
* 使用自定义查询更新邮箱
*/
public void updateUserEmailWithCustom(String userId, String newEmail) {
userRepository.updateUserEmail(userId, newEmail);
}
/**
* 删除用户
*/
public boolean deleteUser(String id) {
if (userRepository.existsById(id)) {
userRepository.deleteById(id);
return true;
}
return false;
}
/**
* 根据年龄范围查询用户
*/
public List<User> findUsersByAgeRange(int minAge, int maxAge) {
return userRepository.findUsersByAgeBetween(minAge, maxAge);
}
/**
* 统计活跃用户数量
*/
public long countActiveUsers() {
return userRepository.countByIsActiveTrue();
}
}
7. Controller层
less
package com.example.mongodemo.controller;
import com.example.mongodemo.entity.User;
import com.example.mongodemo.service.UserService;
import lombok.RequiredArgsConstructor;
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.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userService.createUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
}
@PostMapping("/batch")
public ResponseEntity<List<User>> batchCreateUsers(@RequestBody List<User> users) {
List<User> savedUsers = userService.batchCreateUsers(users);
return ResponseEntity.status(HttpStatus.CREATED).body(savedUsers);
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable String id) {
Optional<User> user = userService.getUserById(id);
return user.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@GetMapping
public ResponseEntity<Page<User>> getAllUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "createTime") String sortBy) {
Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy).descending());
Page<User> users = userService.getUsersByPage(pageable);
return ResponseEntity.ok(users);
}
@GetMapping("/active")
public ResponseEntity<List<User>> getActiveUsers() {
List<User> activeUsers = userService.getActiveUsers();
return ResponseEntity.ok(activeUsers);
}
@GetMapping("/age-range")
public ResponseEntity<List<User>> getUsersByAgeRange(
@RequestParam int minAge,
@RequestParam int maxAge) {
List<User> users = userService.findUsersByAgeRange(minAge, maxAge);
return ResponseEntity.ok(users);
}
@PutMapping("/{id}/email")
public ResponseEntity<Void> updateEmail(
@PathVariable String id,
@RequestParam String newEmail) {
boolean updated = userService.updateUserEmail(id, newEmail);
return updated ? ResponseEntity.ok().build() : ResponseEntity.notFound().build();
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable String id) {
boolean deleted = userService.deleteUser(id);
return deleted ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build();
}
@GetMapping("/stats/active-count")
public ResponseEntity<Long> getActiveUserCount() {
long count = userService.countActiveUsers();
return ResponseEntity.ok(count);
}
}
8. 聚合查询示例
typescript
package com.example.mongodemo.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.aggregation.GroupOperation;
import org.springframework.data.mongodb.core.aggregation.MatchOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
@RequiredArgsConstructor
@Slf4j
public class UserAggregationService {
private final MongoTemplate mongoTemplate;
/**
* 按城市分组统计用户数量
*/
public List<UserCityStats> groupUsersByCity() {
GroupOperation groupByCity = Aggregation.group("address.city")
.count().as("userCount")
.avg("age").as("averageAge");
Aggregation aggregation = Aggregation.newAggregation(groupByCity);
AggregationResults<UserCityStats> results =
mongoTemplate.aggregate(aggregation, "users", UserCityStats.class);
return results.getMappedResults();
}
/**
* 统计各年龄段用户数量
*/
public List<AgeGroupStats> groupUsersByAgeGroup() {
GroupOperation groupByAgeGroup = Aggregation.group(
Aggregation.function("cond",
Aggregation.function("and",
Aggregation.function("gte", "age", 0),
Aggregation.function("lte", "age", 18)
),
"0-18",
Aggregation.function("cond",
Aggregation.function("and",
Aggregation.function("gt", "age", 18),
Aggregation.function("lte", "age", 30)
),
"19-30",
"30+"
)
)
).count().as("count");
Aggregation aggregation = Aggregation.newAggregation(groupByAgeGroup);
AggregationResults<AgeGroupStats> results =
mongoTemplate.aggregate(aggregation, "users", AgeGroupStats.class);
return results.getMappedResults();
}
// 统计结果类
public static class UserCityStats {
private String city;
private Long userCount;
private Double averageAge;
// getters and setters
}
public static class AgeGroupStats {
private String ageGroup;
private Long count;
// getters and setters
}
}
9. 事务支持(需要MongoDB副本集)
java
package com.example.mongodemo.service;
import com.example.mongodemo.entity.User;
import com.example.mongodemo.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
@Service
@RequiredArgsConstructor
public class TransactionalUserService {
private final UserRepository userRepository;
/**
* 事务操作示例
* 注意:MongoDB事务需要副本集
*/
@Transactional(rollbackFor = Exception.class)
public void createUserWithTransaction(User user1, User user2) {
// 保存第一个用户
user1.setCreateTime(LocalDateTime.now());
user1.setIsActive(true);
userRepository.save(user1);
// 模拟业务逻辑
if (user1.getEmail() == null) {
throw new RuntimeException("Email is required");
}
// 保存第二个用户
user2.setCreateTime(LocalDateTime.now());
user2.setIsActive(true);
userRepository.save(user2);
}
}
10. 配置类
kotlin
package com.example.mongodemo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@Configuration
@EnableMongoRepositories(basePackages = "com.example.mongodemo.repository")
@EnableMongoAuditing // 启用审计功能
public class MongoConfig {
// 可以配置MongoTemplate的自定义设置
// @Bean
// public MongoTemplate mongoTemplate(MongoDatabaseFactory factory) {
// return new MongoTemplate(factory);
// }
}
11. 审计功能(自动填充创建/更新时间)
kotlin
package com.example.mongodemo.entity;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.mapping.Document;
import java.time.LocalDateTime;
@Data
@Document(collection = "auditable_entities")
public class AuditableEntity {
private String id;
private String name;
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
}
12. 测试类
scss
package com.example.mongodemo;
import com.example.mongodemo.entity.User;
import com.example.mongodemo.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.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import java.util.Arrays;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
class MongoDemoApplicationTests {
@Autowired
private UserRepository userRepository;
@Test
void testCRUDOperations() {
// 创建用户
User user = User.builder()
.username("testuser")
.email("test@example.com")
.age(25)
.isActive(true)
.hobbies(Arrays.asList("reading", "sports"))
.build();
// 保存
User savedUser = userRepository.save(user);
assertThat(savedUser.getId()).isNotNull();
// 查询
List<User> users = userRepository.findByUsername("testuser");
assertThat(users).hasSize(1);
// 更新
savedUser.setEmail("updated@example.com");
userRepository.save(savedUser);
// 删除
userRepository.delete(savedUser);
// 验证删除
long count = userRepository.count();
assertThat(count).isEqualTo(0);
}
@Test
void testPagination() {
// 创建测试数据
for (int i = 0; i < 15; i++) {
User user = User.builder()
.username("user" + i)
.email("user" + i + "@example.com")
.age(20 + i)
.isActive(true)
.build();
userRepository.save(user);
}
// 分页查询
Page<User> page = userRepository.findAll(PageRequest.of(0, 10));
assertThat(page.getContent()).hasSize(10);
assertThat(page.getTotalElements()).isEqualTo(15);
}
}
三、总结
1. 整合优势
Spring Data MongoDB提供了:
- 简化的操作:通过Repository接口减少大量模板代码
- 强大的查询:支持方法名查询、@Query注解、Criteria API
- 对象映射:自动将文档映射为Java对象
- 事务支持:支持MongoDB 4.0+的事务功能
- 聚合框架:支持复杂的聚合操作
- 审计功能:自动填充创建/更新时间
2. 最佳实践
- 连接配置:使用连接池提高性能,合理设置连接参数
- 索引优化:为频繁查询的字段创建索引
- 文档设计:合理设计文档结构,避免过度嵌套
- 批量操作:使用批量操作提高数据插入/更新效率
- 读写分离:考虑使用副本集实现读写分离
3. 注意事项
- 事务要求:MongoDB事务需要副本集环境
- 内存管理:注意大文档的内存消耗
- 连接管理:合理配置连接池大小
- 数据建模:根据查询模式设计文档结构
- 索引策略:避免过多的索引影响写入性能
4. 适用场景
- 内容管理系统:文章、评论等半结构化数据
- 物联网应用:设备日志、传感器数据
- 实时分析:用户行为分析、实时统计
- 社交应用:用户动态、消息数据
- 电商平台:商品信息、用户收藏
SpringBoot与MongoDB的整合提供了高效、灵活的数据存储方案,特别适合处理半结构化、快速增长的数据场景。通过Spring Data MongoDB的封装,可以像操作传统关系型数据库一样操作MongoDB,同时享受NoSQL数据库的灵活性和扩展性优势。

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。
您的一键三连,是我更新的最大动力,谢谢
山水有相逢,来日皆可期,谢谢阅读,我们再会
我手中的金箍棒,上能通天,下能探海