【springboot 技术代码】集成mongodb 详细步骤_springboot mongodb
SpringBoot 深度集成 MongoDB 详细步骤
-
- 1. MongoDB 简介与 SpringBoot 集成概述
-
- 1.1 SpringBoot 集成 MongoDB 的优势
- 2. 环境准备与依赖配置
-
- 2.1 版本兼容性矩阵
- 2.2 详细依赖配置
- 2.3 详细配置说明
-
- 2.3.1 单节点配置
- 2.3.2 集群配置
- 3. 实体映射与集合管理
-
- 3.1 详细实体类注解
- 3.2 索引管理
- 4. 数据操作详解
-
- 4.1 Repository 接口扩展
- 4.2 MongoTemplate 高级操作
-
- 4.2.1 CRUD 操作
- 4.2.2 复杂查询构建
- 4.2.3 聚合操作
- 5. 高级特性与最佳实践
-
- 5.1 事务管理
- 5.2 变更流(Change Streams)
- 5.3 GridFS 文件存储
- 5.4 性能优化策略
- 6. 实战案例:电商平台商品管理系统
-
- 6.1 系统架构设计
- 6.2 核心功能实现
-
- 6.2.1 商品服务
- 6.2.2 商品评价服务
- 7. 测试策略
-
- 7.1 单元测试
- 7.2 集成测试
- 8. 常见问题与解决方案
-
- 8.1 连接问题排查
- 8.2 性能问题排查
- 8.3 数据一致性问题
- 9. 未来发展与扩展
-
- 9.1 分片集群扩展
- 9.2 时间序列集合(MongoDB 5.0+)
- 9.3 与 Atlas Search 集成
- 10. 总结
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 版本兼容性矩阵
2.2 详细依赖配置
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <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> <dependency> <groupId>de.flapdoodle.embed</groupId> <artifactId>de.flapdoodle.embed.mongo</artifactId> <scope>test</scope> </dependency></dependencies>
2.3 详细配置说明
2.3.1 单节点配置
# application.ymlspring: data: mongodb: host: localhost port: 27017 database: mydb username: myuser password: mypassword authentication-database: admin # 认证数据库 # 连接池配置 auto-index-creation: true gridfs: database: mygridfs # GridFS 存储的数据库
2.3.2 集群配置
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
3. 实体映射与集合管理
3.1 详细实体类注解
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;}
3.2 索引管理
import org.springframework.data.mongodb.core.index.IndexOperations;import org.springframework.data.mongodb.core.index.IndexInfo;@Servicepublic 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); }}
4. 数据操作详解
4.1 Repository 接口扩展
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();}
4.2 MongoTemplate 高级操作
4.2.1 CRUD 操作
@Servicepublic 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); }}
4.2.2 复杂查询构建
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);}
4.2.3 聚合操作
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();}// 聚合结果类@Datapublic static class CategoryStats { private String category; private Double avgPrice; private Double totalRevenue; private Long productCount;}
5. 高级特性与最佳实践
5.1 事务管理
@Servicepublic 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); }}
5.2 变更流(Change Streams)
@Servicepublic 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); }}
5.3 GridFS 文件存储
@Servicepublic 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<>()); }}
5.4 性能优化策略
- 索引优化:
- 为常用查询字段创建适当索引
- 使用复合索引优化多字段查询
- 定期分析查询性能并调整索引
- 查询优化:
- 使用投影只返回必要字段
- 限制返回文档数量
- 避免使用 $where 和 JavaScript 表达式
- 使用 covered queries(查询完全由索引满足)
- 批量操作:
- 使用 bulkWrite 进行批量插入/更新
- 批量操作时适当设置 ordered 参数
- 读写分离:
- 配置从节点读取
@Beanpublic MongoClient mongoClient() { ConnectionString connectionString = new ConnectionString( \"mongodb://host1:27017,host2:27017,host3:27017/mydb?readPreference=secondaryPreferred\"); return MongoClients.create(connectionString);}
- 分片集群:
- 对于大数据集,配置分片集群
- 选择合适的分片键
6. 实战案例:电商平台商品管理系统
6.1 系统架构设计
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ 前端应用/API │───▶│ SpringBoot应用 │───▶│ MongoDB集群 │└─────────────────┘ └─────────────────┘ └─────────────────┘ ▲▲▲ │││┌──────┴───────┐ ┌─────────┴─────────┐ ┌──────┴───────┐│ 缓存(Redis) │ │ 消息队列(Kafka) │ │ 文件存储(S3) │└──────────────┘ └───────────────────┘ └───────────────┘
6.2 核心功能实现
6.2.1 商品服务
@Servicepublic 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(); }}
6.2.2 商品评价服务
@Servicepublic 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 }}
7. 测试策略
7.1 单元测试
@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()); }}
7.2 集成测试
@SpringBootTest@Testcontainerspublic 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); }}
8. 常见问题与解决方案
8.1 连接问题排查
问题现象:无法连接到 MongoDB 服务器
排查步骤:
- 检查连接字符串是否正确
- 验证网络连通性
- 检查认证信息
- 查看 MongoDB 服务器日志
// 测试连接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());}
8.2 性能问题排查
问题现象:查询响应慢
排查步骤:
- 使用 explain() 分析查询执行计划
Query query = new Query(Criteria.where(\"price\").gt(100));Document explain = mongoTemplate.getCollection(\"products\") .find(query.getQueryObject()) .explain();System.out.println(explain.toJson());
- 检查索引使用情况
- 分析 MongoDB 服务器资源使用情况
- 优化查询条件和索引
8.3 数据一致性问题
解决方案:
- 使用多文档事务(MongoDB 4.0+)
- 实现最终一致性模式
- 使用变更流监听数据变化
- 定期数据校验和修复
9. 未来发展与扩展
9.1 分片集群扩展
@Configurationpublic 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); }}
9.2 时间序列集合(MongoDB 5.0+)
@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;}
9.3 与 Atlas Search 集成
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);}
10. 总结
本文详细介绍了 SpringBoot 集成 MongoDB 的完整方案,涵盖以下内容:
- 环境准备:版本兼容性、依赖配置、连接设置
- 数据建模:实体映射、索引管理、关系处理
- 数据操作:基础CRUD、复杂查询、聚合框架
- 高级特性:事务管理、变更流、GridFS
- 性能优化:索引策略、查询优化、读写分离
- 实战案例:电商平台商品管理系统实现
- 测试策略:单元测试、集成测试方法
- 问题排查:连接问题、性能问题解决方案
- 未来扩展:分片集群、时间序列集合等
通过本指南,您应该能够在 SpringBoot 项目中高效地集成 MongoDB,构建高性能、可扩展的应用程序,并根据业务需求进行定制化开发。