You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Data MongoDB迁移AWS DocumentDB后@DBRef解析失败求助

解决Spring WebFlux + AWS DocumentDB下@DBRef解析报错"DBRef resolution is not supported!"的问题

问题背景

我有一个集成MongoDB的Spring WebFlux应用,实体类使用@DBRef注解,在Spring Boot 2.4.4版本下,仅添加以下依赖并配置spring.data.mongodb.uri时,@DBRef解析功能正常:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

迁移到AWS DocumentDB后,手动编写了数据库配置类来建立反应式与非反应式连接,操作带@DBRef的实体时抛出UnsupportedOperationException,提示"DBRef resolution is not supported!"。

手动配置类代码:

@Slf4j
@Configuration
public class MongoConfiguration {

  private static final String SSL_CERTIFICATE = "sslCertificate";
  private static final String KEY_STORE_FILE_PREFIX = "sys-connect-via-ssl-test-cacerts";
  private static final String KEY_STORE_FILE_SUFFIX = ".jks";
  private static final String DEFAULT_KEY_STORE_KEYPW = "abcd";

  @Value("${mongodb.uri}")
  private String mongoURI;

  @Value("${default.dbname}")
  private String dbName;

  @Value("${default.certificateName}")
  private String defaultCertificatePath;

  @Bean
  public ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory()
      throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException, KeyManagementException {

    MongoClientSettings.Builder builder = MongoClientSettings.builder();

    SslSettings.Builder sslBuilder = SslSettings.builder();

    X509Certificate caCert = createCertificate();
    File keyStoreFile = File.createTempFile(KEY_STORE_FILE_PREFIX, KEY_STORE_FILE_SUFFIX);
    FileOutputStream fos = new FileOutputStream(keyStoreFile.getPath());

    TrustManagerFactory trustManagerFactory = TrustManagerFactory
        .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null);
    keyStore.setCertificateEntry("caCert", caCert);
    keyStore.store(fos, DEFAULT_KEY_STORE_KEYPW.toCharArray());

    trustManagerFactory.init(keyStore);

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
    sslBuilder.invalidHostNameAllowed(true);
    sslBuilder.enabled(true);

    sslBuilder.context(sslContext);
    SslSettings sslsettings = sslBuilder.build();

    builder.applyConnectionString(new ConnectionString(
        mongoURI));

    MongoClientSettings settings = builder
        .applyToSslSettings(builder1 -> builder1.applySettings(sslsettings)).build();

    return new SimpleReactiveMongoDatabaseFactory(MongoClients.create(settings), dbName);
  }

  @Bean
  public MongoDatabaseFactory mongoDatabaseFactory()
      throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException, KeyManagementException {

    MongoClientSettings.Builder builder = MongoClientSettings.builder();

    SslSettings.Builder sslBuilder = SslSettings.builder();

    X509Certificate caCert = createCertificate();
    File keyStoreFile = File.createTempFile(KEY_STORE_FILE_PREFIX, KEY_STORE_FILE_SUFFIX);
    FileOutputStream fos = new FileOutputStream(keyStoreFile.getPath());

    TrustManagerFactory trustManagerFactory = TrustManagerFactory
        .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null);
    keyStore.setCertificateEntry("caCert", caCert);
    keyStore.store(fos, DEFAULT_KEY_STORE_KEYPW.toCharArray());

    trustManagerFactory.init(keyStore);

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
    sslBuilder.invalidHostNameAllowed(true);
    sslBuilder.enabled(true);

    sslBuilder.context(sslContext);
    SslSettings sslsettings = sslBuilder.build();

    builder.applyConnectionString(new ConnectionString(
        mongoURI));

    MongoClientSettings settings = builder
        .applyToSslSettings(builder1 -> builder1.applySettings(sslsettings)).build();

    return new SimpleMongoClientDatabaseFactory(com.mongodb.client.MongoClients.create(settings), dbName);
  }

  @Bean
  public ReactiveMongoTemplate reactiveMongoTemplate()
      throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, KeyManagementException {
    return new ReactiveMongoTemplate(reactiveMongoDatabaseFactory());
  }

  @Primary
  @Bean
  public MongoTemplate mongoTemplate()
      throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, KeyManagementException {
    return new MongoTemplate(mongoDatabaseFactory());
  }

  private X509Certificate createCertificate()
      throws CertificateException, MalformedURLException {
    String sslCertificate = System.getProperty(SSL_CERTIFICATE);
    if (StringUtils.isEmpty(sslCertificate)) {
      sslCertificate = defaultCertificatePath;
    }

    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
    URL url = new File(sslCertificate).toURI().toURL();

    try (InputStream certInputStream = url.openStream()) {
      return (X509Certificate) certFactory.generateCertificate(certInputStream);
    } catch (IOException e) {
      log.error(e.getMessage());
      return null;
    }
  }

}

