SpringBoot + MongoDB 配置详解

95 阅读5分钟

SpringBoot + MongoDB 配置详解

本文将详细介绍SpringBoot与MongoDB的整合配置,包括依赖引入、连接配置、连接池设置、实体类定义、Repository使用和高级功能等内容,帮助开发者快速实现MongoDB数据库操作。

一、依赖配置

首先在pom.xml中添加Spring Data MongoDB的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- 可选:添加Lombok简化实体类开发 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

二、基本连接配置 (application.yml)

application.yml中配置MongoDB连接信息:

1. 基本连接(无认证)

spring:
  data:
    mongodb:
      # 基本URI格式
      uri: mongodb://localhost:27017/yourdatabase

2. 带认证的连接

spring:
  data:
    mongodb:
      # 普通用户认证
      uri: mongodb://用户名:密码@127.0.0.1:27017/yourdatabase
      # 超级管理员认证(需要指定authSource)
      # uri: mongodb://用户名:密码@127.0.0.1:27017/yourdatabase?authSource=admin&authMechanism=SCRAM-SHA-1

3. 连接池配置

spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/yourdatabase
      # 连接池配置
      connection-pool:
        max-size: 50          # 最大连接数
        min-size: 10          # 最小连接数
        max-wait-queue-size: 500  # 最大等待队列大小
        max-wait-time: 2000ms     # 最大等待时间
        max-idle-time: 60000ms    # 最大空闲时间
        max-life-time: 1800000ms  # 连接最大生命周期

4. 副本集配置

spring:
  data:
    mongodb:
      # 副本集连接格式
      uri: mongodb://user:password@host1:27017,host2:27017,host3:27017/yourdatabase?replicaSet=rs0

三、自定义MongoDB配置类

对于更复杂的配置需求,可以创建自定义配置类:

1. 配置属性类

import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import java.util.List;

@Component
@ConfigurationProperties(prefix = "spring.data.mongodb.custom")
@Validated
public class MongoSettingsProperties {
    @NotBlank
    private String database;
    @NotEmpty
    private List<String> hosts;
    @NotEmpty
    private List<Integer> ports;
    private String replicaSet;
    private String username;
    private String password;
    private String authenticationDatabase;
    private Integer minConnectionsPerHost = 10;
    private Integer connectionsPerHost = 20;
    
    // getter和setter方法
    // ...
}

2. 配置类实现

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.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class MongoConfig {

    @Autowired
    private MongoSettingsProperties mongoSettingsProperties;

    @Bean
    public MongoClient mongoClient() {
        // 创建服务器地址列表
        List<ServerAddress> serverAddresses = new ArrayList<>();
        for (int i = 0; i < mongoSettingsProperties.getHosts().size(); i++) {
            serverAddresses.add(new ServerAddress(
                    mongoSettingsProperties.getHosts().get(i),
                    mongoSettingsProperties.getPorts().get(i)
            ));
        }

        // 创建认证信息
        MongoCredential credential = null;
        if (mongoSettingsProperties.getUsername() != null && !mongoSettingsProperties.getUsername().isEmpty()) {
            credential = MongoCredential.createCredential(
                    mongoSettingsProperties.getUsername(),
                    mongoSettingsProperties.getAuthenticationDatabase() != null ? 
                            mongoSettingsProperties.getAuthenticationDatabase() : 
                            mongoSettingsProperties.getDatabase(),
                    mongoSettingsProperties.getPassword().toCharArray()
            );
        }

        // 构建MongoClientSettings
        MongoClientSettings.Builder builder = MongoClientSettings.builder()
                .applyToConnectionPoolSettings(builder1 -> {
                    builder1.maxSize(mongoSettingsProperties.getConnectionsPerHost());
                    builder1.minSize(mongoSettingsProperties.getMinConnectionsPerHost());
                });

        if (credential != null) {
            builder.credential(credential);
        }

        if (mongoSettingsProperties.getReplicaSet() != null && !mongoSettingsProperties.getReplicaSet().isEmpty()) {
            builder.applyToClusterSettings(builder1 -> 
                    builder1.requiredReplicaSetName(mongoSettingsProperties.getReplicaSet())
            );
        }

        builder.applyToClusterSettings(builder1 -> builder1.hosts(serverAddresses));

        return MongoClients.create(builder.build());
    }

    @Bean
    public MongoDatabaseFactory mongoDatabaseFactory(MongoClient mongoClient) {
        return new SimpleMongoClientDatabaseFactory(
                mongoClient, mongoSettingsProperties.getDatabase());
    }

    @Bean
    public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDatabaseFactory) {
        return new MongoTemplate(mongoDatabaseFactory);
    }
}

3. 自定义配置对应的application.yml

spring:
  data:
    mongodb:
      custom:
        hosts:
          - 127.0.0.1
        ports:
          - 27017
        # 副本集配置
        # replica-set: rs0
        username: admin
        password: admin123
        database: testdb
        authentication-database: admin
        connections-per-host: 20
        min-connections-per-host: 10

四、实体类定义

使用Spring Data MongoDB注解定义实体类:

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

