属性映射工具——MapStruct(二)
目录:
写完第一篇,好长时间没写了。趁着今天有时间,继续总结一下吧。来吧,我们继续。今天我们看一个转化的例子,例子几乎涉及到了大部分情况。
一、目标及要求
先定义两个互相赋值的类People1,People2。我们的目的是把People1的属性赋值给People2。当然了在赋值的过程中有一些修改。先看看两个People的区别以及赋值的要求吧。
1) id——主键——属性相同,直接赋值;
2) name——名称——需要将name1赋值给name2;
3) no——编号——需要将no1赋值给no2;
4) age——年龄——不需要赋值,即people2不能有这个值;
5) height——身高——需要将“cm”为单位的height1转化为以“m”单位的height2;
6) weight——体重——需要将“kg”为单位的weight转化为以“斤”为单位的weight2,并且加上单位;
7) sex——性别——需要将枚举转化为对应的code;
8) createTime——创建时间——需要将Date类型的创建时间转化为字符串形式,并且是"yyyy-MM-dd HH:mm:ss:SSS"的形式;
9) bloodType——血型——不管任何人,血型都是“A型血”;
10) salary——工资——工资是Double类型的,需要转化为String类型,并且保留三位小数;
11) saseDough——私房钱(小金库)—私房钱市String类型的,需要转化为Integer类型的;
12) address——地址——people1里面的地址是一个对象(省+市+县+街道),但是people2里面只有省,所以需要将people1里面的对应的省给people2里面。
@Data @NoArgsConstructor @AllArgsConstructor public class People1 { //主键 private String id; //名称 private String name1; //编号 private String no1; //年龄 private String age1; //身高(cm) private Long height1; //体重(kg) private Integer weight1; //性别 private SexEnum sex1; //创建时间 private Date createTime1; //血型 private Character bloodType1; //工资卡 private Double salary1; //小金库 private String caseDough1; //地址 private Address address; }
@Data @NoArgsConstructor @AllArgsConstructor public class People2 { //主键 private String id; //名称 private String name2; //编号 private String no2; //年龄 private Integer age2; //身高(m) private Double height2; //体重(斤) private String weight2; //性别(1:男,0:女) private Integer sex2; //创建时间 private String createTime2; //血型 private String bloodType2; //工资卡 private String salary2; //小金库 private Integer caseDough2; //地址(所在省) private String province; }
辅助类:
@Data @NoArgsConstructor @AllArgsConstructor public class Address { //省 private String province; //市 private String city; //县 private String county; //街道 private String street; }
public enum SexEnum { MAN(1, "男"), WOMAN(0, "女"); private Integer code; private String desc; SexEnum(Integer code, String desc) { this.code = code; this.desc = desc; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } }
二、实现
以下是我们根据要求写的PeopleMapper转化接口。总之一句话,学习MapStruct就是看文档,然后看它生成的转化实体类,你就大彻大悟了。
1) id——主键——属性相同,直接赋值
属性名称、类型均相同,所以不需要特别指定。
2) name——名称——需要将name1赋值给name2;
属性类型相同。名称不同,需要指定——@Mapping(source = "name1", target = "name2")
3) no——编号——需要将no1赋值给no2;
类似name。属性类型相同。名称不同,需要指定——@Mapping(source = "no1", target = "no2")
4) age——年龄——不需要赋值;
增加了ignore参数,该参数默认是false,这里设置为true。所以忽略该参数的赋值——@Mapping(source = "age1", target = "age2", ignore = true)
5) height——身高——需要将“cm”为单位的height1转化为以“m”单位的height2;
a) 这是一个比较复杂的转化。我们使用了qualifiedByName。qualifiedByName的意思就是使用这个Mapper接口中的指定的默认方法去处理这个属性的转换,而不是简单的get set。可用于格式化小数位等,在po转换为vo时就已格式化小数位完成,所以不必单独再写代码处理小数位。
b) 我们在PeopleMapper接口里面定义了一个接口的默认方法heightConvert(default关键字开头的,只有java8及以上的版本支持,java8以下的版本,请参考weight的转化)。方法的名字是啥不重要,重要的是方法上面@Named()中的值。必须确保qualifiedByName的值和方法上加的@Named的值是一样的,这样才可以进行匹配。这里qualifiedByName的值是字符串。
6) weight——体重——需要将“kg”为单位的weight转化为以“斤”为单位的weight2;
这个转化类似与height的转化,但是我们采用了不同的方式
a) 我们没有在PeopleMapper中定义接口的默认方法。而是单独的创建了一个转化的类WeightMapper,并且定义了转化的方法weightConvert。同理,类的名称、方法的名称是啥不重要,重要的是@Named里面的值。
b) 我们的qualifiedByName的值不再是字符串,而是一个数组,数组中两个元素,第一个是类的Named值,第二个是方法的Named值。
c) 如果不采用单独写转化类。那么PeopleMapper上注解@Mapper就可以了。但是如果是单独写转化类的这种形式,必须在PeopleMapper的@Mapper中指定uses的值,uses的值是一个数组,里面是转化类的class对象,转化接口中你引用了几个,你就写几个,否则取不到值。
结果:@Mapping(source = "weight1", target = "weight2", qualifiedByName = {"WeightMapper", "WeightConvert"})
7) sex——性别——需要将枚举转化为对应的code;
这里我们直接用链式法则写了——@Mapping(source = "sex1.code",target = "sex2")
8) createTime——创建时间——需要将Date类型的创建时间转化为字符串形式,并且是"yyyy-MM-dd HH:mm:ss:SSS"的形式;
这里我们采用了@Mapping的新的属性dateFormat,值为SimpleDateFormat的值,看了它生成的源码,你就会有所发现了。但是dateFormat属性只能用于 Date 转 String 类型。
9) bloodType——血型——不管任何人,血型都是“A型血”;
同样的我们使用了@Mapping的新属性constant。但是这里需要注意的是,与其他属性不同,其他属性搭配的往往必须有source、target。但是constant搭配的属性只有target,意思是不管你原属性是啥样的,我最终的值是constant的值,和你无关。如果你搭配了source属性,会编译异常——Source and constant are both defined in @Mapping, either define a source or a constant。
10) salary——工资——工资是Double类型的,需要转化为String类型,并且保留三位小数;
使用了@Mapping的新属性numberFormat。number是使用DecimalFormat将一个Number(java里面所有的数值类都继承了这个抽象类)转化为String,或者将String转化为Number,其他的转化是不生效的。比如Double转Double,String转String等。所以你就知道了DecimalFormat怎么玩,你这里就怎么用。不清楚的看这里 java.text.DecimalFormat实战。
11) saseDough——私房钱(小金库)—私房钱市String类型的,需要转化为Integer类型的;
原理同salary。只不过是Double类型转化为Integer类型。
12) address——地址——people1里面的地址是一个对象(省+市+县+街道),但是people2里面只有省,所以需要将people1里面的对应的省给people2里面。
这里是使用了多参数转化,类似与sex的转换,但是稍微有所不同。大家可以类似的对照理解。
@Mapper(uses={WeightMapper.class}) public interface PeopleMapper { PeopleMapper INSTANCE = Mappers.getMapper(PeopleMapper.class); @Mappings({ @Mapping(source = "name1", target = "name2"), @Mapping(source = "no1", target = "no2"), @Mapping(source = "age1", target = "age2", ignore = true), @Mapping(source = "height1", target = "height2", qualifiedByName = "heightConvert"), @Mapping(source = "weight1", target = "weight2", qualifiedByName = {"WeightMapper", "WeightConvert"}), @Mapping(source = "sex1.code",target = "sex2") }) People2 toPeople2(People1 people1); @Named("heightConvert") default Double heightConvert(Long height) { return height * 1.0 / 100; } }
@Component @Named("WeightMapper") public class WeightMapper { @Named("WeightConvert") public String weightConvert(Integer weight){ //kg —— 斤 return (weight*2)+"斤"; } }
三、结果
编译后自动生成的PeopleMapper对应实现类PeopleMapperImpl。
import java.text.DecimalFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import javax.annotation.Generated; @Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2020-07-23T11:04:13+0800", comments = "version: 1.2.0.Final, compiler: javac, environment: Java 1.8.0_201 (Oracle Corporation)" ) public class PeopleMapperImpl implements PeopleMapper { private final WeightMapper weightMapper = new WeightMapper(); @Override public People2 toPeople2(People1 people1) { if ( people1 == null ) { return null; } People2 people2 = new People2(); people2.setNo2( people1.getNo1() ); people2.setHeight2( heightConvert( people1.getHeight1() ) ); String province = people1AddressProvince( people1 ); if ( province != null ) { people2.setProvince( province ); } Integer code = people1Sex1Code( people1 ); if ( code != null ) { people2.setSex2( code ); } if ( people1.getSalary1() != null ) { people2.setSalary2( new DecimalFormat( "0.000" ).format( people1.getSalary1() ) ); } try { if ( people1.getCaseDough1() != null ) { people2.setCaseDough2( new DecimalFormat( "0." ).parse( people1.getCaseDough1() ).intValue() ); } } catch ( ParseException e ) { throw new RuntimeException( e ); } people2.setName2( people1.getName1() ); people2.setWeight2( weightMapper.weightConvert( people1.getWeight1() ) ); if ( people1.getCreateTime1() != null ) { people2.setCreateTime2( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss:SSS" ).format( people1.getCreateTime1() ) ); } people2.setId( people1.getId() ); people2.setBloodType2( "A型血" ); return people2; } private String people1AddressProvince(People1 people1) { if ( people1 == null ) { return null; } Address address = people1.getAddress(); if ( address == null ) { return null; } String province = address.getProvince(); if ( province == null ) { return null; } return province; } private Integer people1Sex1Code(People1 people1) { if ( people1 == null ) { return null; } SexEnum sex1 = people1.getSex1(); if ( sex1 == null ) { return null; } Integer code = sex1.getCode(); if ( code == null ) { return null; } return code; } }
测试结果:
public class PeopleTest { /** * 输出结果: * People1(id=0001, name1=张三, no1=001, age1=18, height1=183, weight1=70, sex1=MAN, createTime1=Thu Jul 22 10:19:36 CST 2010, bloodType1=A, salary1=28.09888888888, caseDough1=1.08888888888, address=Address(province=安徽省, city=安庆市, county=太湖县, street=123街道)) * People2(id=0001, name2=张三, no2=001, age2=null, height2=1.83, weight2=140斤, sex2=1, createTime2=2010-07-22 10:19:36:000, bloodType2=A型血, salary2=28.099, caseDough2=1, province=安徽省) */ @Test public void test1() { People2 people2 = PeopleMapper.INSTANCE.toPeople2(setPeople1()); System.out.println(people2); } private People1 setPeople1() { People1 people1 = new People1(); people1.setId("0001"); people1.setName1("张三"); people1.setNo1("001"); people1.setAge1("18"); people1.setHeight1(183L); people1.setWeight1(70); people1.setSex1(SexEnum.MAN); people1.setCreateTime1(new Date(1279765176000L)); people1.setBloodType1('A'); people1.setSalary1(28.09888888888); people1.setCaseDough1("1.08888888888"); people1.setAddress(new Address("安徽省", "安庆市", "太湖县", "123街道")); System.out.println(people1); return people1; } }
如果你是从头至尾的看完这篇博文的话,相信你对mapStruct的印象更深了。好了,今天就到这里了。下期我们继续!!!