问题原因

手动配置时直接实例化ReactiveMongoTemplate和MongoTemplate,没有初始化Spring Data MongoDB用于解析@DBRef的核心组件——MappingMongoConverter(及对应的DefaultDbRefResolver)。Spring Boot自动配置会自动创建并关联这些组件,包括MongoMappingContext来处理实体映射和DBRef解析,而手动创建模板时默认使用的转换器不支持DBRef解析,导致抛出异常。

解决方案

方案一:复用Spring自动配置组件,手动配置支持DBRef的模板

修改配置类中的reactiveMongoTemplate和mongoTemplate Bean,手动创建支持DBRef解析的MappingMongoConverter:

// 反应式模板配置
@Bean
public ReactiveMongoTemplate reactiveMongoTemplate(ReactiveMongoMappingContext mappingContext,
                                                  MongoCustomConversions customConversions)
      throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, KeyManagementException {
    // 创建DBRef解析器,关联数据库工厂
    DefaultDbRefResolver dbRefResolver = new DefaultDbRefResolver(reactiveMongoDatabaseFactory());
    // 创建映射转换器,关联解析器和映射上下文
    MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
    // 设置自定义转换规则
    converter.setCustomConversions(customConversions);
    // 初始化转换器
    converter.afterPropertiesSet();
    return new ReactiveMongoTemplate(reactiveMongoDatabaseFactory(), converter);
}

// 非反应式模板配置
@Primary
@Bean
public MongoTemplate mongoTemplate(MongoMappingContext mappingContext,
                                  MongoCustomConversions customConversions)
      throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, KeyManagementException {
    DefaultDbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory());
    MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext);
    converter.setCustomConversions(customConversions);
    converter.afterPropertiesSet();
    return new MongoTemplate(mongoDatabaseFactory(), converter);
}

方案二:简化配置,复用Spring Boot自动配置(推荐)

不需要手动创建ReactiveMongoDatabaseFactory、MongoDatabaseFactory、ReactiveMongoTemplate和MongoTemplate,仅配置MongoClientSettings Bean,让Spring Boot自动完成其余配置:

修改配置类,只保留mongoClientSettings和createCertificate方法:

@Slf4j
@Configuration
public class MongoConfiguration {

  private static final String SSL_CERTIFICATE = "sslCertificate";
  private static final String KEY_STORE_FILE_PREFIX = "sys-connect-via-ssl-test-cacerts";
  private static final String KEY_STORE_FILE_SUFFIX = ".jks";
  private static final String DEFAULT_KEY_STORE_KEYPW = "abcd";

  @Value("${mongodb.uri}")
  private String mongoURI;

  @Value("${default.certificateName}")
  private String defaultCertificatePath;

  @Bean
  public MongoClientSettings mongoClientSettings() throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException, KeyManagementException {
    MongoClientSettings.Builder builder = MongoClientSettings.builder();

    SslSettings.Builder sslBuilder = SslSettings.builder();

    X509Certificate caCert = createCertificate();
    File keyStoreFile = File.createTempFile(KEY_STORE_FILE_PREFIX, KEY_STORE_FILE_SUFFIX);
    FileOutputStream fos = new FileOutputStream(keyStoreFile.getPath());

    TrustManagerFactory trustManagerFactory = TrustManagerFactory
        .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null);
    keyStore.setCertificateEntry("caCert", caCert);
    keyStore.store(fos, DEFAULT_KEY_STORE_KEYPW.toCharArray());

    trustManagerFactory.init(keyStore);

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
    sslBuilder.invalidHostNameAllowed(true);
    sslBuilder.enabled(true);

    sslBuilder.context(sslContext);
    SslSettings sslsettings = sslBuilder.build();

    builder.applyConnectionString(new ConnectionString(mongoURI));

    return builder
        .applyToSslSettings(builder1 -> builder1.applySettings(sslsettings)).build();
  }

  private X509Certificate createCertificate()
      throws CertificateException, MalformedURLException {
    String sslCertificate = System.getProperty(SSL_CERTIFICATE);
    if (StringUtils.isEmpty(sslCertificate)) {
      sslCertificate = defaultCertificatePath;
    }

    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
    URL url = new File(sslCertificate).toURI().toURL();

    try (InputStream certInputStream = url.openStream()) {
      return (X509Certificate) certFactory.generateCertificate(certInputStream);
    } catch (IOException e) {
      log.error(e.getMessage());
      return null;
    }
  }
}

同时在配置文件中添加数据库名称配置:

spring.data.mongodb.database=${default.dbname}

这样Spring Boot会自动使用自定义的MongoClientSettings创建连接,并自动配置支持DBRef解析的ReactiveMongoTemplate和MongoTemplate,无需手动处理组件初始化。

内容的提问来源于stack exchange,提问作者techmagister

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 09:27:20