【springboot 技术代码】集成mongodb 详细步骤

1. MongoDB 简介与 SpringBoot 集成概述

MongoDB 是一个基于分布式文件存储的开源 NoSQL 数据库系统,采用文档存储格式(BSON,类似 JSON)。与传统关系型数据库相比,MongoDB 具有以下特点:

  1. 无模式设计:集合中的文档可以有不同的结构
  2. 水平扩展:通过分片实现数据分布式存储
  3. 高性能:内存映射文件技术提高 I/O 效率
  4. 丰富的查询语言:支持丰富的查询表达式和索引类型
  5. 聚合框架:强大的数据分析能力

1.1 SpringBoot 集成 MongoDB 的优势

  1. 自动配置:SpringBoot 自动配置 MongoDB 客户端连接
  2. Repository 抽象:类似 JPA 的操作方式,降低学习曲线
  3. 对象映射:自动将 Java 对象与 MongoDB 文档相互转换
  4. 事务支持:支持多文档 ACID 事务(MongoDB 4.0+)
  5. 与 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 性能优化策略

  1. 索引优化:

    • 为常用查询字段创建适当索引
    • 使用复合索引优化多字段查询
    • 定期分析查询性能并调整索引
  2. 查询优化:

    • 使用投影只返回必要字段
    • 限制返回文档数量
    • 避免使用 $where 和 JavaScript 表达式
    • 使用 covered queries(查询完全由索引满足)
  3. 批量操作:

    • 使用 bulkWrite 进行批量插入/更新
    • 批量操作时适当设置 ordered 参数
  4. 读写分离:

    • 配置从节点读取
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
  1. 分片集群:

    • 对于大数据集,配置分片集群
    • 选择合适的分片键

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 服务器

排查步骤:

  1. 检查连接字符串是否正确
  2. 验证网络连通性
  3. 检查认证信息
  4. 查看 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 性能问题排查

问题现象:查询响应慢

排查步骤:

  1. 使用 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
  1. 检查索引使用情况
  2. 分析 MongoDB 服务器资源使用情况
  3. 优化查询条件和索引

8.3 数据一致性问题

解决方案:

  1. 使用多文档事务(MongoDB 4.0+)
  2. 实现最终一致性模式
  3. 使用变更流监听数据变化
  4. 定期数据校验和修复

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
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 的完整方案,涵盖以下内容:

  1. 环境准备:版本兼容性、依赖配置、连接设置
  2. 数据建模:实体映射、索引管理、关系处理
  3. 数据操作:基础CRUD、复杂查询、聚合框架
  4. 高级特性:事务管理、变更流、GridFS
  5. 性能优化:索引策略、查询优化、读写分离
  6. 实战案例:电商平台商品管理系统实现
  7. 测试策略:单元测试、集成测试方法
  8. 问题排查:连接问题、性能问题解决方案
  9. 未来扩展:分片集群、时间序列集合等
    通过本指南,您应该能够在 SpringBoot 项目中高效地集成 MongoDB,构建高性能、可扩展的应用程序,并根据业务需求进行定制化开发。
相关推荐
不能放弃治疗3 分钟前
MCP
后端
万亿少女的梦16822 分钟前
基于Spring Boot的游戏交易管理系统设计与实现
java·spring boot·mysql·系统设计·交易管理
江湖十年1 小时前
Go 语言中 YAML to JSON 踩坑笔记
后端·go·json
你驴我2 小时前
WhatsApp 消息撤回与编辑的幂等性设计实践
java·服务器·前端·后端·python
一缕清烟在人间2 小时前
HarmonyOS开发实战:小分享-TextEditPage文字编辑器——Header+TextArea+工具栏
后端·华为·harmonyos·鸿蒙
人间凡尔赛2 小时前
K8s 十年之变:从 Cloud Native 到 AI Native,2026 年后端架构的范式革命
后端·云原生·架构
程序员cxuan2 小时前
Opus 5 深夜炸场,价格还挺香。。。
人工智能·后端·程序员
北冥you鱼2 小时前
Go 语言新手扫盲:指针 * 和 & 使用场景详解
开发语言·后端·golang
青山木2 小时前
Hot 100 ---腐烂的橘子
java·数据结构·后端·算法·leetcode·广度优先
卷无止境3 小时前
Python的collections模块:那些被低估的"瑞士军刀"
后端·python