import java.util.Date;

@Data
@Document(collection = "users")  // 指定集合名称
public class User {
    @Id  // 主键
    private String id;
    
    @Field("username")  // 字段映射
    private String username;
    
    @Field("email")
    private String email;
    
    @Field("created_at")
    private Date createdAt;
    
    @Field("age")
    private Integer age;
}

五、Repository接口

Spring Data MongoDB支持Repository模式,简化数据访问层开发:

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<User, String> {
    
    // 基于方法名自动生成查询
    Optional<User> findByUsername(String username);
    
    List<User> findByAgeGreaterThan(int age);
    
    // 自定义查询
    @Query("{ 'email' : ?0 }")
    Optional<User> findByEmail(String email);
    
    // 分页查询
    Page<User> findByAgeBetween(int minAge, int maxAge, Pageable pageable);
}

六、使用MongoTemplate操作数据库

对于更复杂的查询需求,可以使用MongoTemplate:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    
    @Autowired
    private MongoTemplate mongoTemplate;
    
    @Autowired
    private UserRepository userRepository;
    
    // 使用MongoTemplate查询
    public List<User> findUsersByAge(int age) {
        Query query = new Query(Criteria.where("age").is(age));
        return mongoTemplate.find(query, User.class);
    }
    
    // 保存用户
    public User saveUser(User user) {
        return mongoTemplate.save(user);
    }
    
    // 复杂查询
    public List<User> findUsersByCondition(String keyword, int minAge) {
        Criteria criteria = new Criteria();
        criteria.orOperator(
                Criteria.where("username").regex(keyword),
                Criteria.where("email").regex(keyword)
        );
        criteria.and("age").gte(minAge);
        
        Query query = new Query(criteria);
        return mongoTemplate.find(query, User.class);
    }
    
    // 删除用户
    public void deleteUser(String id) {
        mongoTemplate.remove(Query.query(Criteria.where("_id").is(id)), User.class);
    }
}

七、控制器实现

创建RESTful API控制器:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private UserRepository userRepository;
    
    // 获取所有用户
    @GetMapping
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }
    
    // 分页查询
    @GetMapping("/page")
    public Page<User> getUsersByPage(@RequestParam int page, @RequestParam int size) {
        return userRepository.findAll(PageRequest.of(page, size));
    }
    
    // 根据ID获取用户
    @GetMapping("/{id}")
    public Optional<User> getUserById(@PathVariable String id) {
        return userRepository.findById(id);
    }
    
    // 创建用户
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.saveUser(user);
    }
    
    // 更新用户
    @PutMapping("/{id}")
    public User updateUser(@PathVariable String id, @RequestBody User user) {
        user.setId(id);
        return userService.saveUser(user);
    }
    
    // 删除用户
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable String id) {
        userService.deleteUser(id);
    }
    
    // 按年龄查询
    @GetMapping("/by-age")
    public List<User> getUsersByAge(@RequestParam int age) {
        return userService.findUsersByAge(age);
    }
}

八、副本集高级配置

对于MongoDB副本集配置,需要注意以下几点:

  1. 确保MongoDB实例已配置为副本集
  2. 在连接URI中指定副本集名称
  3. 配置读取偏好,实现读写分离
@Configuration
public class MongoReplicaSetConfig {
    
    @Bean
    public MongoClient mongoClient() {
        MongoClientSettings settings = MongoClientSettings.builder()
                .applyToClusterSettings(builder -> 
                        builder.hosts(Arrays.asList(
                                new ServerAddress("host1", 27017),
                                new ServerAddress("host2", 27017),
                                new ServerAddress("host3", 27017)
                        ))
                        .requiredReplicaSetName("rs0")
                )
                .applyToConnectionPoolSettings(builder -> {
                    builder.maxSize(50);
                    builder.minSize(10);
                })
                // 配置读取首选项,实现读写分离
                .readPreference(ReadPreference.secondaryPreferred())
                .build();
        
        return MongoClients.create(settings);
    }
}

九、常见问题与解决方案

1. 连接超时问题

  • 检查MongoDB服务是否启动
  • 确认网络连接和防火墙设置
  • 调整连接超时参数
spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/testdb?connectTimeoutMS=5000&socketTimeoutMS=30000

2. 认证失败问题

  • 确认用户名和密码是否正确
  • 检查认证数据库是否正确(通常是admin)
  • 验证用户权限

3. 性能优化

  • 合理设置连接池大小
  • 创建适当的索引
  • 使用批量操作减少网络开销

4. 序列化问题

对于复杂对象的序列化,可能需要自定义转换器:

@Configuration
public class MongoConfig {
    
    @Bean
    public MongoCustomConversions mongoCustomConversions() {
        List<Converter<?, ?>> converters = new ArrayList<>();
        // 添加自定义转换器
        converters.add(new LocalDateTimeToDateConverter());
        converters.add(new DateToLocalDateTimeConverter());
        return new MongoCustomConversions(converters);
    }
}

通过以上配置和示例,您可以成功实现SpringBoot与MongoDB的整合,并根据项目需求进行灵活的配置和使用。