对象映射框架MapStruct与orika

1.介绍

1.1 MapStruct

MapStruct 是一个 Java注释处理器,用于生成类型安全的 bean 映射类。

您所要做的就是定义一个映射器接口,该接口声明任何所需的映射方法。在编译期间,MapStruct 将生成该接口的实现。这个实现使用普通的Java 方法调用来映射源对象和目标对象,即没有反射或类似的。

与手工编写映射代码相比,MapStruct 通过生成繁琐且容易出错的代码来节省时间。遵循配置方法的约定,MapStruct 使用合理的默认值,但在配置或实现特殊行为时会采取措施。

与动态映射框架相比,MapStruct 具有以下优点:

  • 通过使用普通方法调用而不是反射来快速执行

  • 编译时类型安全:只能映射相互映射的对象和属性,不能将订单实体意外映射到客户 DTO 等。

  • 在构建时清除错误报告,如果

    • 映射不完整(并非所有目标属性都已映射)
    • 映射不正确(找不到合适的映射方法或类型转换)

    MapStruct 注解的关键词
    @Mapper 只有在接口加上这个注解, MapStruct 才会去实现该接口
    @Mapper 里有个 componentModel 属性,主要是指定实现类的类型,一般用到两个
    default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象
    spring:在接口的实现类上自动添加注解 @Component,可通过 @Autowired 方式注入
    @Mapping:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性
    source:源属性
    target:目标属性
    dateFormat:String 到 Date 日期之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat 的日期格式
    ignore: 忽略这个字段
    @Mappings:配置多个@Mapping
    @MappingTarget 用于更新已有对象
    @InheritConfiguration 用于继承配置

1.2Orika

Orika是java Bean映射框架,可以实现从一个对象递归拷贝数据至另一个对象。在开发多层应用程序中非常有用。在这些层之间交换数据时,通常为了适应不同API需要转换一个实例至另一个实例。

有很多方法可以实现:硬代码拷贝或Dozer实现bean映射等。总之,需要简化不同层对象之间映射过程。

Orika使用字节码生成器创建开销最小的快速映射,比其他基于反射方式实现(如,Dozer)更快。之前使用Bean Copy 性能非常慢,发现在这个领域业界还是有很多新秀的。 Orika 应该就算一个比较好的吧。

工具 实现方式 缺点 说明
mapstruct getter/setter方法 需要了解注解和配置项语法 JSR269注解处理器在编译期自动生成Java Bean转换代码,支持可配置化,扩展性强
orika 动态生成字节码 首次调用耗时较久,性能适中 采用javassist类库生成Bean映射的字节码,之后直接加载执行生成的字节码文件

2.MapStruct简单使用

官方文档: https://mapstruct.org/documentation/stable/reference/html/

案例教程:https://blog.csdn.net/Z143430039/article/details/111054479 https://blog.csdn.net/zhige_me/article/details/80699784

