1. 环境准备与项目初始化在开始Spring Boot与MongoDB的整合前我们需要先完成基础环境搭建。不同于传统关系型数据库MongoDB的安装配置有其特殊性这也是许多初学者容易踩坑的地方。1.1 MongoDB安装与验证对于MacOS用户推荐使用Homebrew进行安装brew tap mongodb/brew brew install mongodb-community brew services start mongodb-communityWindows用户可以从MongoDB官网下载MSI安装包安装时需注意取消勾选Install MongoDB Compass可加快安装速度建议将安装路径设置为不含空格的目录如C:\mongodb安装完成后需要手动将bin目录加入系统PATH验证安装是否成功mongo --version mongod --version常见问题如果遇到无法定位程序输入点错误通常是版本兼容性问题建议卸载后重新安装最新稳定版。1.2 Spring Boot项目创建使用Spring Initializr创建项目时除了基础的Web依赖外需要特别添加Spring Data MongoDBLombok简化实体类代码Spring Boot Actuator可选用于健康检查Maven关键依赖配置dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-mongodb/artifactId /dependency !-- 使用Reactive编程时可添加 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-mongodb-reactive/artifactId /dependency /dependencies1.3 配置文件详解application.yml中MongoDB配置的完整参数示例spring: data: mongodb: uri: mongodb://user:passwordlocalhost:27017/blogdb?authSourceadmin auto-index-creation: true # 是否自动创建索引 authentication-database: admin # 认证数据库 gridfs: database: fs # GridFS存储配置2. 数据建模与Repository设计2.1 实体类注解深度解析MongoDB实体类的设计比JPA更灵活核心注解包括Document(collection articles, language english) public class Article { Id private String id; // 推荐使用String而非ObjectId Indexed(unique true, direction IndexDirection.DESCENDING) private String title; TextIndexed(weight 2) // 全文检索权重 private String content; Field(author_name) // 字段名映射 private String author; Transient // 不持久化到数据库 private Integer wordCount; CreatedDate // 自动审计 private LocalDateTime createTime; }2.2 复合索引与TTL索引在配置类中定义复杂索引Configuration public class MongoConfig { Bean public MongoTemplate mongoTemplate(MongoDatabaseFactory factory) { MongoTemplate template new MongoTemplate(factory); // 复合索引 template.indexOps(Article.class).ensureIndex( new CompoundIndexDefinition( new Document(author, 1) .append(createTime, -1) ).named(author_createTime_idx) ); // TTL索引自动过期 template.indexOps(Comment.class).ensureIndex( new Index().on(expireAt, Sort.Direction.ASC) .expire(0, TimeUnit.SECONDS) .named(comment_ttl_idx) ); return template; } }2.3 自定义Repository实现结合MongoRepository和MongoTemplate的优势public interface ArticleRepository extends MongoRepositoryArticle, String, CustomArticleRepository { // 方法名派生查询 ListArticle findByTagsContainsAndAuthorOrderByCreateTimeDesc( String tag, String author); Query(value {comments: {$elemMatch: {likes: {$gte: ?0}}}}) ListArticle findArticlesWithPopularComments(int minLikes); } public interface CustomArticleRepository { ListArticle findArticlesByComplexCriteria(SearchCriteria criteria); } public class CustomArticleRepositoryImpl implements CustomArticleRepository { Autowired private MongoTemplate mongoTemplate; Override public ListArticle findArticlesByComplexCriteria(SearchCriteria criteria) { Criteria where Criteria.where(published).is(true); if (criteria.getKeyword() ! null) { where.and(title).regex(criteria.getKeyword(), i); } Query query new Query(where) .with(Sort.by(Sort.Direction.DESC, createTime)) .limit(criteria.getLimit()); return mongoTemplate.find(query, Article.class); } }3. CRUD操作进阶实践3.1 批量操作优化MongoTemplate提供的批量操作比循环saveAll更高效public void bulkInsertArticles(ListArticle articles) { BulkOperations bulkOps mongoTemplate.bulkOps( BulkOperations.BulkMode.UNORDERED, Article.class); articles.forEach(article - { Document doc new Document() .append(title, article.getTitle()) .append(content, article.getContent()) .append(createTime, new Date()); bulkOps.insert(doc); }); bulkOps.execute(); }3.2 嵌套文档更新技巧更新数组中的特定元素需要使用位置运算符$public void updateSpecificComment(String articleId, String commentId, CommentUpdate update) { Update updateOps new Update() .set(comments.$.content, update.getContent()) .set(comments.$.updateTime, LocalDateTime.now()); Query query Query.query( Criteria.where(id).is(articleId) .and(comments.id).is(commentId) ); mongoTemplate.updateFirst(query, updateOps, Article.class); }3.3 原子计数器与数组操作保证并发安全的原子操作public void incrementViewCount(String articleId) { Query query new Query(Criteria.where(id).is(articleId)); Update update new Update().inc(viewCount, 1); mongoTemplate.updateFirst(query, update, Article.class); } public void addTag(String articleId, String newTag) { Query query new Query(Criteria.where(id).is(articleId)); Update update new Update().addToSet(tags, newTag); mongoTemplate.updateFirst(query, update, Article.class); }4. 分页查询与聚合操作4.1 高性能分页实现传统分页在数据量大时性能较差推荐使用基于游标的分页public PageArticle searchArticles(ArticleQuery query, Pageable pageable) { Criteria criteria buildCriteria(query); Query mongoQuery new Query(criteria) .with(pageable) .with(Sort.by(Sort.Direction.DESC, createTime)); long total mongoTemplate.count(mongoQuery, Article.class); ListArticle content mongoTemplate.find(mongoQuery, Article.class); return new PageImpl(content, pageable, total); } // 优化后的游标分页 public ListArticle findArticlesAfterCursor(String lastId, int limit) { Query query new Query() .addCriteria(Criteria.where(id).gt(lastId)) .limit(limit) .with(Sort.by(Sort.Direction.ASC, id)); return mongoTemplate.find(query, Article.class); }4.2 聚合管道实战实现复杂统计分析的聚合查询public ListAuthorStats getAuthorStatistics() { TypedAggregationArticle aggregation Aggregation.newAggregation( Article.class, Aggregation.match(Criteria.where(published).is(true)), Aggregation.group(author) .count().as(articleCount) .sum(viewCount).as(totalViews) .avg(viewCount).as(avgViews) .push(title).as(titles), Aggregation.project() .and(_id).as(author) .and(articleCount).as(articleCount) .and(totalViews).as(totalViews) .and(avgViews).as(avgViews) .and(titles).as(titles) .andExclude(_id), Aggregation.sort(Sort.Direction.DESC, totalViews) ); return mongoTemplate.aggregate(aggregation, AuthorStats.class) .getMappedResults(); }4.3 全文检索实现利用MongoDB的文本索引实现搜索功能// 创建全文索引 Document TextIndexed(weight 2) public class Article { TextIndexed(weight 1) private String title; TextIndexed private String content; } // 执行全文搜索 public ListArticle fullTextSearch(String keywords) { TextCriteria criteria TextCriteria.forDefaultLanguage() .matchingAny(keywords.split( )); Query query TextQuery.queryText(criteria) .sortByScore() .with(Sort.by(Sort.Direction.DESC, createTime)); return mongoTemplate.find(query, Article.class); }5. 事务管理与性能优化5.1 多文档事务实现MongoDB 4.0支持跨文档ACID事务Transactional public void transferComment(String sourceId, String targetId, String commentId) { // 从源文章移除评论 Query sourceQuery new Query(Criteria.where(id).is(sourceId) .and(comments.id).is(commentId)); Update removeUpdate new Update().pull(comments, new BasicDBObject(id, commentId)); mongoTemplate.updateFirst(sourceQuery, removeUpdate, Article.class); // 向目标文章添加评论 Comment comment getCommentById(sourceId, commentId); Query targetQuery new Query(Criteria.where(id).is(targetId)); Update addUpdate new Update().push(comments, comment); mongoTemplate.updateFirst(targetQuery, addUpdate, Article.class); }5.2 读写分离配置在application.yml中配置读写分离spring: data: mongodb: uri: mongodb://primary.example.com:27017,secondary1.example.com:27017,secondary2.example.com:27017/blogdb?replicaSetrs0readPreferencesecondaryPreferred5.3 性能监控与调优启用MongoDB性能监控Configuration EnableMongoAuditing public class MongoConfig { Bean public MongoTemplate mongoTemplate(MongoDatabaseFactory factory) { MongoTemplate template new MongoTemplate(factory); // 启用查询日志 template.setWriteConcern(WriteConcern.ACKNOWLEDGED); template.setReadPreference(ReadPreference.secondaryPreferred()); return template; } Bean public CommandLineRunner commandLineRunner(MongoTemplate template) { return args - { // 打印慢查询日志配置 template.executeCommand({ profile: 1, slowms: 100, sampleRate: 1.0 }); }; } }6. 安全与异常处理6.1 数据验证与加密使用Spring Data的验证注解Document public class Article { Id private String id; NotBlank Size(max 100) private String title; Field(content) Encrypted private String encryptedContent; }6.2 自定义异常处理全局异常处理器示例RestControllerAdvice public class MongoExceptionHandler { ExceptionHandler(DuplicateKeyException.class) public ResponseEntityErrorResponse handleDuplicateKey( DuplicateKeyException ex) { ErrorResponse error new ErrorResponse( DATA_CONFLICT, 唯一键冲突: ex.getMessage(), Instant.now()); return ResponseEntity.status(HttpStatus.CONFLICT).body(error); } ExceptionHandler(MongoQueryException.class) public ResponseEntityErrorResponse handleQueryException( MongoQueryException ex) { ErrorResponse error new ErrorResponse( QUERY_ERROR, 查询语法错误: ex.getMessage(), Instant.now()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); } }6.3 审计日志集成配置审计日志记录所有数据变更Configuration EnableMongoAuditing public class AuditConfig { Bean public AuditorAwareString auditorAware() { return () - Optional.ofNullable(SecurityContextHolder.getContext()) .map(SecurityContext::getAuthentication) .map(Authentication::getName) .or(() - Optional.of(system)); } } Document EntityListeners(AuditingEntityListener.class) public class Article { CreatedBy private String createdBy; LastModifiedBy private String lastModifiedBy; }7. 实战案例博客系统API设计7.1 RESTful API规范完整的博客API设计示例RestController RequestMapping(/api/v1/articles) RequiredArgsConstructor public class ArticleController { private final ArticleService service; GetMapping public ResponseEntityPageResponseArticle listArticles( RequestParam(defaultValue 0) int page, RequestParam(defaultValue 10) int size, RequestParam(required false) String tag) { Pageable pageable PageRequest.of(page, size, Sort.by(createTime).descending()); PageArticle result service.listArticles(tag, pageable); return ResponseEntity.ok(PageResponse.from(result)); } GetMapping(/{id}) public ResponseEntityArticle getArticle(PathVariable String id) { return ResponseEntity.ok(service.getArticle(id)); } PostMapping public ResponseEntityArticle createArticle( Valid RequestBody ArticleCreateRequest request) { Article created service.createArticle(request); URI location ServletUriComponentsBuilder.fromCurrentRequest() .path(/{id}) .buildAndExpand(created.getId()) .toUri(); return ResponseEntity.created(location).body(created); } PutMapping(/{id}) public ResponseEntityArticle updateArticle( PathVariable String id, Valid RequestBody ArticleUpdateRequest request) { return ResponseEntity.ok(service.updateArticle(id, request)); } DeleteMapping(/{id}) public ResponseEntityVoid deleteArticle(PathVariable String id) { service.deleteArticle(id); return ResponseEntity.noContent().build(); } GetMapping(/search) public ResponseEntityListArticle searchArticles( RequestParam String q, RequestParam(defaultValue false) boolean highlight) { return ResponseEntity.ok(service.searchArticles(q, highlight)); } }7.2 GraphQL API集成对于复杂查询场景可以集成GraphQLConfiguration public class GraphQLConfig { Bean public GraphQL graphQL(MongoTemplate template) { RuntimeWiring wiring RuntimeWiring.newRuntimeWiring() .type(Query, typeWiring - typeWiring .dataFetcher(article, env - { String id env.getArgument(id); return template.findById(id, Article.class); }) .dataFetcher(articles, env - { String tag env.getArgument(tag); Query query new Query(); if (tag ! null) { query.addCriteria(Criteria.where(tags).in(tag)); } return template.find(query, Article.class); })) .build(); SchemaParser parser new SchemaParser(); SchemaGenerator generator new SchemaGenerator(); File schemaFile new ClassPathResource(graphql/schema.graphqls).getFile(); TypeDefinitionRegistry registry parser.parse(schemaFile); return GraphQL.newGraphQL(generator.makeExecutableSchema(registry, wiring)) .build(); } }8. 部署与监控8.1 Docker化部署docker-compose.yml示例version: 3.8 services: mongodb: image: mongo:5.0 container_name: mongodb environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: example ports: - 27017:27017 volumes: - mongodb_data:/data/db healthcheck: test: echo db.runCommand(ping).ok | mongosh localhost:27017/test --quiet interval: 10s timeout: 5s retries: 5 app: build: . image: blog-api depends_on: mongodb: condition: service_healthy ports: - 8080:8080 environment: SPRING_DATA_MONGODB_URI: mongodb://root:examplemongodb:27017/blogdb?authSourceadmin volumes: mongodb_data:8.2 性能监控配置集成Micrometer监控MongoDB指标Configuration public class MetricsConfig { Bean public MeterRegistryCustomizerMeterRegistry metricsCommonTags() { return registry - registry.config().commonTags( application, blog-service, database, mongodb ); } Bean public MongoMetricsCommandListener mongoMetrics(MeterRegistry registry) { return new MongoMetricsCommandListener(registry); } }application.yml中启用监控management: endpoints: web: exposure: include: health,metrics,mongodb metrics: tags: application: ${spring.application.name} endpoint: health: show-details: always metrics: enabled: true9. 测试策略9.1 单元测试配置使用DataMongoTest进行Repository测试DataMongoTest ExtendWith(SpringExtension.class) class ArticleRepositoryTest { Autowired private ArticleRepository repository; Autowired private MongoTemplate template; Test void shouldFindByAuthor() { Article article new Article(); article.setAuthor(test); template.save(article); ListArticle result repository.findByAuthor(test); assertThat(result).hasSize(1); } }9.2 集成测试示例使用Testcontainers进行真实MongoDB测试SpringBootTest Testcontainers class ArticleServiceIT { Container static MongoDBContainer mongoDB new MongoDBContainer(mongo:5.0); DynamicPropertySource static void setProperties(DynamicPropertyRegistry registry) { registry.add(spring.data.mongodb.uri, mongoDB::getReplicaSetUrl); } Autowired private ArticleService service; Test void shouldCreateAndRetrieveArticle() { ArticleCreateRequest request new ArticleCreateRequest(); request.setTitle(Test); request.setContent(Content); Article created service.createArticle(request); Article found service.getArticle(created.getId()); assertThat(found.getTitle()).isEqualTo(Test); } }9.3 性能测试建议使用JMeter测试MongoDB性能创建测试计划模拟并发CRUD操作监控MongoDB服务器指标CPU、内存、磁盘I/O重点关注查询响应时间写入吞吐量索引命中率调整以下参数进行优化连接池大小批处理大小读写关注级别10. 进阶主题与扩展10.1 Change Stream实时监听实现数据变更通知Service RequiredArgsConstructor public class ArticleChangeListener { private final MongoTemplate template; PostConstruct public void watchChanges() { Executors.newSingleThreadExecutor().submit(() - { ChangeStreamOptions options ChangeStreamOptions.builder() .returnFullDocumentOnUpdate() .filter(Aggregation.match( Criteria.where(operationType).in(insert, update) )) .build(); template.changeStream(articles, options, Article.class) .forEach(event - { System.out.println(Change detected: event); // 发送事件到消息队列等 }); }); } }10.2 多租户实现基于数据库级别的多租户隔离public class TenantAwareMongoTemplate extends MongoTemplate { private final ThreadLocalString tenantId new ThreadLocal(); public TenantAwareMongoTemplate(MongoDatabaseFactory factory) { super(factory); } public void setTenantId(String tenantId) { this.tenantId.set(tenantId); } Override protected T MongoCollectionDocument getAndCreateCollection( ClassT entityClass) { String collectionName getCollectionName(entityClass); String tenantDb tenantId.get() _db; return getDb(tenantDb).getCollection(collectionName); } }10.3 数据迁移策略使用MongoDB工具进行数据迁移# 导出数据 mongodump --urimongodb://source-host:27017 --dbblogdb --out./backup # 恢复数据 mongorestore --urimongodb://target-host:27017 --dbblogdb ./backup/blogdb # 增量同步 mongodump --urimongodb://source-host:27017 --dbblogdb --collectionarticles --query{createTime: {$gt: {$date: 2023-01-01T00:00:00Z}}} --out./incremental