03-MapStruct-基本的映射方法
前面在00-MapStruct使用文档中,我们已经简单的介绍了如何使用MapStruct来进行类型转换。
本文主要介绍一些常用的映射方法。
1. 默认转换
对于Source类与Target类中相同名称的属性,如果它们的类型也相同,则该类属性是会默认转换的。即:我们在编写Mapper的时候,不用针对该类属性配置@Mapping注解。
下面给出具体的代码示例:
1.1 Source类定义
@Data public class Source {// 属性名 和 类型 都相同// 原生类型private int sameTypeSameName;// 属性名 和 类型 都相同// 原生类型的包装类型private Long longWrapper;// 属性名 和 类型 都相同// 字符串类型private String str;// 属性名 和 类型 都相同// 对象或引用类型private SomeObject obj;// 属性名 和 类型 都相同// 集合类型private List<SomeObject> objList;} @Data public class SomeObject {private int value;}1.2 Target类定义
@Data public class Target {// 属性名 和 类型 都相同// 原生类型private int sameTypeSameName;// 属性名 和 类型 都相同// 原生类型的包装类型private Long longWrapper;// 属性名 和 类型 都相同// 字符串类型private String str;// 属性名 和 类型 都相同// 对象或引用类型private SomeObject obj;// 属性名 和 类型 都相同// 集合类型private List<SomeObject> objList;}1.3 Mapper
@Mapper public interface SourceTargetMapper {SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);Target s2t(Source source);Source t2s(Target target);}这样就可以完成Source和Target对象之间的转换了。
1.4 测试
编写测试程序:
public class Main {public static void main(String[] args) {Long longWrapper = Long.valueOf(167989);SomeObject obj = new SomeObject();obj.setValue(17);Source source = new Source();source.setSameTypeSameName(3);source.setLongWrapper(longWrapper);source.setStr("Hello World");source.setObj(obj);source.setObjList(Arrays.asList(obj));Target target = SourceTargetMapper.INSTANCE.s2t(source);System.out.println("Target: " + target);System.out.println("包装类型是否相等:" + (source.getLongWrapper() == target.getLongWrapper()));System.out.println("String类型是否相等:" + (source.getStr() == target.getStr()));System.out.println("对象类型是否相等:" + (source.getObj() == target.getObj()));System.out.println("集合类型是否相等:" + (source.getObjList() == target.getObjList()));Source newSource = SourceTargetMapper.INSTANCE.t2s(target);System.out.println(newSource);}}程序的输出结果如下:
Target: Target(sameTypeSameName=3, longWrapper=167989, str=Hello World, obj=SomeObject(value=17), objList=[SomeObject(value=17)]) 包装类型是否相等:true String类型是否相等:true 对象类型是否相等:true 集合类型是否相等:false Source(sameTypeSameName=3, longWrapper=167989, str=Hello World, obj=SomeObject(value=17), objList=[SomeObject(value=17)])1.5 小结
生成的Mapper实现类的代码如下:
public class SourceTargetMapperImpl implements SourceTargetMapper {@Overridepublic Target s2t(Source source) {if ( source == null ) {return null;}Target target = new Target();target.setSameTypeSameName( source.getSameTypeSameName() );target.setLongWrapper( source.getLongWrapper() );target.setStr( source.getStr() );target.setObj( source.getObj() );// 集合类型的转换List<SomeObject> list = source.getObjList();if ( list != null ) {target.setObjList( new ArrayList<SomeObject>( list ) );}return target;}// 省略其他代码 ...... }总结:
2. 隐式转换
对于Source类与Target类中相同名称的属性,如果它们的类型不相同,但类型之间符合MapStruct支持的隐式类型转换规则,那么我们也是不需要对这类属性进行单独配置的。
下面给出具体的代码示例:
2.1 Source类定义
@Data public class Source {// 属性名相同,类型不相同// Source: int; Target: Integerprivate int sameName1;// 属性名相同,类型不相同// Source: int; Target: Longprivate int sameName2;// 属性名相同,类型不相同// Source: long; Target: Integer// 损失精度的转换private long sameName3;// 属性名相同,类型不相同// Source: double; Target: Stringprivate double sameName4;// 属性名相同,类型不相同// Source: SomeType(枚举); Target: Stringprivate SomeType type;// 属性名相同,类型不相同// Source: Date(日期); Target: Stringprivate Date date;// 属性名相同,类型不相同// Source: LocalDateTime(日期); Target: Stringprivate LocalDateTime localDateTime;// 属性名相同,类型不相同// Source: long; Target: BigIntegerprivate long count;// 属性名相同,类型不相同// Source: double; Target: BigDecimalprivate double price;} // 枚举类 public enum SomeType {A, B; }2.2 Target类定义
@Data public class Target {// 属性名相同,类型不相同// Source: int; Target: Integerprivate Integer sameName1;// 属性名相同,类型不相同// Source: int; Target: Longprivate Long sameName2;// 属性名相同,类型不相同// Source: long; Target: Integer// 损失精度的转换private Integer sameName3;// 属性名相同,类型不相同// Source: double; Target: Stringprivate String sameName4;// 属性名相同,类型不相同// Source: SomeType(枚举); Target: Stringprivate String type;// 属性名相同,类型不相同// Source: Date(日期); Target: Stringprivate String date;// 属性名相同,类型不相同// Source: LocalDateTime(日期); Target: Stringprivate String localDateTime;// 属性名相同,类型不相同// Source: long; Target: BigIntegerprivate BigInteger count;// 属性名相同,类型不相同// Source: double; Target: BigDecimalprivate BigDecimal price;}2.3 Mapper
@Mapper public interface SourceTargetMapper {SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);Target s2t(Source source);Source t2s(Target target);}这样就可以完成Source和Target对象之间的转换了。
2.4 测试
编写测试程序:
public class Main {public static void main(String[] args) {Source source = new Source();source.setSameName1(1);source.setSameName2(2);source.setSameName3(3L);source.setSameName4(4.56);source.setType(SomeType.A);source.setDate(new Date());source.setLocalDateTime(LocalDateTime.now());source.setCount(10L);source.setPrice(10.23);Target target = SourceTargetMapper.INSTANCE.s2t(source);System.out.println(target);Source newSource = SourceTargetMapper.INSTANCE.t2s(target);System.out.println(newSource);}}程序的输出结果如下:
Target(sameName1=1, sameName2=2, sameName3=3, sameName4=4.56, type=A, date=22-11-3 下午2:55, localDateTime=2022-11-03T14:55:51.635, count=10, price=10.23) Source(sameName1=1, sameName2=2, sameName3=3, sameName4=4.56, type=A, date=Thu Nov 03 14:55:00 CST 2022, localDateTime=2022-11-03T14:55:51.635, count=10, price=10.23)2.5 小结
生成的Mapper实现类的代码如下:
public class SourceTargetMapperImpl implements SourceTargetMapper {@Overridepublic Target s2t(Source source) {if ( source == null ) {return null;}Target target = new Target();target.setSameName1( source.getSameName1() );target.setSameName2( (long) source.getSameName2() );target.setSameName3( (int) source.getSameName3() );target.setSameName4( String.valueOf( source.getSameName4() ) );if ( source.getType() != null ) {target.setType( source.getType().name() );}if ( source.getDate() != null ) {target.setDate( new SimpleDateFormat().format( source.getDate() ) );}if ( source.getLocalDateTime() != null ) {target.setLocalDateTime( DateTimeFormatter.ISO_LOCAL_DATE_TIME.format( source.getLocalDateTime() ) );}target.setCount( BigInteger.valueOf( source.getCount() ) );target.setPrice( BigDecimal.valueOf( source.getPrice() ) );return target;}@Overridepublic Source t2s(Target target) {if ( target == null ) {return null;}Source source = new Source();if ( target.getSameName1() != null ) {source.setSameName1( target.getSameName1() );}if ( target.getSameName2() != null ) {source.setSameName2( target.getSameName2().intValue() );}if ( target.getSameName3() != null ) {source.setSameName3( target.getSameName3() );}if ( target.getSameName4() != null ) {source.setSameName4( Double.parseDouble( target.getSameName4() ) );}if ( target.getType() != null ) {source.setType( Enum.valueOf( SomeType.class, target.getType() ) );}try {if ( target.getDate() != null ) {source.setDate( new SimpleDateFormat().parse( target.getDate() ) );}}catch ( ParseException e ) {throw new RuntimeException( e );}if ( target.getLocalDateTime() != null ) {source.setLocalDateTime( LocalDateTime.parse( target.getLocalDateTime() ) );}if ( target.getCount() != null ) {source.setCount( target.getCount().longValue() );}if ( target.getPrice() != null ) {source.setPrice( target.getPrice().doubleValue() );}return source;} }从代码实现上,我们可以看到:MapStruct支持很多类型之间的隐式转换。例如:
-
Java原生数据类型 <-> 相应的包装类型,例如:int -> Integer、boolean -> Boolean
-
注意:包装类型 -> 原生类型之前,是会有null值判断的;
-
-
Java原生数值类型之间,原生数值类型 <-> 数值包装类型,例如:int -> long、int -> Long
-
Java原生数据类型 or 包装类型 <-> String,例如:double -> String、Double -> String
-
枚举 <-> String
-
日期 <-> String,例如:Date -> String、LocalDateTime -> String
-
Date <-> String之间的转换,是使用SimpleDateFormat进行转换的;
-
LocalDateTime -> String之间的转换,是使用DateTimeFormatter进行转换的;
-
更多隐式的类型转换,可参考官方文档:MapStruct支持的隐式类型转换
2.6 扩展
对于Java原生数据类型 or 包装类型 <-> String之间的转换,我们可以自定义其转换的格式。
当我们指定了转换的格式后,MapStruct会使用DecimalFormat进行类型之间转换。
更多DecimalFormat的用法可自行百度。这里仅帖个链接:DecimalFormat用法举例
@Mapper public interface SourceTargetMapper {SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);// numberFormat 指定了转换后的字符串格式@Mapping(target = "sameName4", numberFormat = "#.##元")Target s2t(Source source);// @InheritInverseConfiguration 注解表示 继承并反转Target s2t(Source source)的转换规则@InheritInverseConfigurationSource t2s(Target target);}上面测试程序的输入如下:
Target(sameName1=1, sameName2=2, sameName3=3, sameName4=4.56元, type=A, date=22-11-3 下午3:26, localDateTime=2022-11-03T15:26:18.641, count=10, price=10.23) Source(sameName1=1, sameName2=2, sameName3=3, sameName4=4.56, type=A, date=Thu Nov 03 15:26:00 CST 2022, localDateTime=2022-11-03T15:26:18.641, count=10, price=10.23)对于日期 <-> String之间的转换,我们可以自定义其转换的格式。
@Mapper public interface SourceTargetMapper {SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);// dateFormat 指定了日期转换成字符串后的格式@Mapping(target = "sameName4", numberFormat = "这是格式化的数字#.##")@Mapping(target = "date", dateFormat = "yyyy-MM-dd")@Mapping(target = "localDateTime", dateFormat = "yyyy-MM-dd HH:mm:ss")Target s2t(Source source);// @InheritInverseConfiguration 注解表示 继承并反转Target s2t(Source source)的转换规则@InheritInverseConfigurationSource t2s(Target target);}上面测试程序的输入如下:
Target(sameName1=1, sameName2=2, sameName3=3, sameName4=这是格式化的数字4.56, type=A, date=2022-11-03, localDateTime=2022-11-03 15:29:29, count=10, price=10.23) Source(sameName1=1, sameName2=2, sameName3=3, sameName4=4.56, type=A, date=Thu Nov 03 00:00:00 CST 2022, localDateTime=2022-11-03T15:29:29, count=10, price=10.23)这里贴一下Mapper实现类的代码,大家可以结合代码理解下。
public class SourceTargetMapperImpl implements SourceTargetMapper {private final DateTimeFormatter dateTimeFormatter_yyyy_MM_dd_HH_mm_ss_11333195168 = DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" );@Overridepublic Target s2t(Source source) {if ( source == null ) {return null;}Target target = new Target();target.setSameName4( new DecimalFormat( "这是格式化的数字#.##" ).format( source.getSameName4() ) );if ( source.getDate() != null ) {target.setDate( new SimpleDateFormat( "yyyy-MM-dd" ).format( source.getDate() ) );}if ( source.getLocalDateTime() != null ) {target.setLocalDateTime( dateTimeFormatter_yyyy_MM_dd_HH_mm_ss_11333195168.format( source.getLocalDateTime() ) );}target.setSameName1( source.getSameName1() );target.setSameName2( (long) source.getSameName2() );target.setSameName3( (int) source.getSameName3() );if ( source.getType() != null ) {target.setType( source.getType().name() );}target.setCount( BigInteger.valueOf( source.getCount() ) );target.setPrice( BigDecimal.valueOf( source.getPrice() ) );return target;}@Overridepublic Source t2s(Target target) {if ( target == null ) {return null;}Source source = new Source();try {if ( target.getSameName4() != null ) {source.setSameName4( new DecimalFormat( "这是格式化的数字#.##" ).parse( target.getSameName4() ).doubleValue() );}}catch ( ParseException e ) {throw new RuntimeException( e );}try {if ( target.getDate() != null ) {source.setDate( new SimpleDateFormat( "yyyy-MM-dd" ).parse( target.getDate() ) );}}catch ( ParseException e ) {throw new RuntimeException( e );}if ( target.getLocalDateTime() != null ) {source.setLocalDateTime( LocalDateTime.parse( target.getLocalDateTime(), dateTimeFormatter_yyyy_MM_dd_HH_mm_ss_11333195168 ) );}if ( target.getSameName1() != null ) {source.setSameName1( target.getSameName1() );}if ( target.getSameName2() != null ) {source.setSameName2( target.getSameName2().intValue() );}if ( target.getSameName3() != null ) {source.setSameName3( target.getSameName3() );}if ( target.getType() != null ) {source.setType( Enum.valueOf( SomeType.class, target.getType() ) );}if ( target.getCount() != null ) {source.setCount( target.getCount().longValue() );}if ( target.getPrice() != null ) {source.setPrice( target.getPrice().doubleValue() );}return source;} }3. 通过@Mapping指定转换规则
当Source类和Target类中,对应的字段名不一样时,我们需要告诉MapStruct,哪两个属性之间是对应的。我们可以通过@Mapping注解指定属性之间的对应关系。
3.1 Source类定义
@Data public class Source {// Source: s1; Target: t1private int s1;// Source: s2; Target: t2private double s2;// Source: s3; Target: t3private Date s3;// Source: s4; Target: t4private SomeType s4;// Source: s5; Target: t5private SomeSourceObject s5;} public enum SomeType {A, B;} @Data public class SomeSourceObject {private int value;}3.2 Target类定义
@Data public class Target {// Source: s1; Target: t1private int t1;// Source: s2; Target: t2private String t2;// Source: s3; Target: t3private String t3;// Source: s4; Target: t4private String t4;// Source: s5; Target: t5private SomeSourceObject t5;// Source: SomeSourceObject s5; Target: SomeTargetObject t6private SomeTargetObject t6;} @Data public class SomeTargetObject {private int targetValue;}3.3 Mapper
上述代码的注释中已经指定了Source与Target中属性的对应关系。
我们可以通过@Mapping注解的source()指定源类型中的属性名,target()指定目标类型中的属性名。
@Mapper public interface SourceTargetMapper {SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);@Mapping(source = "s1", target = "t1")@Mapping(source = "s2", target = "t2", numberFormat = "格式化的数字#.##")@Mapping(source = "s3", target = "t3", dateFormat = "yyyy-MM-dd")@Mapping(source = "s4", target = "t4")@Mapping(source = "s5", target = "t5")@Mapping(source = "s5", target = "t6")Target s2t(Source source);/*** 注解@InheritInverseConfiguration表示:继承并反转Target s2t(Source source)的转换规则** @param target* @return*/@InheritInverseConfigurationSource t2s(Target target);/*** 用于转换 SomeSourceObject -> SomeTargetObject* @param some* @return*/@Mapping(source = "value", target = "targetValue")SomeTargetObject someS2t(SomeSourceObject some);@InheritInverseConfigurationSomeSourceObject someT2s(SomeTargetObject some);}对于Source类中的属性s5 与 Target类中的属性t5,MapStruct直接使用值拷贝的方式进行转换;
对于Source类中的属性s5 与 Target类中的属性t6,由于它们是对象类型,并且类型是不同的,MapStruct是不知道它们之间是如何转换的,所以我们需要单独定义它们之间的转换规则,如:
-
方法SomeTargetObject someS2t(SomeSourceObject some)
-
方法SomeSourceObject someT2s(SomeTargetObject some)
3.4 测试
编写测试程序:
public class Main {public static void main(String[] args) {SomeSourceObject someObj = new SomeSourceObject();someObj.setValue(123);Source source = new Source();source.setS1(1);source.setS2(5.46);source.setS3(new Date());source.setS4(SomeType.A);source.setS5(someObj);Target target = SourceTargetMapper.INSTANCE.s2t(source);System.out.println(target);Source newSource = SourceTargetMapper.INSTANCE.t2s(target);System.out.println(newSource);}}程序的输出结果如下:
Target(t1=1, t2=格式化的数字5.46, t3=2022-11-03, t4=A, t5=SomeSourceObject(value=123), t6=SomeTargetObject(targetValue=123)) Source(s1=1, s2=5.46, s3=Thu Nov 03 00:00:00 CST 2022, s4=A, s5=SomeSourceObject(value=123))3.5 小结
生成的Mapper实现类的代码如下:
public class SourceTargetMapperImpl implements SourceTargetMapper {@Overridepublic Target s2t(Source source) {if ( source == null ) {return null;}Target target = new Target();target.setT1( source.getS1() );target.setT2( new DecimalFormat( "格式化的数字#.##" ).format( source.getS2() ) );if ( source.getS3() != null ) {target.setT3( new SimpleDateFormat( "yyyy-MM-dd" ).format( source.getS3() ) );}if ( source.getS4() != null ) {target.setT4( source.getS4().name() );}target.setT5( source.getS5() );target.setT6( someS2t( source.getS5() ) );return target;}@Overridepublic Source t2s(Target target) {if ( target == null ) {return null;}Source source = new Source();source.setS1( target.getT1() );try {if ( target.getT2() != null ) {source.setS2( new DecimalFormat( "格式化的数字#.##" ).parse( target.getT2() ).doubleValue() );}}catch ( ParseException e ) {throw new RuntimeException( e );}try {if ( target.getT3() != null ) {source.setS3( new SimpleDateFormat( "yyyy-MM-dd" ).parse( target.getT3() ) );}}catch ( ParseException e ) {throw new RuntimeException( e );}if ( target.getT4() != null ) {source.setS4( Enum.valueOf( SomeType.class, target.getT4() ) );}source.setS5( target.getT5() );return source;}@Overridepublic SomeTargetObject someS2t(SomeSourceObject some) {if ( some == null ) {return null;}SomeTargetObject someTargetObject = new SomeTargetObject();someTargetObject.setTargetValue( some.getValue() );return someTargetObject;}@Overridepublic SomeSourceObject someT2s(SomeTargetObject some) {if ( some == null ) {return null;}SomeSourceObject someSourceObject = new SomeSourceObject();someSourceObject.setValue( some.getTargetValue() );return someSourceObject;} }总结
以上是生活随笔为你收集整理的03-MapStruct-基本的映射方法的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 2022北京工业互联网安全大赛初赛-wa
- 下一篇: 基于YOLOv4的交通视频监控车辆识别