<properties><mapstruct.version>1.4.1.Final </mapstruct.version><lombok.version>1.18.12</lombok.version></properties><dependencies><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>${mapstruct.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version><scope>provided</scope></dependency><!--     commons工具--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version></dependency>
<!--     测试框架--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version></dependency></dependencies><!--     mapstruct和lombok整合配置--><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>8</source><target>8</target><encoding>UTF-8</encoding><annotationProcessorPaths><path><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${mapstruct.version}</version></path><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></path></annotationProcessorPaths></configuration></plugin></plugins></build>
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Person {private Long id;private String name;private String email;private Date birthday;private User user;
}@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {private Integer age;
}@NoArgsConstructor
@AllArgsConstructor
@Data
public class PersonDTO {private Long id;private String name;/*** 对应 Person.user.age*/private Integer age;private String email;/*** 与 DO 里面的字段名称(birthDay)不一致*/private Date birth;/*** 对 DO 里面的字段(birthDay)进行拓展,dateFormat 的形式*/private String birthDateFormat;/*** 对 DO 里面的字段(birthDay)进行拓展,expression 的形式*/private String birthExpressionFormat;
}
@Mapper
public interface PersonConverter {PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);@Mappings({@Mapping(source = "birthday", target = "birth"),@Mapping(source = "birthday", target = "birthDateFormat", dateFormat = "yyyy-MM-dd HH:mm:ss"),@Mapping(target = "birthExpressionFormat", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(person.getBirthday(),\"yyyy-MM-dd HH:mm:ss\"))"),@Mapping(source = "user.age", target = "age"),@Mapping(target = "email", ignore = true)})PersonDTO domain2dto(Person person);List<PersonDTO> domain2dto(List<Person> people);/***自定义实现(如果你想手写)*/default LocalDate map(LocalDateTime time) {return time.toLocalDate();}
}
@Generated(value = "org.mapstruct.ap.MappingProcessor",date = "2022-04-07T15:07:34+0800",comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_40 (Oracle Corporation)"
)
public class PersonConverterImpl implements PersonConverter {@Overridepublic PersonDTO domain2dto(Person person) {if ( person == null ) {return null;}PersonDTO personDTO = new PersonDTO();personDTO.setBirth( person.getBirthday() );if ( person.getBirthday() != null ) {personDTO.setBirthDateFormat( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format( person.getBirthday() ) );}personDTO.setAge( personUserAge( person ) );personDTO.setId( person.getId() );personDTO.setName( person.getName() );personDTO.setBirthExpressionFormat( org.apache.commons.lang3.time.DateFormatUtils.format(person.getBirthday(),"yyyy-MM-dd HH:mm:ss") );return personDTO;}@Overridepublic List<PersonDTO> domain2dto(List<Person> people) {if ( people == null ) {return null;}List<PersonDTO> list = new ArrayList<PersonDTO>( people.size() );for ( Person person : people ) {list.add( domain2dto( person ) );}return list;}private Integer personUserAge(Person person) {if ( person == null ) {return null;}User user = person.getUser();if ( user == null ) {return null;}Integer age = user.getAge();if ( age == null ) {return null;}return age;}
}

3.Orika简单使用

官方文档: http://orika-mapper.github.io/orika-docs/mappings-via-classmapbuilder.html

//处理不同属性的映射
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(User.class, UserVo.class)
.field("name", "userName")
.field("age", "ageOne")
.byDefault().register();
UserVo userVo = mapperFactory.getMapperFacade().map(User, UserVo.class);
     <dependency><groupId>ma.glasnost.orika</groupId><artifactId>orika-core</artifactId><version>1.4.6</version></dependency>

public enum MapperUtils {/*** 实例*/INSTANCE;/*** 默认字段工厂*/private static final MapperFactory MAPPER_FACTORY = new DefaultMapperFactory.Builder().build();/*** 默认字段实例*/private static final MapperFacade MAPPER_FACADE = MAPPER_FACTORY.getMapperFacade();/*** 默认字段实例集合*/private static Map<String, MapperFacade> CACHE_MAPPER_FACADE_MAP = new ConcurrentHashMap<>();/*** 映射实体(默认字段)** @param toClass 映射类对象* @param data    数据(对象)* @return 映射类对象*/public <E, T> E map(Class<E> toClass, T data) {return MAPPER_FACADE.map(data, toClass);}/*** 映射实体(自定义配置)** @param toClass   映射类对象* @param data      数据(对象)* @param configMap 自定义配置* @return 映射类对象*/public <E, T> E map(Class<E> toClass, T data, Map<String, String> configMap) {MapperFacade mapperFacade = this.getMapperFacade(toClass, data.getClass(), configMap);return mapperFacade.map(data, toClass);}/*** 映射集合(默认字段)** @param toClass 映射类对象* @param data    数据(集合)* @return 映射类对象*/public <E, T> List<E> mapAsList(Class<E> toClass, Collection<T> data) {return MAPPER_FACADE.mapAsList(data, toClass);}/*** 映射集合(自定义配置)** @param toClass   映射类* @param data      数据(集合)* @param configMap 自定义配置* @return 映射类对象*/public <E, T> List<E> mapAsList(Class<E> toClass, Collection<T> data, Map<String, String> configMap) {T t = data.stream().findFirst().orElseThrow(() -> new ExceptionInInitializerError("映射集合,数据集合为空"));MapperFacade mapperFacade = this.getMapperFacade(toClass, t.getClass(), configMap);return mapperFacade.mapAsList(data, toClass);}/*** 获取自定义映射** @param toClass   映射类* @param dataClass 数据映射类* @param configMap 自定义配置* @return 映射类对象*/private <E, T> MapperFacade getMapperFacade(Class<E> toClass, Class<T> dataClass, Map<String, String> configMap) {String mapKey = dataClass.getCanonicalName() + "_" + toClass.getCanonicalName();MapperFacade mapperFacade = CACHE_MAPPER_FACADE_MAP.get(mapKey);if (Objects.isNull(mapperFacade)) {MapperFactory factory = new DefaultMapperFactory.Builder().build();ClassMapBuilder classMapBuilder = factory.classMap(dataClass, toClass);configMap.forEach(classMapBuilder::field);classMapBuilder.byDefault().register();mapperFacade = factory.getMapperFacade();CACHE_MAPPER_FACADE_MAP.put(mapKey, mapperFacade);}return mapperFacade;}
}
    @Testpublic void test1() {Person person = new Person(1L,"zhige","zhige.me@gmail.com",new Date(),new User(1));//默认映射PersonDTO userDTO = MapperUtils.INSTANCE.map(PersonDTO.class, person);;//自定义映射Map<String, String> config = new HashMap<>();// 自定义配置(birthday 转 birth)config.put("birthday", "birth");config.put("user.age", "age");PersonDTO userDomain = MapperUtils.INSTANCE.map(PersonDTO.class, person, config);System.out.println(userDomain);//默认        PersonDTO(id=1, name=zhige, age=null, email=zhige.me@gmail.com, birth=null, birthDateFormat=null, birthExpressionFormat=null)
//加confi     PersonDTO(id=1, name=zhige, age=1, email=zhige.me@gmail.com, birth=Thu Apr 07 15:53:39 CST 2022, birthDateFormat=null, birthExpressionFormat=null)Person person2 = new Person(11L,"zhansan","zhansan.me@gmail.com",new Date(),new User(21));Person person3 = new Person(12L,"lisi","lisi.me@gmail.com",new Date(),new User(31));List<Person> userDOList =new ArrayList<>();userDOList.add(person);userDOList.add(person2);userDOList.add(person3);List<PersonDTO> userDTOS = MapperUtils.INSTANCE.mapAsList(PersonDTO.class,userDOList);List<PersonDTO> userDTOS1 = MapperUtils.INSTANCE.mapAsList(PersonDTO.class,userDOList,config);}

对象映射框架MapStruct与orika的简单使用相关推荐

  1. 可能是.NET领域性能最好的对象映射框架——Mapster

    我之前文章提到过 MediatR 的作者 Jimmy Bogard,他也是大名鼎鼎的对象映射框架 AutoMapper 的作者.AutoMapper 的功能强大,在 .NET 领域的开发者中有非常高的 ...

  2. 叮咚,Redis OM对象映射框架来了

    叮咚,Redis OM对象映射框架来了! 一.Redis OM来了 11月23日,redis宣布了四个新的Redis高级客户端的预览版,将这些库称为Redis OM(Redis Object Mapp ...

  3. 重磅发布:Redis 对象映射框架来了,操作大大简化!

    欢迎关注方志朋的博客,回复"666"获面试宝典 前言 最近,Redis 官方博客宣布推出四个用于 Redis 的客户端工具库:Redis OM(对象映射库,Object Mappi ...

  4. java的对象对象映射_Java对象到对象映射器

    java的对象对象映射 我在该项目上使用了Dozer一段时间. 但是,最近我遇到了一个非常有趣的错误,这促使我环顾四周,并尝试使用其他"对象到对象"映射器. 这是我找到的工具列表: ...

  5. Java对象到对象映射器

    我在该项目上使用了Dozer一段时间. 但是,最近我遇到了一个非常有趣的错误,它促使我环顾四周,并尝试使用其他"对象到对象"映射器. 这是我找到的工具列表: 推土机:推土机是Jav ...

  6. orika 映射非空字段_Orika:将JAXB对象映射到业务/域对象

    orika 映射非空字段 这篇文章着眼于使用Orika将JAXB对象映射到业务域对象. 本月初, 我使用基于反射的Dozer讨论 了相同的映射用例 . 在本文中,我假设需要映射相同的示例类,但是它们将 ...

  7. Orika:将JAXB对象映射到业务/域对象

    这篇文章着眼于使用Orika将JAXB对象映射到业务域对象. 本月初, 我使用基于反射的Dozer讨论 了相同的映射用例 . 在本文中,我假设需要映射相同的示例类,但是它们将使用Orika而不是Doz ...

  8. Java_JavaBean映射框架Orika

    简介 Orika是一个JavaBean映射框架,它递归地将数据从一个对象复制到另一个对象.它在开发多层应用程序时非常有用. Orika使用字节代码生成来创建开销最小的快速映射程序. <depen ...

  9. android对象关系映射框架ormlite之一对多(OneToMany)

    前两天,用ormlite对单张表进行了基本的操作,但是,我们知道通常情况对于单张表格进行操作在实际情况中很前两天不现实,那么ormlite能否像Hibenate那样实现多张表之间的一对多,多对多(即O ...

最新文章

  1. Win10 KB4541335 部分用户遭遇系统蓝屏/性能下降问题
  2. 机器学习系列(5)_从白富美相亲名单看特征选择与预处理(上)
  3. curl -windows下接口通讯
  4. silverlight2.0 demo实例,源码下载
  5. RedisDesktopManager的安装与使用
  6. Android 中发送短信
  7. JDBC学习笔记(查询SQL语句得到的结果对象)
  8. 为何控件删除不了_NBA开出800万合同,易建联为何却主动解约了?湖人太没诚意了...
  9. cacti-0.8.8a那点儿事
  10. 精心挑选的15款优秀 jQuery 文本特效插件和教程
  11. Pyqt5_QPushButton
  12. mysql数据库容灾方案_本地IDC机房数据库容灾解决方案
  13. 网页添加 Live2D 看板娘
  14. python如何设计一个排队系统
  15. 建站分享:调整WordPress自带标签云参数
  16. 深度学习之DCN-v2
  17. pcl点云特征提取 法线估计 PFH FPFH NARF 惯量偏心矩 RoPs特征 视点特征直方图VFH GASD特征
  18. 关于js中0==‘‘判断为True
  19. 权重衰减(L2正则化)的作用
  20. BUUCTF [GYCTF2020] Blacklist

热门文章

  1. delphi使用SQL的教程7
  2. Oracle:ORA-24247: 网络访问被访问控制列表 (ACL) 拒绝
  3. 语音备忘录怎么添加录音
  4. Vue2/3 脚手架搭建
  5. 【蓝桥杯每日一练】查找整数
  6. 用python来玩科学计算
  7. Maya动画——使用诺亦腾惯性动捕设备进行动作捕捉的方法
  8. linux下打补丁和生成补丁
  9. Java web video 视频开发
  10. 国内十大资质正规黄金交易平台排名(2023名单汇总)