目录
常见Bean映射框架
Dozer
Orika
MapStruct
ModelMapper
JMapper
测试模型
转化器
OrikaConverter
DozerConverter
MapperStructConvert
JMapperConvert
ModelMapperConverter
测试
平均时间
吞吐量
SingleShotTime
采集时间
DTO(Data Transfer Object,数据传输对象)
PO(Presistent Object,持久化对象)
常见Bean映射框架
推荐使用MapStruct
Dozer
Dozer 是一个映射框架,它使用递归将数据从一个对象复制到另一个对象。框架不仅能够在 bean 之间复制属性,还能够在不同类型之间自动转换。
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.5.1</version>
</dependency>
更多关于 Dozer 的内容可以在官方文档中找到: Dozer - Usage ,或者你也可以阅读这篇文章:A Guide to Mapping With Dozer | Baeldung 。
Orika
Orika 是一个 bean 到 bean 的映射框架,它递归地将数据从一个对象复制到另一个对象。
Orika 的工作原理与 Dozer 相似。两者之间的主要区别是 Orika 使用字节码生成。这允许以最小的开销生成更快的映射器。
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.5.2</version>
</dependency>
更多关于 Orika 的内容可以在官方文档中找到:https://orika-mapper.github.io/orika-docs/,或者你也可以阅读这篇文章:https://www.baeldung.com/orika-mapping。
MapStruct
MapStruct 是一个自动生成 bean mapper 类的代码生成器。MapStruct 还能够在不同的数据类型之间进行转换。Github 地址:https://github.com/mapstruct/mapstruct。
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.2.0.Final</version>
</dependency>
ModelMapper
ModelMapper 是一个旨在简化对象映射的框架,它根据约定确定对象之间的映射方式。它提供了类型安全的和重构安全的 API。
更多关于 ModelMapper 的内容可以在官方文档中找到:ModelMapper - Simple, Intelligent, Object Mapping. 。
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>1.1.0</version>
</dependency>
JMapper
JMapper 是一个映射框架,旨在提供易于使用的、高性能的 Java bean 之间的映射。该框架旨在使用注释和关系映射应用 DRY 原则。该框架允许不同的配置方式:基于注释、XML 或基于 api。
更多关于 JMapper 的内容可以在官方文档中找到:https://github.com/jmapper-framework/jmapper-core/wiki。
<dependency>
<groupId>com.googlecode.jmapper-framework</groupId>
<artifactId>jmapper-core</artifactId>
<version>1.6.0.1</version>
</dependency>
测试模型
简单模型如下:
public class SourceCode {
String code;
// getter and setter
}
public class DestinationCode {
String code;
// getter and setter
}
bean 示例如下:
public class SourceOrder {
private String orderFinishDate;
private PaymentType paymentType;
private Discount discount;
private DeliveryData deliveryData;
private User orderingUser;
private List<Product> orderedProducts;
private Shop offeringShop;
private int orderId;
private OrderStatus status;
private LocalDate orderDate;
// standard getters and setters
}
public class Order {
private User orderingUser;
private List<Product> orderedProducts;
private OrderStatus orderStatus;
private LocalDate orderDate;
private LocalDate orderFinishDate;
private PaymentType paymentType;
private Discount discount;
private int shopId;
private DeliveryData deliveryData;
private Shop offeringShop;
// standard getters and setters
}
转化器
为了简化测试设置的设计,我们创建了如下所示的转换器接口:
public interface Converter {
Order convert(SourceOrder sourceOrder);
DestinationCode convert(SourceCode sourceCode);
}
OrikaConverter
Orika 支持完整的 API 实现,这大大简化了 mapper 的创建:
public class OrikaConverter implements Converter{
private MapperFacade mapperFacade;
public OrikaConverter() {
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(Order.class, SourceOrder.class)
.field("orderStatus", "status").byDefault().register();
mapperFacade = mapperFactory.getMapperFacade();
} @Override
public Order convert(SourceOrder sourceOrder) {
return mapperFacade.map(sourceOrder, Order.class);
} @Override
public DestinationCode convert(SourceCode sourceCode) {
return mapperFacade.map(sourceCode, DestinationCode.class);
}
}
DozerConverter
Dozer 需要 XML 映射文件
<mappings xmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net
http://dozer.sourceforge.net/schema/beanmapping.xsd">
<mapping>
<class-a>com.baeldung.performancetests.model.source.SourceOrder</class-a>
<class-b>com.baeldung.performancetests.model.destination.Order</class-b>
<field>
<a>status</a>
<b>orderStatus</b>
</field>
</mapping>
<mapping>
<class-a>com.baeldung.performancetests.model.source.SourceCode</class-a>
<class-b>com.baeldung.performancetests.model.destination.DestinationCode</class-b>
</mapping>
</mappings>
public class DozerConverter implements Converter {
private final Mapper mapper;
public DozerConverter() {
DozerBeanMapper mapper = new DozerBeanMapper();
mapper.addMapping(
DozerConverter.class.getResourceAsStream("/dozer-mapping.xml"));
this.mapper = mapper;
} @Override
public Order convert(SourceOrder sourceOrder) {
return mapper.map(sourceOrder,Order.class);
} @Override
public DestinationCode convert(SourceCode sourceCode) {
return mapper.map(sourceCode, DestinationCode.class);
}
}
MapperStructConvert
MapStruct 结构的定义非常简单,因为它完全基于代码生成:
@Mapper
public interface MapStructConverter extends Converter {
MapStructConverter MAPPER = Mappers.getMapper(MapStructConverter.class);
@Mapping(source = "status", target = "orderStatus")
@Override
Order convert(SourceOrder sourceOrder);
@Override
DestinationCode convert(SourceCode sourceCode);
}
更复杂的属性转换,参考:
Mapstruct自定义转换规则_好奇的菜鸟的博客-CSDN博客_mapstruct自定义转换
【java】mapstruct自定义类的转换示例_王佑辉的博客-CSDN博客_mapstruct自定义转换
JMapperConvert
public class JMapperConverter implements Converter {
JMapper realLifeMapper;
JMapper simpleMapper;
public JMapperConverter() {
JMapperAPI api = new JMapperAPI()
.add(JMapperAPI.mappedClass(Order.class));
realLifeMapper = new JMapper(Order.class, SourceOrder.class, api);
JMapperAPI simpleApi = new JMapperAPI()
.add(JMapperAPI.mappedClass(DestinationCode.class));
simpleMapper = new JMapper(
DestinationCode.class, SourceCode.class, simpleApi);
}
@Override
public Order convert(SourceOrder sourceOrder) {
return (Order) realLifeMapper.getDestination(sourceOrder);
}
@Override
public DestinationCode convert(SourceCode sourceCode) {
return (DestinationCode) simpleMapper.getDestination(sourceCode);
}
}
我们还需要向目标类的每个字段添加@JMap
注释。此外,JMapper 不能在 enum 类型之间转换,它需要我们创建自定义映射函数:
@JMapConversion(from = "paymentType", to = "paymentType")
public PaymentType conversion(com.baeldung.performancetests.model.source.PaymentType type) {
PaymentType paymentType = null;
switch(type) {
case CARD:
paymentType = PaymentType.CARD;
break; case CASH:
paymentType = PaymentType.CASH;
break; case TRANSFER:
paymentType = PaymentType.TRANSFER;
break;
}
return paymentType;
}
ModelMapperConverter
public class ModelMapperConverter implements Converter {
private ModelMapper modelMapper;
public ModelMapperConverter() {
modelMapper = new ModelMapper();
}
@Override
public Order convert(SourceOrder sourceOrder) {
return modelMapper.map(sourceOrder, Order.class);
}
@Override
public DestinationCode convert(SourceCode sourceCode) {
return modelMapper.map(sourceCode, DestinationCode.class);
}
}
测试
平均时间
JMH 返回以下平均运行时间结果(越少越好):
吞吐量
在这种模式下,基准测试返回每秒的操作数。我们收到以下结果(越多越好):
SingleShotTime
这种模式允许测量单个操作从开始到结束的时间。基准给出了以下结果(越少越好):
采集时间
这种模式允许对每个操作的时间进行采样。三个不同百分位数的结果如下: