Java 如何自定义ModelMapper
我想使用ModelMapper将实体转换为DTO和DTO。大多数情况下,它是有效的,但我如何定制它呢。它有如此多的选择,以至于很难决定从哪里开始。什么是最佳实践Java 如何自定义ModelMapper,java,rest,modelmapper,Java,Rest,Modelmapper,我想使用ModelMapper将实体转换为DTO和DTO。大多数情况下,它是有效的,但我如何定制它呢。它有如此多的选择,以至于很难决定从哪里开始。什么是最佳实践 我会在下面自己回答,但如果另一个答案更好,我会接受它。首先这里有一些链接 我对mm的印象是它设计得非常好。代码是可靠的,阅读起来很愉快。但是,文档非常简洁,示例很少。此外,api令人困惑,因为似乎有10种方法可以做任何事情,但没有说明为什么要这样或那样做 有两种选择:一种是最受欢迎的,另一种是因为易于使用而获得好评 假设你仍
我会在下面自己回答,但如果另一个答案更好,我会接受它。首先这里有一些链接
ModelMapper
,应该是应用程序中的单例。对我来说,这意味着@Bean使用Spring。它适用于简单的情况。例如,假设您有两个类:
class DogData
{
private String name;
private int mass;
}
class DogInfo
{
private String name;
private boolean large;
}
使用适当的getter/setter。您可以这样做:
ModelMapper mm = new ModelMapper();
DogData dd = new DogData();
dd.setName("fido");
dd.setMass(70);
DogInfo di = mm.map(dd, DogInfo.class);
“名称”将从dd复制到di
有很多方法可以自定义mm,但首先您需要了解它是如何工作的
mm对象包含每个有序类型对的TypeMap,例如和将是两个TypeMap
每个类型映射都包含一个带有映射列表的属性映射。因此,在本例中,mm将自动创建一个TypeMap,其中包含一个具有单个映射的PropertyMap
我们可以写这个
TypeMap<DogData, DogInfo> tm = mm.getTypeMap(DogData.class, DogInfo.class);
List<Mapping> list = tm.getMappings();
for (Mapping m : list)
{
System.out.println(m);
}
当您调用mm.map()时,它就是这样做的
- 如果TypeMap有一个自定义转换器,则调用它
- 或者,生成一个属性映射(基于配置标志加上添加的任何自定义映射),并使用它 (注意:TypeMap还具有可选的自定义Pre/PostPropertyConverter,我认为它将在每次映射之前和之后运行。)
- 步骤5a.–编写自定义类型映射转换器,或
- 步骤5b.–编写自定义属性映射
Converter<DogData, DogInfo> myConverter = new Converter<DogData, DogInfo>()
{
public DogInfo convert(MappingContext<DogData, DogInfo> context)
{
DogData s = context.getSource();
DogInfo d = context.getDestination();
d.setName(s.getName());
d.setLarge(s.getMass() > 25);
return d;
}
};
mm.addConverter(myConverter);
Converter<Integer, Boolean> convertMassToLarge = new Converter<Integer, Boolean>()
{
public Boolean convert(MappingContext<Integer, Boolean> context)
{
// If the dog weighs more than 25, then it must be large
return context.getSource() > 25;
}
};
PropertyMap<DogData, DogInfo> mymap = new PropertyMap<DogData, DogInfo>()
{
protected void configure()
{
// Note: this is not normal code. It is "EDSL" so don't get confused
map(source.getName()).setName(null);
using(convertMassToLarge).map(source.getMass()).setLarge(false);
}
};
mm.addMappings(mymap);
在自定义属性Map.configure()中
在本例中,我必须编写一个转换器,将整数映射为布尔值。在大多数情况下,这是不必要的,因为mm会自动将整数转换为字符串等
我听说你也可以创建映射表达式。我试过了,但我想不出来
最终建议和最佳实践
默认情况下,mm使用匹配策略.STANDARD
,这是危险的。它很容易选择错误的映射,并导致奇怪的、难以发现的错误。如果明年有人在数据库中添加一个新的列呢?所以不要这样做。确保使用严格模式:
mm.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
始终编写单元测试并确保所有映射都经过验证
DogInfo di = mm.map(dd, DogInfo.class);
mm.validate(); // make sure nothing in the destination is accidentally skipped
如上图所示,使用mm.addMappings()
修复任何验证失败
将所有映射放在创建mm singleton的中心位置。我在过去6个月一直在使用它,我将解释我对此的一些想法: 首先,建议将其作为一个独特的实例使用(singleton、SpringBean等等),这在手册中有解释,我想大家都同意这一点
ModelMapper
是一个强大的映射库,具有广泛的灵活性。由于它的灵活性,有许多方法可以获得相同的结果,这就是为什么它应该出现在最佳实践手册中,说明何时使用一种或另一种方法来做相同的事情
从ModelMapper
开始有点困难,它有一个非常紧凑的学习曲线,有时不容易理解做某事的最佳方法,或者如何做其他事情。因此,开始时需要准确阅读和理解手册
您可以使用以下设置根据需要配置映射:
Access level
Field matching
Naming convention
Name transformer
Name tokenizer
Matching strategy
默认配置只是最好的(),但是如果您想定制它,您可以这样做
只有一件事与匹配策略配置有关,我认为这是最重要的配置,需要小心使用。我会使用严格的
或标准的
,但从不使用松散的
,为什么
- 由于Loose是最灵活和智能的映射器,它可以映射一些您无法期望的属性。所以,确切地说,要小心。我认为最好创建自己的PropertyMap并在需要时使用转换器,而不是将其配置为松散的
验证
所有属性匹配非常重要,您需要验证它的所有功能,而使用ModelMapper,由于智能映射是通过反射完成的,因此您将不会获得编译器帮助,它将继续编译,但映射将在未实现的情况下失败。这是我最不喜欢的事情之一,但它需要避免样板和手动映射
最后,如果您确定要在项目中使用ModelMapper,您应该按照它建议的方式使用它,不要将它与手动映射混合使用(
DogInfo di = mm.map(dd, DogInfo.class);
mm.validate(); // make sure nothing in the destination is accidentally skipped
Access level
Field matching
Naming convention
Name transformer
Name tokenizer
Matching strategy
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class EntityDtoConversionUtil {
@Autowired
private ModelMapper modelMapper;
public Object convert(Object object,Class<?> type) {
Object MapperObject=modelMapper.map(object, type);
return MapperObject;
}
}
@Entity
class Student {
private Long id;
@OneToOne
@JoinColumn(name = "laptop_id")
private Laptop laptop;
}
class StudentDto {
private Long id;
private LaptopDto laptopDto;
}
private ModelMapper modelMapper;
public StudentController(ModelMapper modelMapper) {
this.modelMapper = modelMapper;
this.modelMapper.typeMap(Student.class, StudentDto.class).addMapping(Student::getLaptop, StudentDto::setLaptopDto);
}
modelMapper.map(student, studentDto);