menudo:MapStruct 总结


title: MapStruct 总结
date: 2022-03-22 13:51:27
tags:

  • Java
    categories:
  • 开发技术及框架
    cover: https://cover.png
    feature: false

1. 概念

在系统工程开发过程中,会有各个层之间的对象转换,比如 VO、DTO、PO、DO 等,如果都是手动 setter、getter 特别浪费时间,还可能操作错误,所以选择一个自动化工具会更加方便。常见的有 json2Json、Apache 和 Spring 的 BeanUtils.copyProperties()、BeanCopier、JMapper 等

对象属性转换的操作无非是基于反射、AOP、CGlib、ASM、Javassist 在编译时和运行期进行处理,再有好的思路就是在编译前生成出对应的 setter、getter,就像手写出来的一样。MapStruct 就是直接在编译期生成对应的 setter、getter,性能更好、使用方便

2. POM

导入依赖

<dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>1.5.0.RC1</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.5.0.RC1</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-jdk8</artifactId><version>1.5.0.RC1</version></dependency>

可以选择在 IDEA 中下载其插件

3. 实体类

定义几个 DO 和 DTO

// UserDO@Data@Builderpublic class UserDO { private Integer id; private String name; private Integer age; private String address; private String birthday; private String phone; private String userDOGender; private Date createTime; private Map<String, String> map; private List<PersonDO> personList;}// UserDTO@Data@Builderpublic class UserDTO { private Integer id; private String name; private Integer age; private String address; private String userDTOGender; private Date createTime; private Map<String, String> map; private List<PersonDO> personList;}// PersonDO@Data@Builderpublic class PersonDO { private Integer id; private String name; private String job; private String remark; private Date createTime;}// PersonDTO@Data@Builderpublic class PersonDTO { private Integer id; private String name; private String job;}

3. 映射器

  • 如果 DTO 和实体类中的字段名称是一致的,只需要写方法签名即可
  • 如果参数名称有变化,需要使用 @Maping 注解,source 为原参数名称,target 为转换后的类的参数名称
  • 隐式类型转换
    在许多情况下,MapStruct 会自动处理类型转换。如在 source 中是 int 类型但在 target 中是 String 类型,会自动进行转换。以及所有 Java 基本数据类型及其相应的包装类型,如 int 和 Integer,boolean 和 Boolean 等。还有所有 Java Number类型和包装器类型之间,如 int 和 long 或 byte 和 Integer
@Mapper // 直接使用@Mapper(componentModel = "spring") // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可public interface MapStruct {MapStruct INSTANCE = Mappers.getMapper(MapStruct.class); @Mapping(source = "userDOGender", target = "userDTOGender") UserDTO userDOToUserDTO(UserDO userDO); @Mapping(source = "userDTOGender", target = "userDOGender") UserDO UserDTOToUserDO(UserDTO userDTO); PersonDTO personDOToPersonDTO(PersonDO personDO); PersonDO PersonDTOToPersonDO(PersonDTO personDTO);}

编译后,会在同级目录生成实现类:

@Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2022-03-22T12:16:06+0800", comments = "version: 1.5.0.RC1, compiler: javac, environment: Java 1.8.0_131 (Oracle Corporation)")public class MapStructImpl implements MapStruct { @Override public UserDTO userDOToUserDTO(UserDO userDO) { if ( userDO == null ) { return null; } UserDTO.UserDTOBuilder userDTO = UserDTO.builder(); userDTO.userDTOGender( userDO.getUserDOGender() ); userDTO.id( userDO.getId() ); userDTO.name( userDO.getName() ); userDTO.age( userDO.getAge() ); userDTO.address( userDO.getAddress() ); userDTO.createTime( userDO.getCreateTime() ); Map<String, String> map = userDO.getMap(); if ( map != null ) { userDTO.map( new LinkedHashMap<String, String>( map ) ); } List<PersonDO> list = userDO.getPersonList(); if ( list != null ) { userDTO.personList( new ArrayList<PersonDO>( list ) ); } return userDTO.build(); } @Override public UserDO UserDTOToUserDO(UserDTO userDTO) { if ( userDTO == null ) { return null; } UserDO.UserDOBuilder userDO = UserDO.builder(); userDO.userDOGender( userDTO.getUserDTOGender() ); userDO.id( userDTO.getId() ); userDO.name( userDTO.getName() ); userDO.age( userDTO.getAge() ); userDO.address( userDTO.getAddress() ); userDO.createTime( userDTO.getCreateTime() ); Map<String, String> map = userDTO.getMap(); if ( map != null ) { userDO.map( new LinkedHashMap<String, String>( map ) ); } List<PersonDO> list = userDTO.getPersonList(); if ( list != null ) { userDO.personList( new ArrayList<PersonDO>( list ) ); } return userDO.build(); } @Override public PersonDTO personDOToPersonDTO(PersonDO personDO) { if ( personDO == null ) { return null; } PersonDTO.PersonDTOBuilder personDTO = PersonDTO.builder(); personDTO.id( personDO.getId() ); personDTO.name( personDO.getName() ); personDTO.job( personDO.getJob() ); return personDTO.build(); } @Override public PersonDO PersonDTOToPersonDO(PersonDTO personDTO) { if ( personDTO == null ) { return null; } PersonDO.PersonDOBuilder personDO = PersonDO.builder(); personDO.id( personDTO.getId() ); personDO.name( personDTO.getName() ); personDO.job( personDTO.getJob() ); return personDO.build(); }}

4. 测试

public class Test { public static void main(String[] args) { UserDO userDO = UserDO.builder().id(1) .name("张三") .age(18) .birthday("2003-01-04") .phone("12343") .userDOGender("男") .createTime(new Date(System.currentTimeMillis())) .map(new HashMap<String, String>(){ { this.put("key", "value"); } }) .personList(new ArrayList<PersonDO>(){ { this.add(PersonDO.builder().id(11).build()); } }).build(); UserDTO userDTO = Mappers.getMapper(MapStruct.class).userDOToUserDTO(userDO); System.out.println(userDTO); PersonDTO personDTO = PersonDTO.builder().id(11) .name("李四") .job("开发").build(); PersonDO personDO = MapStruct.INSTANCE.PersonDTOToPersonDO(personDTO); System.out.println(personDO); }}

5. 自定义转换规则

MapStruct 只提供了隐式类型转换和默认的一些转换,假如需要特定的类型转换,如将 java.sql.Timestamp 日期转换为只保留年月日的字符串等,可以自定义转换规则

5.1 使用 expression 表达式

1、创建一个转换规则类

public class MapStructRule { public static String toDate(Timestamp date) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); return simpleDateFormat.format(date); }}

2、在映射器对应的转换方法上使用 @Mapping 注解的 expression 属性标识转换规则,这里的属性值为需要写 全类名.方法名(参数),使用了 expression 就不需要 source 属性

@Mapper(componentModel = "spring") // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可public interface MapStruct { MapStruct INSTANCE = Mappers.getMapper(MapStruct.class); @Mapping(target = "createTime", expression = "java(fan.fanblog.utils.MapStructRule.toDate(menuDO.getCreateTime()))") @Mapping(target = "updateTime", expression = "java(fan.fanblog.utils.MapStructRule.toDate(menuDO.getUpdateTime()))") MenuVO MenuDOToMenuVO(MenuDO menuDO);}

3、测试类

public class Demo { public static void main(String[] args) { MenuDO menuDO = new MenuDO(); menuDO.setCreateTime(new Timestamp(System.currentTimeMillis())); menuDO.setUpdateTime(new Timestamp(System.currentTimeMillis())); System.out.println(menuDO); MenuVO menuVO = MapStruct.INSTANCE.MenuDOToMenuVO(menuDO); System.out.println(menuVO); }}

5.2 使用 @Named 注解

1、创建一个转换规则类,在对应的转换方法上标注 @Named 注解表示转换方法名

public class MapStructRule { @Named("toDate") public static String toDate(Timestamp date) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return simpleDateFormat.format(date); }}

2、在映射器上使用 @Mapper 的 uses 属性,属性值为转换规则类的 Class 对象,然后再映射器对应的方法上使用 @Mapping 注解的 qualifiedByName 属性,属性值为转换规则类 @Named 定义的方法名

@Mapper(componentModel = "spring", uses = MapStructRule.class) // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可public interface MapStruct { MapStruct INSTANCE = Mappers.getMapper(MapStruct.class); @Mapping(target = "createTime", source = "createTime", qualifiedByName = "toDate") @Mapping(target = "updateTime", source = "updateTime", qualifiedByName = "toDate") MenuVO MenuDOToMenuVO(MenuDO menuDO);}

3、测试类

public class Demo { public static void main(String[] args) { MenuDO menuDO = new MenuDO(); menuDO.setCreateTime(new Timestamp(System.currentTimeMillis())); menuDO.setUpdateTime(new Timestamp(System.currentTimeMillis())); System.out.println(menuDO); MenuVO menuVO = MapStruct.INSTANCE.MenuDOToMenuVO(menuDO); System.out.println(menuVO); }}

6. 忽略属性不转换

@Mapper(componentModel = "spring", uses = MapStructRule.class) // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可public interface MapStruct { MapStruct INSTANCE = Mappers.getMapper(MapStruct.class); @Mapping(target = "createTime", ignore = true) @Mapping(target = "updateTime", ignore = true) MenuDO MenuVOToMenuDO(MenuVO menuVO);}

相关推荐

相关文章