1. MongoDB 简介与 SpringBoot 集成概述
MongoDB 是一个基于[分布式]文件存储的开源 NoSQL 数据库系统,采用文档存储格式(BSON,类似 JSON)。与传统关系型数据库相比,MongoDB 具有以下特点:
- 无模式设计:集合中的文档可以有不同的结构
- 水平扩展:通过分片实现数据分布式存储
- 高性能:内存映射文件技术提高 I/O 效率
- 丰富的查询语言:支持丰富的查询表达式和索引类型
- 聚合框架:强大的数据分析能力
1.1 SpringBoot 集成 MongoDB 的优势
- 自动配置:SpringBoot 自动配置 MongoDB 客户端连接
- Repository 抽象:类似 JPA 的操作方式,降低学习曲线
- 对象映射:自动将 Java 对象与 MongoDB 文档相互转换
- 事务支持:支持多文档 ACID 事务(MongoDB 4.0+)
- 与 Spring 生态无缝集成:可与 Spring Data、Spring Security 等协同工作
2. 环境准备与依赖配置
2.1 版本兼容性矩阵
SpringBoot 版本 | Spring Data MongoDB 版本 | MongoDB 驱动版本 | MongoDB 服务器版本 |
---|---|---|---|
2.7.x | 3.4.x | 4.4.x | 4.4-5.0 |
3.0.x | 4.0.x | 4.8.x | 5.0-6.0 |
3.1.x | 4.1.x | 4.9.x | 6.0+ |
2.2 详细依赖配置
xml
<!-- pom.xml -->
<dependencies>
<!-- SpringBoot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Data MongoDB -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- 响应式支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<!-- 工具类 -->
<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>
<!-- MongoDB 嵌入式测试 -->
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
AI写代码xml
1234567891011121314151617181920212223242526272829303132333435363738394041
2.3 详细配置说明
2.3.1 单节点配置
yaml
# application.yml
spring:
data:
mongodb:
host: localhost
port: 27017
database: mydb
username: myuser
password: mypassword
authentication-database: admin # 认证数据库
# 连接池配置
auto-index-creation: true
gridfs:
database: mygridfs # GridFS 存储的数据库
AI写代码yaml
123456789101112131415
2.3.2 集群配置
yaml
spring:
data:
mongodb:
uri: mongodb://user:password@host1:27017,host2:27017,host3:27017/mydb?replicaSet=myReplicaSet&authSource=admin
# 高级连接配置
option:
min-connection-per-host: 10
max-connection-per-host: 100
max-wait-time: 120000
connect-timeout: 5000
socket-timeout: 60000
server-selection-timeout: 30000
AI写代码yaml
12345678910111213
3. 实体映射与集合管理
3.1 详细实体类注解
kotlin
import org.springframework.data.annotation.*;
import org.springframework.data.mongodb.core.index.*;
import org.springframework.data.mongodb.core.mapping.*;
@Document(collection = "products") // 指定集合名称
@CompoundIndex(def = "{'name': 1, 'category': 1}", name = "name_category_idx")
public class Product {
@Id
private String id;
@Indexed(unique = true, direction = IndexDirection.ASCENDING)
private String sku;
@Field("product_name") // 自定义字段名
@TextIndexed(weight = 2)
private String name;
@TextIndexed
private String description;
@Indexed(direction = IndexDirection.DESCENDING)
private Double price;
@Indexed
private String category;
@CreatedDate
private Date createTime;
@LastModifiedDate
private Date updateTime;
@Version
private Long version;
@DBRef
private List<Review> reviews;
@Transient // 不持久化到数据库
private Double discountPrice;
// 嵌套文档
private Manufacturer manufacturer;
// 数组
private List<String> tags;
// 地理空间数据
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
private double[] location;
// 省略 getter/setter
}
// 嵌套文档类
public class Manufacturer {
private String name;
private String address;
private String contact;
}
// 引用文档类
@Document(collection = "reviews")
public class Review {
@Id
private String id;
private String content;
private Integer rating;
private Date createTime;
}
AI写代码java
运行
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
3.2 索引管理
typescript
import org.springframework.data.mongodb.core.index.IndexOperations;
import org.springframework.data.mongodb.core.index.IndexInfo;
@Service
public class IndexManagementService {
@Autowired
private MongoTemplate mongoTemplate;
// 创建索引
public String createIndex(Class<?> entityClass, String field, IndexDirection direction) {
IndexOperations indexOps = mongoTemplate.indexOps(entityClass);
Index index = new Index().on(field, direction);
return indexOps.ensureIndex(index);
}
// 创建复合索引
public String createCompoundIndex(Class<?> entityClass, String... fields) {
IndexOperations indexOps = mongoTemplate.indexOps(entityClass);
IndexDefinition indexDef = new CompoundIndexDefinition(
new Document().append(fields[0], 1).append(fields[1], 1)
);
return indexOps.ensureIndex(indexDef);
}
// 创建文本索引
public String createTextIndex(Class<?> entityClass, String... fields) {
IndexOperations indexOps = mongoTemplate.indexOps(entityClass);
IndexDefinition indexDef = new TextIndexDefinitionBuilder()
.onFields(fields)
.withDefaultLanguage("english")
.withLanguageOverride("language")
.build();
return indexOps.ensureIndex(indexDef);
}
// 获取所有索引
public List<IndexInfo> getIndexes(Class<?> entityClass) {
IndexOperations indexOps = mongoTemplate.indexOps(entityClass);
return indexOps.getIndexInfo();
}
// 删除索引
public void dropIndex(Class<?> entityClass, String indexName) {
IndexOperations indexOps = mongoTemplate.indexOps(entityClass);
indexOps.dropIndex(indexName);
}
}
AI写代码java
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
4. 数据操作详解
4.1 Repository 接口扩展
kotlin
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.*;
public interface ProductRepository extends MongoRepository<Product, String> {
// 基本查询
List<Product> findByName(String name);
Product findBySku(String sku);
List<Product> findByPriceBetween(Double minPrice, Double maxPrice);
List<Product> findByCategoryOrderByPriceDesc(String category);
// 分页查询
Page<Product> findByCategory(String category, Pageable pageable);
// 使用 @Query 注解
@Query("{'name': ?0}")
List<Product> findByNameCustom(String name);
@Query("{'price': {$gt: ?0, $lt: ?1}}")
List<Product> findByPriceRange(Double minPrice, Double maxPrice);
// 使用正则表达式
List<Product> findByNameLike(String namePattern);
// 使用 SpEL 表达式
@Query("{'$where': 'this.price > ?0'}")
List<Product> findByPriceGreaterThan(Double price);
// 多条件查询
List<Product> findByNameAndCategory(String name, String category);
// 使用聚合
@Aggregation(pipeline = {
"{$match: {category: ?0}}",
"{$group: {_id: '$manufacturer.name', avgPrice: {$avg: '$price'}}}"
})
List<AveragePriceByManufacturer> averagePriceByManufacturer(String category);
// 文本搜索
@TextScore
List<Product> findByDescriptionContaining(String text, Pageable pageable);
// 地理空间查询
List<Product> findByLocationNear(Point point, Distance distance);
// 使用 exists 查询
List<Product> findByManufacturerExists(boolean exists);
// 使用注解定义索引
@Meta(maxScanDocuments = 1000)
List<Product> findByTagsIn(List<String> tags);
}
// 聚合结果投影接口
public interface AveragePriceByManufacturer {
String getManufacturerName();
Double getAvgPrice();
}
AI写代码java
运行
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
4.2 MongoTemplate 高级操作
4.2.1 CRUD 操作
csharp
@Service
public class ProductService {
@Autowired
private MongoTemplate mongoTemplate;
// 插入文档
public Product saveProduct(Product product) {
return mongoTemplate.save(product);
}
// 批量插入
public List<Product> saveAllProducts(List<Product> products) {
return mongoTemplate.insertAll(products);
}
// 查询单个文档
public Product findById(String id) {
return mongoTemplate.findById(id, Product.class);
}
// 条件查询
public List<Product> findExpensiveProducts(Double minPrice) {
Query query = new Query(Criteria.where("price").gt(minPrice));
return mongoTemplate.find(query, Product.class);
}
// 更新文档
public UpdateResult updateProductPrice(String id, Double newPrice) {
Query query = new Query(Criteria.where("id").is(id));
Update update = new Update().set("price", newPrice)
.currentDate("updateTime");
return mongoTemplate.updateFirst(query, update, Product.class);
}
// 删除文档
public DeleteResult deleteProduct(String id) {
Query query = new Query(Criteria.where("id").is(id));
return mongoTemplate.remove(query, Product.class);
}
}
AI写代码java
运行
1234567891011121314151617181920212223242526272829303132333435363738394041
4.2.2 复杂查询构建
scss
public List<Product> complexProductSearch(ProductSearchCriteria criteria) {
Criteria criteriaChain = new Criteria();
// 关键词查询
if (StringUtils.isNotBlank(criteria.getKeyword())) {
TextCriteria textCriteria = TextCriteria.forDefaultLanguage()
.matchingAny(criteria.getKeyword())
.caseSensitive(false);
criteriaChain.andOperator(textCriteria);
}
// 分类过滤
if (CollectionUtils.isNotEmpty(criteria.getCategories())) {
criteriaChain.and("category").in(criteria.getCategories());
}
// 价格范围
if (criteria.getMinPrice() != null || criteria.getMaxPrice() != null) {
Criteria priceCriteria = new Criteria("price");
if (criteria.getMinPrice() != null) {
priceCriteria.gte(criteria.getMinPrice());
}
if (criteria.getMaxPrice() != null) {
priceCriteria.lte(criteria.getMaxPrice());
}
criteriaChain.andOperator(priceCriteria);
}
// 标签查询
if (CollectionUtils.isNotEmpty(criteria.getTags())) {
criteriaChain.and("tags").all(criteria.getTags());
}
// 地理位置查询
if (criteria.getLatitude() != null && criteria.getLongitude() != null) {
Point point = new Point(criteria.getLongitude(), criteria.getLatitude());
criteriaChain.and("location").nearSphere(point)
.maxDistance(criteria.getDistanceInKilometers() / 111.12); // 转换为弧度
}
Query query = new Query(criteriaChain);
// 排序
if (StringUtils.isNotBlank(criteria.getSortBy())) {
Sort.Direction direction = criteria.isAscending() ? Sort.Direction.ASC : Sort.Direction.DESC;
query.with(Sort.by(direction, criteria.getSortBy()));
}
// 分页
if (criteria.getPage() != null && criteria.getSize() != null) {
query.with(PageRequest.of(criteria.getPage(), criteria.getSize()));
}
// 字段投影
if (CollectionUtils.isNotEmpty(criteria.getFieldsToInclude())) {
Fields fields = Fields.fields();
criteria.getFieldsToInclude().forEach(fields::include);
query.fields(fields);
}
return mongoTemplate.find(query, Product.class);
}
AI写代码java
运行
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
4.2.3 聚合操作
kotlin
public List<CategoryStats> getCategoryStatistics() {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("price").gt(0)), // 过滤条件
Aggregation.group("category") // 按分类分组
.avg("price").as("avgPrice") // 计算平均价格
.sum("price").as("totalRevenue") // 计算总收入
.count().as("productCount"), // 计算产品数量
Aggregation.project() // 投影
.and("_id").as("category")
.and("avgPrice").as("avgPrice")
.and("totalRevenue").as("totalRevenue")
.and("productCount").as("productCount"),
Aggregation.sort(Sort.Direction.DESC, "totalRevenue"), // 按总收入降序排序
Aggregation.limit(10) // 限制返回结果数
);
AggregationResults<CategoryStats> results = mongoTemplate.aggregate(
aggregation, "products", CategoryStats.class);
return results.getMappedResults();
}
// 聚合结果类
@Data
public static class CategoryStats {
private String category;
private Double avgPrice;
private Double totalRevenue;
private Long productCount;
}
AI写代码java
运行
123456789101112131415161718192021222324252627282930
5. 高级特性与最佳实践
5.1 事务管理
scss
@Service
public class OrderService {
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private ProductRepository productRepository;
@Transactional
public Order createOrder(Order order) {
// 检查并扣减库存
for (OrderItem item : order.getItems()) {
Product product = productRepository.findById(item.getProductId())
.orElseThrow(() -> new RuntimeException("Product not found"));
if (product.getStock() < item.getQuantity()) {
throw new RuntimeException("Insufficient stock");
}
product.setStock(product.getStock() - item.getQuantity());
productRepository.save(product);
}
// 保存订单
return mongoTemplate.save(order);
}
// 配置事务管理器
@Bean
public MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
}
AI写代码java
运行
12345678910111213141516171819202122232425262728293031323334
5.2 变更流(Change Streams)
csharp
@Service
public class ProductChangeListener {
@Autowired
private MongoTemplate mongoTemplate;
@PostConstruct
public void watchProductChanges() {
new Thread(() -> {
List<Bson> pipeline = Arrays.asList(
Aggregates.match(
Filters.in("operationType",
Arrays.asList("insert", "update", "delete"))
)
);
MongoCollection<Document> collection = mongoTemplate.getCollection("products");
ChangeStreamIterable<Document> changeStream = collection.watch(pipeline)
.fullDocument(FullDocument.UPDATE_LOOKUP);
changeStream.forEach(event -> {
String operationType = event.getOperationType().getValue();
Document fullDocument = event.getFullDocument();
switch (operationType) {
case "insert":
handleInsert(fullDocument);
break;
case "update":
handleUpdate(event.getDocumentKey(), fullDocument);
break;
case "delete":
handleDelete(event.getDocumentKey());
break;
}
});
}).start();
}
private void handleInsert(Document productDoc) {
System.out.println("New product added: " + productDoc.toJson());
}
private void handleUpdate(Bson documentKey, Document fullDocument) {
System.out.println("Product updated: " + documentKey + ", new data: " + fullDocument.toJson());
}
private void handleDelete(Bson documentKey) {
System.out.println("Product deleted: " + documentKey);
}
}
AI写代码java
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
5.3 GridFS 文件存储
typescript
@Service
public class FileStorageService {
@Autowired
private GridFsTemplate gridFsTemplate;
public String storeFile(InputStream inputStream, String filename, String contentType) {
ObjectId fileId = gridFsTemplate.store(inputStream, filename, contentType);
return fileId.toString();
}
public GridFSDBFile getFile(String fileId) {
return gridFsTemplate.findOne(new Query(Criteria.where("_id").is(fileId)));
}
public void deleteFile(String fileId) {
gridFsTemplate.delete(new Query(Criteria.where("_id").is(fileId)));
}
public List<GridFSFile> listFiles() {
return gridFsTemplate.find(new Query()).into(new ArrayList<>());
}
}
AI写代码java
运行
1234567891011121314151617181920212223
5.4 性能优化策略
-
索引优化:
- 为常用查询字段创建适当索引
- 使用复合索引优化多字段查询
- 定期分析查询性能并调整索引
-
查询优化:
- 使用投影只返回必要字段
- 限制返回文档数量
- 避免使用 $where 和 JavaScript 表达式
- 使用 covered queries(查询完全由索引满足)
-
批量操作:
- 使用 bulkWrite 进行批量插入/更新
- 批量操作时适当设置 ordered 参数
-
读写分离:
- 配置从节点读取
java
@Bean
public MongoClient mongoClient() {
ConnectionString connectionString = new ConnectionString(
"mongodb://host1:27017,host2:27017,host3:27017/mydb?readPreference=secondaryPreferred");
return MongoClients.create(connectionString);
}
AI写代码java
运行
123456
-
分片集群:
- 对于大数据集,配置分片集群
- 选择合适的分片键
6. 实战案例:电商平台商品管理系统
6.1 系统架构设计
scss
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 前端应用/API │───▶│ SpringBoot应用 │───▶│ MongoDB集群 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
▲ ▲ ▲
│ │ │
┌──────┴───────┐ ┌─────────┴─────────┐ ┌──────┴───────┐
│ 缓存(Redis) │ │ 消息队列(Kafka) │ │ 文件存储(S3) │
└──────────────┘ └───────────────────┘ └───────────────┘
AI写代码
12345678
6.2 核心功能实现
6.2.1 商品服务
java
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductRepository productRepository;
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String PRODUCT_CACHE_PREFIX = "product:";
@Override
public Product createProduct(Product product) {
// 验证SKU唯一性
if (productRepository.findBySku(product.getSku()) != null) {
throw new RuntimeException("SKU already exists");
}
// 设置创建时间
product.setCreateTime(new Date());
product.setUpdateTime(new Date());
// 保存到数据库
Product savedProduct = productRepository.save(product);
// 清除相关缓存
redisTemplate.delete(PRODUCT_CACHE_PREFIX + savedProduct.getId());
redisTemplate.delete("products:category:" + savedProduct.getCategory());
return savedProduct;
}
@Override
public Product getProductById(String id) {
// 先尝试从缓存获取
Product cachedProduct = (Product) redisTemplate.opsForValue()
.get(PRODUCT_CACHE_PREFIX + id);
if (cachedProduct != null) {
return cachedProduct;
}
// 从数据库查询
Product product = productRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Product not found"));
// 存入缓存
redisTemplate.opsForValue().set(
PRODUCT_CACHE_PREFIX + id,
product,
30,
TimeUnit.MINUTES);
return product;
}
@Override
public Page<Product> searchProducts(ProductSearchRequest request) {
// 构建查询条件
Criteria criteria = new Criteria();
if (StringUtils.isNotBlank(request.getKeyword())) {
criteria.orOperator(
Criteria.where("name").regex(request.getKeyword(), "i"),
Criteria.where("description").regex(request.getKeyword(), "i")
);
}
if (StringUtils.isNotBlank(request.getCategory())) {
criteria.and("category").is(request.getCategory());
}
if (request.getMinPrice() != null && request.getMaxPrice() != null) {
criteria.and("price").gte(request.getMinPrice()).lte(request.getMaxPrice());
}
Query query = new Query(criteria);
// 分页和排序
Pageable pageable = PageRequest.of(
request.getPage(),
request.getSize(),
Sort.by(Sort.Direction.fromString(request.getSortDirection()), request.getSortBy()));
query.with(pageable);
// 执行查询
long count = mongoTemplate.count(query, Product.class);
List<Product> products = mongoTemplate.find(query, Product.class);
return new PageImpl<>(products, pageable, count);
}
@Override
@Transactional
public void updateProductStock(String productId, int quantityChange) {
// 使用原子操作更新库存
Query query = new Query(Criteria.where("id").is(productId));
Update update = new Update().inc("stock", quantityChange)
.currentDate("updateTime");
UpdateResult result = mongoTemplate.updateFirst(query, update, Product.class);
if (result.getMatchedCount() == 0) {
throw new RuntimeException("Product not found");
}
// 清除缓存
redisTemplate.delete(PRODUCT_CACHE_PREFIX + productId);
}
@Override
public List<CategoryStats> getCategoryStatistics() {
// 使用聚合框架统计分类数据
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("status").is("ACTIVE")),
Aggregation.group("category")
.avg("price").as("avgPrice")
.sum("stock").as("totalStock")
.count().as("productCount"),
Aggregation.sort(Sort.Direction.DESC, "productCount")
);
AggregationResults<CategoryStats> results = mongoTemplate.aggregate(
aggregation, Product.class, CategoryStats.class);
return results.getMappedResults();
}
}
AI写代码java
运行
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
6.2.2 商品评价服务
kotlin
@Service
public class ReviewServiceImpl implements ReviewService {
@Autowired
private ReviewRepository reviewRepository;
@Autowired
private MongoTemplate mongoTemplate;
@Override
public Review addReview(Review review) {
// 验证产品是否存在
if (!mongoTemplate.exists(
new Query(Criteria.where("id").is(review.getProductId())),
Product.class)) {
throw new RuntimeException("Product not found");
}
// 设置创建时间
review.setCreateTime(new Date());
// 保存评价
Review savedReview = reviewRepository.save(review);
// 更新产品的平均评分
updateProductRating(review.getProductId());
return savedReview;
}
private void updateProductRating(String productId) {
// 使用聚合框架计算平均评分
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("productId").is(productId)),
Aggregation.group().avg("rating").as("avgRating")
);
AggregationResults<AverageRating> results = mongoTemplate.aggregate(
aggregation, Review.class, AverageRating.class);
Double avgRating = results.getMappedResults().isEmpty() ?
0.0 : results.getMappedResults().get(0).getAvgRating();
// 更新产品评分
Query query = new Query(Criteria.where("id").is(productId));
Update update = new Update().set("averageRating", avgRating)
.currentDate("updateTime");
mongoTemplate.updateFirst(query, update, Product.class);
}
@Override
public Page<Review> getProductReviews(String productId, Pageable pageable) {
return reviewRepository.findByProductId(productId, pageable);
}
@Override
public List<RatingDistribution> getRatingDistribution(String productId) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("productId").is(productId)),
Aggregation.group("rating")
.count().as("count"),
Aggregation.sort(Sort.Direction.ASC, "_id")
);
AggregationResults<RatingDistribution> results = mongoTemplate.aggregate(
aggregation, Review.class, RatingDistribution.class);
return results.getMappedResults();
}
// 聚合结果类
private static class AverageRating {
private Double avgRating;
// getter/setter
}
private static class RatingDistribution {
private Integer rating;
private Long count;
// getter/setter
}
}
AI写代码java
运行
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
7. 测试策略
7.1 单元测试
scss
@DataMongoTest
@ExtendWith(SpringExtension.class)
public class ProductRepositoryTest {
@Autowired
private ProductRepository productRepository;
@Autowired
private MongoTemplate mongoTemplate;
@BeforeEach
public void setup() {
productRepository.deleteAll();
// 初始化测试数据
Product product1 = new Product();
product1.setName("Laptop");
product1.setCategory("Electronics");
product1.setPrice(999.99);
Product product2 = new Product();
product2.setName("Smartphone");
product2.setCategory("Electronics");
product2.setPrice(699.99);
productRepository.saveAll(Arrays.asList(product1, product2));
}
@Test
public void testFindByCategory() {
List<Product> electronics = productRepository.findByCategory("Electronics");
assertEquals(2, electronics.size());
}
@Test
public void testPriceRangeQuery() {
List<Product> expensiveProducts = productRepository.findByPriceBetween(700.0, 1000.0);
assertEquals(1, expensiveProducts.size());
assertEquals("Laptop", expensiveProducts.get(0).getName());
}
}
AI写代码java
运行
1234567891011121314151617181920212223242526272829303132333435363738394041
7.2 集成测试
java
@SpringBootTest
@Testcontainers
public class ProductServiceIntegrationTest {
@Container
static MongoDBContainer mongoDBContainer = new MongoDBContainer("mongo:5.0");
@DynamicPropertySource
static void setProperties(DynamicPropertyRegistry registry) {
registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl);
}
@Autowired
private ProductService productService;
@Autowired
private ProductRepository productRepository;
@Test
@Transactional
public void testCreateAndRetrieveProduct() {
Product product = new Product();
product.setName("Test Product");
product.setCategory("Test");
product.setPrice(100.0);
Product savedProduct = productService.createProduct(product);
assertNotNull(savedProduct.getId());
Product retrievedProduct = productService.getProductById(savedProduct.getId());
assertEquals("Test Product", retrievedProduct.getName());
}
@Test
public void testProductSearch() {
ProductSearchRequest request = new ProductSearchRequest();
request.setCategory("Electronics");
request.setPage(0);
request.setSize(10);
Page<Product> result = productService.searchProducts(request);
assertTrue(result.getTotalElements() > 0);
}
}
AI写代码java
运行
1234567891011121314151617181920212223242526272829303132333435363738394041424344
8. 常见问题与解决方案
8.1 连接问题排查
问题现象:无法连接到 MongoDB 服务器
排查步骤:
- 检查连接字符串是否正确
- 验证网络连通性
- 检查认证信息
- 查看 MongoDB 服务器日志
csharp
// 测试连接
try {
MongoClient client = MongoClients.create("mongodb://localhost:27017");
client.listDatabaseNames().first();
System.out.println("Connection successful");
} catch (Exception e) {
System.err.println("Connection failed: " + e.getMessage());
}
AI写代码java
运行
12345678
8.2 性能问题排查
问题现象:查询响应慢
排查步骤:
- 使用 explain() 分析查询执行计划
ini
Query query = new Query(Criteria.where("price").gt(100));
Document explain = mongoTemplate.getCollection("products")
.find(query.getQueryObject())
.explain();
System.out.println(explain.toJson());
AI写代码java
运行
12345
- 检查索引使用情况
- 分析 MongoDB 服务器资源使用情况
- 优化查询条件和索引
8.3 数据一致性问题
解决方案:
- 使用多文档事务(MongoDB 4.0+)
- 实现最终一致性模式
- 使用变更流监听数据变化
- 定期数据校验和修复
9. 未来发展与扩展
9.1 分片集群扩展
scss
@Configuration
public class ShardingConfig {
@Bean
public MongoClient mongoClient() {
// 配置分片集群连接
ConnectionString connectionString = new ConnectionString(
"mongodb://shard1:27017,shard2:27017,shard3:27017/admin?replicaSet=shardRS");
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.writeConcern(WriteConcern.MAJORITY)
.readPreference(ReadPreference.secondaryPreferred())
.retryWrites(true)
.build();
return MongoClients.create(settings);
}
}
AI写代码java
运行
12345678910111213141516171819
9.2 时间序列集合(MongoDB 5.0+)
typescript
@Document(collection = "sensor_readings")
@TimeSeries(collection = "sensor_readings",
timeField = "timestamp",
metaField = "sensorMetadata",
granularity = TimeSeriesGranularity.SECONDS)
public class SensorReading {
@Id
private String id;
private Date timestamp;
private SensorMetadata sensorMetadata;
private Double value;
// getter/setter
}
public class SensorMetadata {
private String sensorId;
private String location;
private String type;
}
AI写代码java
运行
123456789101112131415161718192021222324
9.3 与 Atlas Search 集成
scss
public List<Product> fullTextSearch(String query) {
TextCriteria criteria = TextCriteria.forDefaultLanguage()
.matching(query)
.caseSensitive(false)
.diacriticSensitive(false);
Query searchQuery = TextQuery.queryText(criteria)
.sortByScore()
.with(PageRequest.of(0, 10));
return mongoTemplate.find(searchQuery, Product.class);
}
AI写代码java
运行
123456789101112
10. 总结
本文详细介绍了 SpringBoot 集成 MongoDB 的完整方案,涵盖以下内容:
- 环境准备:版本兼容性、依赖配置、连接设置
- 数据建模:实体映射、索引管理、关系处理
- 数据操作:基础CRUD、复杂查询、聚合框架
- 高级特性:事务管理、变更流、GridFS
- 性能优化:索引策略、查询优化、读写分离
- 实战案例:电商平台商品管理系统实现
- 测试策略:单元测试、集成测试方法
- 问题排查:连接问题、性能问题解决方案
- 未来扩展:分片集群、时间序列集合等
通过本指南,您应该能够在 SpringBoot 项目中高效地集成 MongoDB,构建高性能、可扩展的应用程序,并根据业务需求进行定制化开发。