Spring Boot 3 与 MongoDB:构建高性能 NoSQL 应用的指南

Spring Boot 3 与 MongoDB:构建高性能 NoSQL 应用的终极指南

1. 引言:为什么选择 Spring Boot 3 和 MongoDB?

在现代应用开发中,快速构建高效、可扩展的系统是开发者的核心目标。Spring Boot 作为 Java 生态中最流行的微服务框架之一,与 MongoDB 这款强大的 NoSQL 数据库结合,能够为开发者提供一个灵活、高性能的解决方案。

  • 快速开发:Spring Boot 提供了开箱即用的功能(例如自动配置、嵌入式服务器),而 Spring Data MongoDB 进一步简化了 MongoDB 的操作。开发者只需定义实体类和 Repository 接口,就能实现对 MongoDB 的 CRUD 操作,大幅减少样板代码。

  • 处理非结构化数据:在许多现代应用中,数据结构可能随着业务需求不断变化(例如电商平台的商品属性、社交平台的用户动态)。MongoDB 的文档模型允许动态调整 Schema,而 Spring Boot 提供了强大的对象映射能力(通过 @Document@Field 注解),让开发者可以轻松应对这种灵活性。

  • 构建高性能微服务:Spring Boot 3 的微服务支持(结合 Spring Cloud)和 MongoDB 的分布式能力相得益彰。开发者可以构建松耦合的微服务架构,每个服务使用独立的 MongoDB 数据库实例,通过分片和副本集实现高可用性和负载均衡。

  • 支持多样化的业务场景:无论是实时分析(通过 MongoDB 聚合管道)、文件存储(通过 GridFS),还是高并发读写(通过索引和连接池优化),Spring Boot 和 MongoDB 的组合都能提供强大的支持。

通过集成 Spring Boot 3 和 MongoDB,开发者可以在快速开发和高效运行之间找到平衡,构建出既灵活又高性能的现代应用。接下来,我们将从环境搭建开始,逐步展示如何实现这一集成,并挖掘两者的潜力。

2. 环境搭建:从零开始集成 MongoDB

2.1 MongoDB 安装与测试

这一步不在本文详细描述,可以去网上搜索一下相关资料

  • 本地安装 MongoDB 或使用 Docker 部署
  • 使用 MongoDB Compass 或命令行测试连接

2.2 Spring Boot 项目初始化

本文使用的是SpringBoot3.4.4版本。

创建项目的相关配置信息如下:

image-20250404233304189

