Spring mvc Spring和MongoDB:以更简单的方式保存值对象
对于我正在设计和开发的一个新的Spring应用程序,出于一些技术原因,我们使用MongoDB作为持久层。这是我尝试实现一些DDD原则的第一个项目,包括值对象。我试图找到保存ValueObject的最佳方法,它实际上只是一个字符串。使用Lombok的@Value,我的SpringREST控制器在RestController端愉快地将值解析为ValueObject。但是当保存值时,它会在MongoDB端以结构化的方式保存 比如说 我的VO:Spring mvc Spring和MongoDB:以更简单的方式保存值对象,spring-mvc,domain-driven-design,spring-data-mongodb,lombok,value-objects,Spring Mvc,Domain Driven Design,Spring Data Mongodb,Lombok,Value Objects,对于我正在设计和开发的一个新的Spring应用程序,出于一些技术原因,我们使用MongoDB作为持久层。这是我尝试实现一些DDD原则的第一个项目,包括值对象。我试图找到保存ValueObject的最佳方法,它实际上只是一个字符串。使用Lombok的@Value,我的SpringREST控制器在RestController端愉快地将值解析为ValueObject。但是当保存值时,它会在MongoDB端以结构化的方式保存 比如说 我的VO: @Value public class PersonKey
@Value
public class PersonKey {
private String value;
}
我将在MongoDB中存储的文档:
@Document
public class PersonDocument {
private PersonKey personKey;
private Name name;
...
}
{.. "personKey": {"value": "faeeaf2"} ...}
MongoDB中保存的内容:
@Document
public class PersonDocument {
private PersonKey personKey;
private Name name;
...
}
{.. "personKey": {"value": "faeeaf2"} ...}
我真正想要的是:
{.. "personKey": "faeeaf2" ..}
当然,使用最少的额外样板代码…-) 似乎您唯一的选择是使用
AbstractMongoEventListener
和onAfterConvert
方法在转换后修改DBObject
。不幸的是,不可能轻松更改文档中单个字段的转换。保存整个文档而不是单个字段时使用自定义转换器。您也不能使用getter方法来替换字段访问(“对象的字段用于与文档中的字段进行转换。不使用公共JavaBean属性。”from)。因此,实现您想要的目标的唯一方法是通过mongodb事件。但是,您可以在事件处理程序中使用反射来检查字段是否用@Value
注释进行了注释,因此可以以更通用的方式对其进行转换。如果存在@Value
注释,只需在DBObject
中将其替换为其Value属性即可
要实现这一点,您需要扩展AbstractMongoEventListener
。您可以通过onBeforeSave
事件处理程序在此处看到示例:
更新:
正如@maartinus在评论中注意到的,使用反射搜索
@Value
对象将不起作用,因为它在运行时不可用(保留设置为源代码
)。因此,您需要使用单个方法value()
来引入自己的注释或接口(例如ValueObject
),该方法将返回对象的值。巧合的是,我正在处理完全相同的问题。你可以用CustomConverters来实现这一点。也许不完全像你想要的,但方式非常相似。我唯一无法克服的问题是,您不能在运行时动态选择转换器。这是由于在行号182
上实现了CustomConversions#registerConversion
这个例子展示了我目前正在做的事情。我使用的是通用转换器
,但您也可以退回到常规的转换器
。本例不检查字段注释。相反,它使用一个名为SingleValue
的接口。表示单个值的所有my Value对象都实现此接口。因此,不需要在字段上进行注释
@Configuration
@Slf4j
public class MongoDbConfiguration {
@Bean
@Primary
public CustomConversions mongoCustomConversions() throws Exception {
final List converterList = new ArrayList<>();
converterList.add(new SingleValueConverter());
return new CustomConversions(converterList);
}
private static class SingleValueConverter implements GenericConverter {
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return new HashSet<>(Arrays.asList(new ConvertiblePair[]{
new ConvertiblePair(UUIDEntityReference.class, String.class),
new ConvertiblePair(String.class, FundingProcessId.class)
// put here all of your type conversions (two way!)
}));
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
try {
// first check if the instance of this type is an instance of our 'SingleValue' interface
if (source instanceof SingleValue) {
return ((SingleValue) source).getValue();
}
final Class<?> objectType = targetType.getType();
final Constructor<?> constructor = objectType.getConstructor(sourceType.getType());
return constructor.newInstance(source);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Could not convert unexpected type " +
sourceType.getObjectType().getName() + " to " + targetType.getObjectType().getName(), e);
}
}
}
}
@配置
@Slf4j
公共类MongoDbConfiguration{
@豆子
@初级的
public CustomConversions mongoCustomConversions()引发异常{
最终列表转换器列表=新的ArrayList();
添加(新的SingleValueConverter());
返回新的CustomConversions(转换器列表);
}
私有静态类SingleValueConverter实现GenericConverter{
@凌驾
公共集getConvertibleTypes(){
返回新的HashSet(Arrays.asList)(新的ConvertiblePair[]{
新的ConvertiblePair(UUIDEntityReference.class,String.class),
新的ConvertiblePair(String.class,FundingProcessId.class)
//把你所有的类型转换都放在这里(双向!)
}));
}
@凌驾
公共对象转换(对象源、类型描述符源类型、类型描述符目标类型){
试一试{
//首先检查此类型的实例是否是“SingleValue”接口的实例
if(SingleValue的源实例){
返回((SingleValue)source.getValue();
}
最终类objectType=targetType.getType();
最终构造函数=objectType.getConstructor(sourceType.getType());
返回构造函数.newInstance(源);
}捕捉(反射操作异常e){
抛出新的RuntimeException(“无法转换意外类型”+
sourceType.getObjectType().getName()+”到“+targetType.getObjectType().getName(),e);
}
}
}
}
这是我能找到的最优雅的方式。仍在寻找自动注册所有类型的方法。这可以通过注释和类路径扫描实现
请注意,此实现假设了以下几点
SingleValue
编辑:我发布了错误的示例代码。在
PHP
中更新了它,我完成了一个转换器类,每次insert
、update
和find
mongodb操作都会调用它。这个转换器是递归的,并且使用反射。这就是PHP,一种动态语言。我敢说Java作为一种静态语言是不同的:-)这就是为什么我没有提供答案的原因。也许这对你有帮助。可以添加自定义转换器,帮助从MongoDB层进行转换,正如在中所解释的,这可能是一个解决方案,但不够动态,不符合我的喜好检查@lombok.Value
无法工作,因为它是@Retention(RetentionPolicy.SOURCE)
。