具有 spring-data-mongodb 存储库的持久路径对象
Persisting Path objects with spring-data-mongodb respositories
在一个项目中,我使用 spring-boot-starter-data-mongodb:2.5.3
,因此 spring-data-mongodb:3.2.3
并且有一个简化的实体 class,如下所示:
@Document
public class Task {
@Id
private final String id;
private final Path taskDir;
...
// constructor, getters, setters
}
使用默认 Spring Mongo允许通过其 ID 检索任务的数据库存储库。
Mongo 配置如下所示:
@Configuration
@EnableMongoRepositories(basePackages = {
"path.to.repository"
}, mongoTemplateRef = MongoConfig.MONGO_TEMPLATE_REF)
@EnableConfigurationProperties(MongoSettings.class)
public class MongoConfig extends MongoConfigurationSupport {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final String MONGO_TEMPLATE_REF = "mongoAlTemplate";
private final MongoSettings mongoSettings;
@Autowired
public MongoConfig(final MongoSettings mongoSettings) {
this.mongoSettings = mongoSettings;
}
@Bean(name = "ourMongo", destroyMethod = "close")
public MongoClient ourMongoClient() {
MongoCredential credential =
MongoCredential.createCredential(mongoSettings.getUser(),
mongoSettings.getDb(),
mongoSettings.getPassword());
MongoClientSettings clientSettings = MongoClientSettings.builder()
.readPreference(ReadPreference.primary())
// enable optimistic locking for @Version and eTag usage
.writeConcern(WriteConcern.ACKNOWLEDGED)
.credential(credential)
.applyToSocketSettings(
builder -> builder.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.MINUTES))
.applyToConnectionPoolSettings(
builder -> builder.maxConnectionIdleTime(10, TimeUnit.MINUTES)
.minSize(5).maxSize(20))
// .applyToClusterSettings(
// builder -> builder.requiredClusterType(ClusterType.REPLICA_SET)
// .hosts(Arrays.asList(new ServerAddress("host1", 27017),
// new ServerAddress("host2", 27017)))
// .build())
.build();
return MongoClients.create(clientSettings);
}
@Override
@Nonnull
protected String getDatabaseName() {
return mongoSettings.getDb();
}
@Bean(name = MONGO_TEMPLATE_REF)
public MongoTemplate ourMongoTemplate() throws Exception {
return new MongoTemplate(ourMongoClient(), getDatabaseName());
}
}
尝试通过 taskRepository.save(task)
保存任务时 Java 最终出现 WhosebugError
java.lang.WhosebugError
at java.lang.ThreadLocal.get(ThreadLocal.java:160)
at java.util.concurrent.locks.ReentrantReadWriteLock$Sync.tryReleaseShared(ReentrantReadWriteLock.java:423)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(AbstractQueuedSynchronizer.java:1341)
at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.unlock(ReentrantReadWriteLock.java:881)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:239)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:201)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:87)
at org.springframework.data.mapping.context.MappingContext.getRequiredPersistentEntity(MappingContext.java:73)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:740)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeProperties(MappingMongoConverter.java:657)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:633)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:746)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeProperties(MappingMongoConverter.java:657)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:633)
...
在 Task
class 中用 @Transient
注释路径对象 taskDir
我能够坚持任务,所以问题似乎与Java/Spring/MongoDB 无法直接处理 Path
个对象。
我的下一次尝试是在 MongoConfig
class 中配置自定义转换器以在 Path
和 String
表示之间进行转换:
@Override
protected void configureConverters(
MongoCustomConversions.MongoConverterConfigurationAdapter converterConfigurationAdapter) {
LOG.info("configuring converters");
converterConfigurationAdapter.registerConverter(new Converter<Path, String>() {
@Override
public String convert(@Nonnull Path path) {
return path.normalize().toAbsolutePath().toString();
}
});
converterConfigurationAdapter.registerConverter(new Converter<String, Path>() {
@Override
public Path convert(@Nonnull String path) {
return Paths.get(path);
}
});
}
尽管错误仍然存在。然后我定义了 Task
对象和 DBObject
之间的直接转换,如 guide
中所示
@Override
protected void configureConverters(
MongoCustomConversions.MongoConverterConfigurationAdapter converterConfigurationAdapter) {
LOG.info("configuring converters");
converterConfigurationAdapter.registerConverter(new Converter<Task, DBObject>() {
@Override
public DBObject convert(@Nonnull Task source) {
DBObject dbObject = new BasicDBObject();
if (source.getTaskDirectory() != null) {
dbObject.put("taskDir", source.getTaskDirectory().normalize().toAbsolutePath().toString());
}
...
return dbObject;
}
});
}
我仍然在 return 中得到 WhosebugError
。通过我添加的日志语句,我看到 Spring 调用了 configureConverters
方法,因此应该已经注册了自定义转换器。
为什么我仍然得到 WhosebugError
?我需要如何告诉 Spring 将 Path
对象视为 String
s,同时坚持并在读取时将 String
值转换为 Path
对象又是?
更新:
我现在已经遵循 the official documentation 中给出的示例并将转换器重构为它自己的 class
import org.bson.Document;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.WritingConverter;
import javax.annotation.Nonnull;
@WritingConverter
public class TaskWriteConverter implements Converter<Task, Document> {
@Override
public Document convert(@Nonnull Task source) {
Document document = new Document();
document.put("_id", source.getId());
if (source.getTaskDir() != null) {
document.put("taskDir", source.getTaskDir().normalize().toAbsolutePath().toString());
}
return document;
}
}
MongoConfig
class 中的配置现在看起来像这样:
@Override
protected void configureConverters(
MongoCustomConversions.MongoConverterConfigurationAdapter adapter) {
LOG.info("configuring converters");
adapter.registerConverter(new TaskWriteConverter());
adapter.registerConverter(new TaskReadConverter());
adapter.registerConverter(new Converter<Path, String>() {
@Override
public String convert(@Nonnull Path path) {
return path.normalize().toAbsolutePath().toString();
}
});
adapter.registerConverter(new Converter<String, Path>() {
@Override
public Path convert(@Nonnull String path) {
return Paths.get(path);
}
});
}
将 org.springframework.data
的日志记录级别更改为 debug
后,我在日志中看到这些转换器也被选中:
2021-09-23 14:09:20.469 [INFO ] [ main] MongoConfig configuring converters
2021-09-23 14:09:20.480 [DEBUG] [ main] CustomConversions Adding user defined converter from class com.acme.Task to class org.bson.Document as writing converter.
2021-09-23 14:09:20.480 [DEBUG] [ main] CustomConversions Adding user defined converter from class org.bson.Document to class com.acme.Task as reading converter.
2021-09-23 14:09:20.481 [DEBUG] [ main] CustomConversions Adding user defined converter from interface java.nio.file.Path to class java.lang.String as writing converter.
2021-09-23 14:09:20.481 [DEBUG] [ main] CustomConversions Adding user defined converter from class java.lang.String to interface java.nio.file.Path as reading converter.
但是,我看到大多数转换器都被添加了多次,即在应用程序点击存储库上的 save
方法之前,我实际上找到了 Adding converter from class java.lang.Character to class java.lang.String as writing converter.
的日志 4 次。由于我的自定义转换器仅在第 3 次添加,所有这些转换器都出现在日志中,我感觉它们以某种方式被覆盖,因为上次“迭代”中的日志不包括我的自定义转换器。
重现该问题的测试用例如下:
@ŚpringBootTest
@AutoConfigureMockMvc
@PropertySource("classpath:application-test.properties")
public class SomeIT {
@Autowired
private TaskRepository taskRepository;
...
@Test
public void testTaskPersistence() throws Exception {
Task task = new Task("1234", Paths.get("/home/roman"));
taskRepository.save(task);
}
...
}
测试方法仅用于调查当前的持久性问题,在正常情况下根本不应该存在,因为集成测试测试大文件的上传、预处理等。然而,由于 Spring 无法(至少看起来是这样)存储包含 Path 对象的实体,此集成测试失败。
请注意,对于简单的实体,我在使用概述的设置持久化它们时没有问题,而且我也在 dockerized MongoDB 中看到它们。
我还没有时间深入研究为什么 Spring 对 Path
对象有这样的问题,或者为什么我的自定义转换器在 CustomConversions
的最后一次迭代中突然消失了日志输出。
事实证明 mongoTemplate
的配置方式确实“覆盖”了任何指定的自定义转换器,因此 Spring 无法利用这些并将 Path
转换为String
反之亦然。
将 MongoConfig
更改为以下内容后,我终于能够使用我的自定义转换器,从而按预期持久化实体:
@Configuration
@EnableMongoRepositories(basePackages = {
"path.to.repository"
}, mongoTemplateRef = MongoConfig.MONGO_TEMPLATE_REF)
@EnableConfigurationProperties(MongoSettings.class)
public class MongoConfig extends MongoConfigurationSupport {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final String MONGO_TEMPLATE_REF = "mongoAlTemplate";
private final MongoSettings mongoSettings;
@Autowired
public MongoConfig(final MongoSettings mongoSettings) {
this.mongoSettings = mongoSettings;
}
@Bean(name = "ourMongo", destroyMethod = "close")
public MongoClient ourMongoClient() {
MongoCredential credential =
MongoCredential.createCredential(mongoSettings.getUser(),
mongoSettings.getDb(),
mongoSettings.getPassword());
MongoClientSettings clientSettings = MongoClientSettings.builder()
.readPreference(ReadPreference.primary())
// enable optimistic locking for @Version and eTag usage
.writeConcern(WriteConcern.ACKNOWLEDGED)
.credential(credential)
.applyToSocketSettings(
builder -> builder.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.MINUTES))
.applyToConnectionPoolSettings(
builder -> builder.maxConnectionIdleTime(10, TimeUnit.MINUTES)
.minSize(5).maxSize(20))
// .applyToClusterSettings(
// builder -> builder.requiredClusterType(ClusterType.REPLICA_SET)
// .hosts(Arrays.asList(new ServerAddress("host1", 27017),
// new ServerAddress("host2", 27017)))
// .build())
.build();
LOG.info("Mongo client initialized. Connecting with user {} to DB {}",
mongoSettings.getUser(), mongoSettings.getDb());
return MongoClients.create(clientSettings);
}
@Override
@Nonnull
protected String getDatabaseName() {
return mongoSettings.getDb();
}
@Bean
public MongoDatabaseFactory ourMongoDBFactory() {
return new SimpleMongoClientDatabaseFactory(ourMongoClient(), getDatabaseName());
}
@Bean(name = MONGO_TEMPLATE_REF)
public MongoTemplate ourMongoTemplate() throws Exception {
return new MongoTemplate(ourMongoDBFactory(), mappingMongoConverter());
}
@Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(ourMongoDBFactory());
MongoCustomConversions customConversions = customConversions();
MongoMappingContext context = mongoMappingContext(customConversions);
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, context);
// this one is actually needed otherwise the WhosebugError re-appears!
converter.setCustomConversions(customConversions);
return converter;
}
@Bean
@Override
@Nonnull
public MongoCustomConversions customConversions() {
return new MongoCustomConversions(
Arrays.asList(new PathWriteConverter(), new PathReadConverter())
);
}
}
因此,不是将 MongoClient
和数据库名称直接传递给 mongoTemplate
,而是保存上述值的 MongoDatabaseFactory
对象和 MappingMongoConverter
对象作为输入传递给模板。
不幸的是,需要在mappingMongoConverter()
方法中传递两次customConversion
对象。如果不这样做,WhosebugError
会重新出现。
使用给定的配置,从 Path
到 String
和 String
到 Path
的转换现在是可能的,因此没有从 Task
到 Task
的自定义转换当前需要 Document
,反之亦然。
在一个项目中,我使用 spring-boot-starter-data-mongodb:2.5.3
,因此 spring-data-mongodb:3.2.3
并且有一个简化的实体 class,如下所示:
@Document
public class Task {
@Id
private final String id;
private final Path taskDir;
...
// constructor, getters, setters
}
使用默认 Spring Mongo允许通过其 ID 检索任务的数据库存储库。
Mongo 配置如下所示:
@Configuration
@EnableMongoRepositories(basePackages = {
"path.to.repository"
}, mongoTemplateRef = MongoConfig.MONGO_TEMPLATE_REF)
@EnableConfigurationProperties(MongoSettings.class)
public class MongoConfig extends MongoConfigurationSupport {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final String MONGO_TEMPLATE_REF = "mongoAlTemplate";
private final MongoSettings mongoSettings;
@Autowired
public MongoConfig(final MongoSettings mongoSettings) {
this.mongoSettings = mongoSettings;
}
@Bean(name = "ourMongo", destroyMethod = "close")
public MongoClient ourMongoClient() {
MongoCredential credential =
MongoCredential.createCredential(mongoSettings.getUser(),
mongoSettings.getDb(),
mongoSettings.getPassword());
MongoClientSettings clientSettings = MongoClientSettings.builder()
.readPreference(ReadPreference.primary())
// enable optimistic locking for @Version and eTag usage
.writeConcern(WriteConcern.ACKNOWLEDGED)
.credential(credential)
.applyToSocketSettings(
builder -> builder.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.MINUTES))
.applyToConnectionPoolSettings(
builder -> builder.maxConnectionIdleTime(10, TimeUnit.MINUTES)
.minSize(5).maxSize(20))
// .applyToClusterSettings(
// builder -> builder.requiredClusterType(ClusterType.REPLICA_SET)
// .hosts(Arrays.asList(new ServerAddress("host1", 27017),
// new ServerAddress("host2", 27017)))
// .build())
.build();
return MongoClients.create(clientSettings);
}
@Override
@Nonnull
protected String getDatabaseName() {
return mongoSettings.getDb();
}
@Bean(name = MONGO_TEMPLATE_REF)
public MongoTemplate ourMongoTemplate() throws Exception {
return new MongoTemplate(ourMongoClient(), getDatabaseName());
}
}
尝试通过 taskRepository.save(task)
保存任务时 Java 最终出现 WhosebugError
java.lang.WhosebugError
at java.lang.ThreadLocal.get(ThreadLocal.java:160)
at java.util.concurrent.locks.ReentrantReadWriteLock$Sync.tryReleaseShared(ReentrantReadWriteLock.java:423)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(AbstractQueuedSynchronizer.java:1341)
at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.unlock(ReentrantReadWriteLock.java:881)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:239)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:201)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:87)
at org.springframework.data.mapping.context.MappingContext.getRequiredPersistentEntity(MappingContext.java:73)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:740)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeProperties(MappingMongoConverter.java:657)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:633)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writePropertyInternal(MappingMongoConverter.java:746)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeProperties(MappingMongoConverter.java:657)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeInternal(MappingMongoConverter.java:633)
...
在 Task
class 中用 @Transient
注释路径对象 taskDir
我能够坚持任务,所以问题似乎与Java/Spring/MongoDB 无法直接处理 Path
个对象。
我的下一次尝试是在 MongoConfig
class 中配置自定义转换器以在 Path
和 String
表示之间进行转换:
@Override
protected void configureConverters(
MongoCustomConversions.MongoConverterConfigurationAdapter converterConfigurationAdapter) {
LOG.info("configuring converters");
converterConfigurationAdapter.registerConverter(new Converter<Path, String>() {
@Override
public String convert(@Nonnull Path path) {
return path.normalize().toAbsolutePath().toString();
}
});
converterConfigurationAdapter.registerConverter(new Converter<String, Path>() {
@Override
public Path convert(@Nonnull String path) {
return Paths.get(path);
}
});
}
尽管错误仍然存在。然后我定义了 Task
对象和 DBObject
之间的直接转换,如 guide
@Override
protected void configureConverters(
MongoCustomConversions.MongoConverterConfigurationAdapter converterConfigurationAdapter) {
LOG.info("configuring converters");
converterConfigurationAdapter.registerConverter(new Converter<Task, DBObject>() {
@Override
public DBObject convert(@Nonnull Task source) {
DBObject dbObject = new BasicDBObject();
if (source.getTaskDirectory() != null) {
dbObject.put("taskDir", source.getTaskDirectory().normalize().toAbsolutePath().toString());
}
...
return dbObject;
}
});
}
我仍然在 return 中得到 WhosebugError
。通过我添加的日志语句,我看到 Spring 调用了 configureConverters
方法,因此应该已经注册了自定义转换器。
为什么我仍然得到 WhosebugError
?我需要如何告诉 Spring 将 Path
对象视为 String
s,同时坚持并在读取时将 String
值转换为 Path
对象又是?
更新:
我现在已经遵循 the official documentation 中给出的示例并将转换器重构为它自己的 class
import org.bson.Document;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.WritingConverter;
import javax.annotation.Nonnull;
@WritingConverter
public class TaskWriteConverter implements Converter<Task, Document> {
@Override
public Document convert(@Nonnull Task source) {
Document document = new Document();
document.put("_id", source.getId());
if (source.getTaskDir() != null) {
document.put("taskDir", source.getTaskDir().normalize().toAbsolutePath().toString());
}
return document;
}
}
MongoConfig
class 中的配置现在看起来像这样:
@Override
protected void configureConverters(
MongoCustomConversions.MongoConverterConfigurationAdapter adapter) {
LOG.info("configuring converters");
adapter.registerConverter(new TaskWriteConverter());
adapter.registerConverter(new TaskReadConverter());
adapter.registerConverter(new Converter<Path, String>() {
@Override
public String convert(@Nonnull Path path) {
return path.normalize().toAbsolutePath().toString();
}
});
adapter.registerConverter(new Converter<String, Path>() {
@Override
public Path convert(@Nonnull String path) {
return Paths.get(path);
}
});
}
将 org.springframework.data
的日志记录级别更改为 debug
后,我在日志中看到这些转换器也被选中:
2021-09-23 14:09:20.469 [INFO ] [ main] MongoConfig configuring converters
2021-09-23 14:09:20.480 [DEBUG] [ main] CustomConversions Adding user defined converter from class com.acme.Task to class org.bson.Document as writing converter.
2021-09-23 14:09:20.480 [DEBUG] [ main] CustomConversions Adding user defined converter from class org.bson.Document to class com.acme.Task as reading converter.
2021-09-23 14:09:20.481 [DEBUG] [ main] CustomConversions Adding user defined converter from interface java.nio.file.Path to class java.lang.String as writing converter.
2021-09-23 14:09:20.481 [DEBUG] [ main] CustomConversions Adding user defined converter from class java.lang.String to interface java.nio.file.Path as reading converter.
但是,我看到大多数转换器都被添加了多次,即在应用程序点击存储库上的 save
方法之前,我实际上找到了 Adding converter from class java.lang.Character to class java.lang.String as writing converter.
的日志 4 次。由于我的自定义转换器仅在第 3 次添加,所有这些转换器都出现在日志中,我感觉它们以某种方式被覆盖,因为上次“迭代”中的日志不包括我的自定义转换器。
重现该问题的测试用例如下:
@ŚpringBootTest
@AutoConfigureMockMvc
@PropertySource("classpath:application-test.properties")
public class SomeIT {
@Autowired
private TaskRepository taskRepository;
...
@Test
public void testTaskPersistence() throws Exception {
Task task = new Task("1234", Paths.get("/home/roman"));
taskRepository.save(task);
}
...
}
测试方法仅用于调查当前的持久性问题,在正常情况下根本不应该存在,因为集成测试测试大文件的上传、预处理等。然而,由于 Spring 无法(至少看起来是这样)存储包含 Path 对象的实体,此集成测试失败。
请注意,对于简单的实体,我在使用概述的设置持久化它们时没有问题,而且我也在 dockerized MongoDB 中看到它们。
我还没有时间深入研究为什么 Spring 对 Path
对象有这样的问题,或者为什么我的自定义转换器在 CustomConversions
的最后一次迭代中突然消失了日志输出。
事实证明 mongoTemplate
的配置方式确实“覆盖”了任何指定的自定义转换器,因此 Spring 无法利用这些并将 Path
转换为String
反之亦然。
将 MongoConfig
更改为以下内容后,我终于能够使用我的自定义转换器,从而按预期持久化实体:
@Configuration
@EnableMongoRepositories(basePackages = {
"path.to.repository"
}, mongoTemplateRef = MongoConfig.MONGO_TEMPLATE_REF)
@EnableConfigurationProperties(MongoSettings.class)
public class MongoConfig extends MongoConfigurationSupport {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final String MONGO_TEMPLATE_REF = "mongoAlTemplate";
private final MongoSettings mongoSettings;
@Autowired
public MongoConfig(final MongoSettings mongoSettings) {
this.mongoSettings = mongoSettings;
}
@Bean(name = "ourMongo", destroyMethod = "close")
public MongoClient ourMongoClient() {
MongoCredential credential =
MongoCredential.createCredential(mongoSettings.getUser(),
mongoSettings.getDb(),
mongoSettings.getPassword());
MongoClientSettings clientSettings = MongoClientSettings.builder()
.readPreference(ReadPreference.primary())
// enable optimistic locking for @Version and eTag usage
.writeConcern(WriteConcern.ACKNOWLEDGED)
.credential(credential)
.applyToSocketSettings(
builder -> builder.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.MINUTES))
.applyToConnectionPoolSettings(
builder -> builder.maxConnectionIdleTime(10, TimeUnit.MINUTES)
.minSize(5).maxSize(20))
// .applyToClusterSettings(
// builder -> builder.requiredClusterType(ClusterType.REPLICA_SET)
// .hosts(Arrays.asList(new ServerAddress("host1", 27017),
// new ServerAddress("host2", 27017)))
// .build())
.build();
LOG.info("Mongo client initialized. Connecting with user {} to DB {}",
mongoSettings.getUser(), mongoSettings.getDb());
return MongoClients.create(clientSettings);
}
@Override
@Nonnull
protected String getDatabaseName() {
return mongoSettings.getDb();
}
@Bean
public MongoDatabaseFactory ourMongoDBFactory() {
return new SimpleMongoClientDatabaseFactory(ourMongoClient(), getDatabaseName());
}
@Bean(name = MONGO_TEMPLATE_REF)
public MongoTemplate ourMongoTemplate() throws Exception {
return new MongoTemplate(ourMongoDBFactory(), mappingMongoConverter());
}
@Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(ourMongoDBFactory());
MongoCustomConversions customConversions = customConversions();
MongoMappingContext context = mongoMappingContext(customConversions);
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, context);
// this one is actually needed otherwise the WhosebugError re-appears!
converter.setCustomConversions(customConversions);
return converter;
}
@Bean
@Override
@Nonnull
public MongoCustomConversions customConversions() {
return new MongoCustomConversions(
Arrays.asList(new PathWriteConverter(), new PathReadConverter())
);
}
}
因此,不是将 MongoClient
和数据库名称直接传递给 mongoTemplate
,而是保存上述值的 MongoDatabaseFactory
对象和 MappingMongoConverter
对象作为输入传递给模板。
不幸的是,需要在mappingMongoConverter()
方法中传递两次customConversion
对象。如果不这样做,WhosebugError
会重新出现。
使用给定的配置,从 Path
到 String
和 String
到 Path
的转换现在是可能的,因此没有从 Task
到 Task
的自定义转换当前需要 Document
,反之亦然。