【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,构建高性能、可扩展的应用程序,并根据业务需求进行定制化开发。
相关推荐
架构师沉默7 分钟前
Java 开发者别忽略 return!这 11 种写法你写对了吗?
java·后端·架构
EndingCoder9 分钟前
React 19 与 Next.js:利用最新 React 功能
前端·javascript·后端·react.js·前端框架·全栈·next.js
RainbowJie114 分钟前
Gemini CLI 与 MCP 服务器:释放本地工具的强大潜力
java·服务器·spring boot·后端·python·单元测试·maven
ITMan彪叔24 分钟前
Nodejs打包 Webpack 中 __dirname 的正确配置与行为解析
javascript·后端
用户895356032822041 分钟前
告别重复,用Go泛型精简Gin代码
后端·gin
运维开发故事1 小时前
AIOps系列 | 开发一个 K8s Chat 命令行工具
后端
惜鸟1 小时前
大模型工具/函数调用原理和实践
后端
神毓逍遥kang1 小时前
nestjs drizzle-orm 构建rbac权限系统
前端·后端
用户298698530141 小时前
如何使用 Spire.Doc 将 Word 转换为 TIFF?
后端