2.3 依赖配置

  • pom.xml文件详细信息如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.kedaya</groupId>
    <artifactId>springboot3-mongodb</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot3-mongodb</name>
    <description>springboot3-mongodb</description>
    <properties>
        <java.version>17</java.version>
        <mapstruct.version>1.5.5.Final</mapstruct.version>
    </properties>
    <dependencies>
      	<!--MongoDB-start依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-configuration-processor</artifactId>
                            <version>${project.parent.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

  • application.properties相关配置信息如下
spring.application.name=springboot3-mongodb
server.port=8080

# MongoDB配置信息 - 开发环境
spring.data.mongodb.host=xxx.xxx.xxx.xx
spring.data.mongodb.port=27017
spring.data.mongodb.database=数据库名称
spring.data.mongodb.username=username
spring.data.mongodb.password=password

# 通用MongoDB配置
spring.data.mongodb.authentication-database=admin
spring.data.mongodb.auto-index-creation=true

# Jackson配置
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
spring.jackson.serialization.write-dates-as-timestamps=false
spring.jackson.default-property-inclusion=NON_NULL

# 日志配置
logging.level.root=INFO
logging.level.com.kedaya=DEBUG
logging.level.org.springframework.data.mongodb=DEBUG

3. 核心功能:使用 Spring Data MongoDB 实现 CRUD

3.1 定义数据模型

  • 创建实体类(@Document 注解)
  • 字段映射(@Id@Field 等)

下面我们创建一个用户实体数据模型

package com.kedaya.springboot3mongodb.model.entity;

import com.kedaya.springboot3mongodb.constant.MongoConstant;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

import java.time.LocalDateTime;

/**
 * 用户实体类
 */
@Data
@Document(collection = MongoConstant.Collection.USER)
public class UserEntity {

    /**
     * 用户主键
     */
    @Id
    private String id;

    /**
     * 用户名
     */
    @Indexed(unique = true)
    private String username;

    /**
     * 用户密码
     */
    private String password;

    /**
     * 用户邮箱
     */
    @Indexed
    private String email;

    /**
     * 用户昵称
     */
    private String nickname;

    /**
     * 用户年龄
     */
    private Integer age;

    /**
     * 用户头像
     */
    private String avatar;

    /**
     * 创建时间
     */
    @Field(MongoConstant.Field.CREATE_TIME)
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    @Field(MongoConstant.Field.UPDATE_TIME)
    private LocalDateTime updateTime;

    /**
     * 是否删除
     */
    @Field(MongoConstant.Field.DELETED)
    private Boolean deleted;
} 

3.2 实现 Repository

  • 使用 MongoRepository 接口自动生成 CRUD 方法
  • 自定义查询方法(例如 findByUsername

接下来我们来创建一个UserRepository

package com.kedaya.springboot3mongodb.repository;

import com.kedaya.springboot3mongodb.model.entity.UserEntity;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

/**
 * 用户存储库接口
 */
@Repository
public interface UserRepository extends MongoRepository<UserEntity, String> {

    /**
     * 根据用户名查找用户
     */
    Optional<UserEntity> findByUsername(String username);

    /**
     * 根据邮箱查找用户
     */
    Optional<UserEntity> findByEmail(String email);

    /**
     * 根据年龄范围查找用户
     */
    List<UserEntity> findByAgeBetween(Integer minAge, Integer maxAge);

    /**
     * 查找未删除的用户
     */
    @Query("{'deleted': false}")
    List<UserEntity> findAllNotDeleted();
} 

3.3 控制器层实现

  • 创建 REST API(@RestController
  • 实现增删改查操作(POSTGETPUTDELETE

接下来我们创建UserController控制器

package com.kedaya.springboot3mongodb.controller;

import com.kedaya.springboot3mongodb.model.dto.UserDTO;
import com.kedaya.springboot3mongodb.model.vo.UserVO;
import com.kedaya.springboot3mongodb.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * 用户控制器
 */
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    /**
     * 创建用户
     */
    @PostMapping
    public ResponseEntity<UserVO> createUser(@RequestBody UserDTO userDTO) {
        UserVO createdUser = userService.createUser(userDTO);
        return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
    }

    /**
     * 更新用户
     */
    @PutMapping("/{id}")
    public ResponseEntity<UserVO> updateUser(@PathVariable String id, @RequestBody UserDTO userDTO) {
        UserVO updatedUser = userService.updateUser(id, userDTO);
        return ResponseEntity.ok(updatedUser);
    }

    /**
     * 删除用户
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable String id) {
        userService.deleteUser(id);
        return ResponseEntity.noContent().build();
    }

    /**
     * 获取单个用户
     */
    @GetMapping("/{id}")
    public ResponseEntity<UserVO> getUserById(@PathVariable String id) {
        UserVO user = userService.getUserById(id);
        return ResponseEntity.ok(user);
    }

    /**
     * 根据用户名查询用户
     */
    @GetMapping("/username/{username}")
    public ResponseEntity<UserVO> getUserByUsername(@PathVariable String username) {
        UserVO user = userService.getUserByUsername(username);
        return ResponseEntity.ok(user);
    }

    /**
     * 获取所有用户
     */
    @GetMapping
    public ResponseEntity<List<UserVO>> getAllUsers() {
        List<UserVO> users = userService.getAllUsers();
        return ResponseEntity.ok(users);
    }

    /**
     * 根据年龄范围查询用户
     */
    @GetMapping("/age")
    public ResponseEntity<List<UserVO>> getUsersByAgeRange(
            @RequestParam Integer minAge,
            @RequestParam Integer maxAge) {
        List<UserVO> users = userService.getUsersByAgeRange(minAge, maxAge);
        return ResponseEntity.ok(users);
    }
} 

3.4 测试 CRUD 功能

  • 使用 Postman 或 cURL 测试 API
  • 验证 MongoDB 数据库中的数据变化

4. 高级特性:挖掘 MongoDB 的潜力

本章节详细介绍MongoDB的高级特性在Spring Boot 3项目中的应用,通过实际示例帮助开发者充分利用MongoDB的强大功能。

4.1 索引优化

索引是提高查询性能的关键。在MongoDB中,合理使用索引可以显著提升查询速度。

4.1.1 索引类型及定义方式
package com.kedaya.springboot3mongodb.model.entity;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.CompoundIndex;
import org.springframework.data.mongodb.core.index.CompoundIndexes;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

import java.math.BigDecimal;
import java.time.LocalDateTime;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;

@Data
@Document(collection = "products")
@CompoundIndexes({
    @CompoundIndex(name = "category_price", def = "{'category': 1, 'price': -1}")
})
public class ProductEntity {
    
    @Id
    private String id;
    
    @NotBlank(message = "商品名称不能为空")
    @Indexed(unique = true)
    private String name;
    
    @NotNull(message = "价格不能为空")
    @Positive(message = "价格必须大于0")
    private BigDecimal price;
    
    @Indexed
    private String category;
    
    private String description;
    
    @Indexed(background = true)
    private LocalDateTime createTime;
} 
4.1.2 索引性能测试

可以使用以下方法验证索引效果:

// 比较有索引和无索引的查询效率
db.products.find({category: "手机"}).explain("executionStats")
4.1.3 索引使用最佳实践
  • 为频繁查询的字段创建索引
  • 避免过多索引,会影响写入性能
  • 对于复合查询,考虑创建复合索引
  • 使用background=true创建后台索引避免索引创建阻塞数据库操作

4.2 聚合管道

聚合管道提供了强大的数据处理能力,可以进行复杂的数据分析。

4.2.1 聚合操作基本组件
// ProductAggregationService.java
public List<Map> getAveragePriceByCategory() {
    // 1. 定义分组操作
    GroupOperation groupByCategory = Aggregation.group("category")
            .avg("price").as("averagePrice")
            .count().as("productCount");
    
    // 2. 定义排序操作
    SortOperation sortByAvgPrice = Aggregation.sort(Sort.Direction.DESC, "averagePrice");
    
    // 3. 指定要包含的字段
    ProjectionOperation projectFields = Aggregation.project()
            .and("_id").as("category")
            .and("averagePrice").as("averagePrice")
            .and("productCount").as("productCount");
    
    // 4. 创建聚合管道
    Aggregation aggregation = Aggregation.newAggregation(
            groupByCategory,
            sortByAvgPrice,
            projectFields
    );
    
    // 5. 执行聚合操作
    AggregationResults<Map> results = mongoTemplate.aggregate(
            aggregation, "products", Map.class);
    
    return results.getMappedResults();
}
4.2.2 常用聚合操作
  • match: 筛选文档
  • group: 分组聚合
  • project: 投影字段
  • sort: 排序
  • limit: 限制结果数量
  • skip: 跳过结果
4.2.3 聚合管道示例
// 按价格范围分组统计产品数量
public List<Map> countProductsByPriceRange() {
    ProjectionOperation projectFields = Aggregation.project("price")
            .and(ConditionalOperators.when(Criteria.where("price").lt(new BigDecimal(100)))
                    .then("便宜")
                    .otherwise(ConditionalOperators.when(Criteria.where("price").lt(new BigDecimal(500)))
                            .then("适中")
                            .otherwise("昂贵")))
            .as("priceRange");
    
    GroupOperation groupByPriceRange = Aggregation.group("priceRange")
            .count().as("count");
    
    Aggregation aggregation = Aggregation.newAggregation(
            projectFields,
            groupByPriceRange
    );
    
    AggregationResults<Map> results = mongoTemplate.aggregate(
            aggregation, "products", Map.class);
    
    return results.getMappedResults();
}

4.3 事务支持

从MongoDB 4.0开始,MongoDB支持多文档事务,Spring Boot 3提供了与之集成的事务支持。

4.3.1 事务配置
@Configuration
public class MongoTransactionConfig {
    @Bean
    public MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
        return new MongoTransactionManager(dbFactory);
    }
}
4.3.2 注解式事务
@Service
public class TransactionalProductService {
    @Autowired
    private ProductRepository productRepository;
    
    @Transactional
    public List<Product> batchCreateProducts(List<Product> products) {
        // 在这个方法中的所有数据库操作都将在一个事务中
        return productRepository.saveAll(products);
    }
    
    @Transactional
    public Product replaceProduct(String oldProductId, Product newProduct) {
        // 删除旧产品
        productRepository.deleteById(oldProductId);
        
        // 创建新产品
        return productRepository.save(newProduct);
        
        // 如果任一操作失败,两个操作都会回滚
    }
}
4.3.3 编程式事务
public void updateProductPrices(List<String> productIds, BigDecimal increaseAmount) {
    TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
    
    transactionTemplate.execute(status -> {
        try {
            for (String id : productIds) {
                Product product = productRepository.findById(id)
                        .orElseThrow(() -> new RuntimeException("产品未找到:" + id));
                
                BigDecimal newPrice = product.getPrice().add(increaseAmount);
                product.setPrice(newPrice);
                
                productRepository.save(product);
            }
            return null;
        } catch (Exception e) {
            status.setRollbackOnly();
            throw e;
        }
    });
}
4.3.4 事务注意事项
  1. 需要副本集:MongoDB事务必须在副本集环境中使用
  2. 性能影响:事务会降低性能,不要在不必要的场景使用
  3. 锁定:事务会锁定相关文档,可能导致阻塞
  4. 超时设置:设置合理的事务超时时间
  5. 重试机制:考虑为事务添加重试机制

4.4 GridFS 存储大文件

GridFS是MongoDB存储大文件(>16MB)的方案,将文件分块存储,支持高效读写。

4.4.1 GridFS服务实现
@Service
public class GridFsService {
    @Autowired
    private GridFsTemplate gridFsTemplate;
    
    @Autowired
    private GridFsOperations gridFsOperations;
    
    /**
     * 上传文件到GridFS
     */
    public String uploadFile(MultipartFile file, String filename, Map<String, String> metadata) throws IOException {
        if (metadata == null) {
            metadata = new HashMap<>();
        }
        
        metadata.put("contentType", file.getContentType());
        metadata.put("size", String.valueOf(file.getSize()));
        
        InputStream inputStream = file.getInputStream();
        ObjectId objectId = gridFsTemplate.store(
                inputStream, 
                filename, 
                file.getContentType(), 
                metadata);
        
        inputStream.close();
        return objectId.toString();
    }
    
    /**
     * 根据ID获取文件
     */
    public GridFsResource getFileById(String fileId) {
        GridFSFile gridFSFile = gridFsTemplate.findOne(
                new Query(Criteria.where("_id").is(new ObjectId(fileId))));
        
        if (gridFSFile == null) {
            return null;
        }
        
        return gridFsOperations.getResource(gridFSFile);
    }
}
4.4.2 文件上传下载API
@RestController
@RequestMapping("/files")
public class FileController {
    @Autowired
    private GridFsService gridFsService;

    @PostMapping("/upload")
    public ResponseEntity<Map<String, Object>> uploadFile(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "filename", required = false) String filename) {
        
        try {
            if (filename == null || filename.isEmpty()) {
                filename = file.getOriginalFilename();
            }
            
            Map<String, String> metadata = new HashMap<>();
            metadata.put("uploadTime", String.valueOf(System.currentTimeMillis()));
            
            String fileId = gridFsService.uploadFile(file, filename, metadata);
            
            Map<String, Object> response = new HashMap<>();
            response.put("success", true);
            response.put("fileId", fileId);
            response.put("filename", filename);
            
            return ResponseEntity.ok(response);
        } catch (IOException e) {
            Map<String, Object> response = new HashMap<>();
            response.put("success", false);
            response.put("error", e.getMessage());
            
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
        }
    }
    
    @GetMapping("/download/{fileId}")
    public ResponseEntity<InputStreamResource> downloadFile(@PathVariable String fileId) {
        try {
            GridFsResource resource = gridFsService.getFileById(fileId);
            
            if (resource == null || !resource.exists()) {
                return ResponseEntity.notFound().build();
            }
            
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.parseMediaType(resource.getContentType()));
            headers.setContentDispositionFormData("attachment", resource.getFilename());
            
            return ResponseEntity.ok()
                    .headers(headers)
                    .body(new InputStreamResource(resource.getInputStream()));
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}
4.4.3 GridFS 使用场景与建议
  • 适用场景:存储大于16MB的文件,如图片、文档、视频等
  • 元数据管理:利用元数据实现文件分类和搜索
  • 流式处理:支持流式读写,不必一次加载整个文件
  • 性能优化:对于频繁访问的小文件,考虑直接存储为Binary数据
  • 备份策略:为GridFS集合制定单独的备份策略

5. 性能优化:让应用更快更稳

高级特性的使用需要注意性能影响,建议针对实际应用场景进行性能测试和调优:

  1. 使用MongoDB Compass观察查询执行计划
  2. 监控查询性能和资源使用情况
  3. 设置适当的写入关注级别和读取偏好
  4. 针对高频查询优化索引结构
  5. 大量数据场景下考虑分片

5.1 连接池配置

  • 配置 MongoDB 客户端的连接池
package com.kedaya.springboot3mongodb.config;

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mongo.MongoProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.MongoTransactionManager;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.mapping.event.ValidatingMongoEventListener;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;

import static com.mongodb.MongoCredential.createCredential;
import static java.util.Collections.singletonList;

/**
 * MongoDB配置类
 */
@Configuration
public class MongoConfig {

    @Autowired
    private MongoProperties mongoProperties;
  
    /**
     * 创建MongoDB客户端,配置连接池信息
     * @return
     * @throws Exception
     */
    @Bean
    public MongoClient mongoClient() throws Exception {
        String mongoUri = buildMongoUri();
        MongoClientSettings settings = MongoClientSettings.builder()
                .applyConnectionString(new ConnectionString(mongoUri))
                .applyToClusterSettings(setting -> {
                    setting.hosts(singletonList(new ServerAddress(mongoProperties.getHost(), mongoProperties.getPort())));
                })
                // 配置连接池信息
                .applyToConnectionPoolSettings(builder -> builder
                        .maxSize(100)           // 最大连接数
                        .minSize(10)            // 最小连接数
                        .maxWaitTime(120000, java.util.concurrent.TimeUnit.MILLISECONDS) // 最大等待时间
                        .maxConnectionIdleTime(300000, java.util.concurrent.TimeUnit.MILLISECONDS) // 最大空闲时间
                        .maxConnectionLifeTime(0, java.util.concurrent.TimeUnit.MILLISECONDS) // 最大存活时间
                )
                .build();
        return MongoClients.create(settings);
    }

    @Bean
    public MongoTemplate mongoTemplate(MongoClient mongoClient) {
        return new MongoTemplate(mongoClient, mongoProperties.getDatabase());
    }

    private String buildMongoUri() throws Exception {
        // 如果配置了 uri,直接使用
        if (mongoProperties.getUri() != null) {
            return mongoProperties.getUri();
        }

        // 否则手动构造
        String host = mongoProperties.getHost() != null ? mongoProperties.getHost() : "localhost";
        int port = mongoProperties.getPort() != 0 ? mongoProperties.getPort() : 27017;
        String database = mongoProperties.getDatabase();
        String username = mongoProperties.getUsername();
        String password = String.valueOf(mongoProperties.getPassword());
        String authDatabase = mongoProperties.getAuthenticationDatabase() != null
                ? mongoProperties.getAuthenticationDatabase()
                : database;

        if (username != null && !username.isEmpty() && password != null && !password.isEmpty()) {
            String encodedUsername = URLEncoder.encode(username, StandardCharsets.UTF_8.toString());
            String encodedPassword = URLEncoder.encode(password, StandardCharsets.UTF_8.toString());
            return String.format("mongodb://%s:%s@%s:%d/%s?authSource=%s",
                    encodedUsername, encodedPassword, host, port, database, authDatabase);
        } else {
            return String.format("mongodb://%s:%d/%s", host, port, database);
        }
    }
} 
  • 分析连接池对性能的影响

5.2 查询优化

  • 避免全表扫描,使用覆盖索引
  • 使用 explain() 分析查询性能

5.3 缓存集成

  • 结合 Spring Cache(@Cacheable)减少数据库查询
  • 示例:使用 Redis 作为缓存层

6. 最佳实践与注意事项

  • 数据模型设计:避免嵌套过深,合理使用引用
  • 异常处理:处理 MongoDB 连接失败、超时等问题
  • 日志管理:使用 SLF4J 记录 MongoDB 操作日志
  • 安全性:配置 MongoDB 认证和加密传输
  • 部署建议:生产环境中 MongoDB 的副本集和分片

7. 参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值