03-MapStruct-基本的映射方法
前面在00-MapStruct使用文檔中,我們已經(jīng)簡單的介紹了如何使用MapStruct來進(jìn)行類型轉(zhuǎn)換。
本文主要介紹一些常用的映射方法。
1. 默認(rèn)轉(zhuǎn)換
對于Source類與Target類中相同名稱的屬性,如果它們的類型也相同,則該類屬性是會默認(rèn)轉(zhuǎn)換的。即:我們在編寫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對象之間的轉(zhuǎn)換了。
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);}}程序的輸出結(jié)果如下:
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 小結(jié)
生成的Mapper實(shí)現(xiàn)類的代碼如下:
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() );// 集合類型的轉(zhuǎn)換List<SomeObject> list = source.getObjList();if ( list != null ) {target.setObjList( new ArrayList<SomeObject>( list ) );}return target;}// 省略其他代碼 ...... }總結(jié):
2. 隱式轉(zhuǎn)換
對于Source類與Target類中相同名稱的屬性,如果它們的類型不相同,但類型之間符合MapStruct支持的隱式類型轉(zhuǎn)換規(guī)則,那么我們也是不需要對這類屬性進(jìn)行單獨(dú)配置的。
下面給出具體的代碼示例:
2.1?Source類定義
@Data public class Source {// 屬性名相同,類型不相同// Source: int; Target: Integerprivate int sameName1;// 屬性名相同,類型不相同// Source: int; Target: Longprivate int sameName2;// 屬性名相同,類型不相同// Source: long; Target: Integer// 損失精度的轉(zhuǎn)換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// 損失精度的轉(zhuǎn)換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對象之間的轉(zhuǎn)換了。
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);}}程序的輸出結(jié)果如下:
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 小結(jié)
生成的Mapper實(shí)現(xiàn)類的代碼如下:
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;} }從代碼實(shí)現(xiàn)上,我們可以看到:MapStruct支持很多類型之間的隱式轉(zhuǎn)換。例如:
-
Java原生數(shù)據(jù)類型 <-> 相應(yīng)的包裝類型,例如:int -> Integer、boolean -> Boolean
-
注意:包裝類型 -> 原生類型之前,是會有null值判斷的;
-
-
Java原生數(shù)值類型之間,原生數(shù)值類型 <-> 數(shù)值包裝類型,例如:int -> long、int -> Long
-
Java原生數(shù)據(jù)類型 or 包裝類型 <-> String,例如:double -> String、Double -> String
-
枚舉 <-> String
-
日期 <-> String,例如:Date -> String、LocalDateTime -> String
-
Date <-> String之間的轉(zhuǎn)換,是使用SimpleDateFormat進(jìn)行轉(zhuǎn)換的;
-
LocalDateTime -> String之間的轉(zhuǎn)換,是使用DateTimeFormatter進(jìn)行轉(zhuǎn)換的;
-
更多隱式的類型轉(zhuǎn)換,可參考官方文檔:MapStruct支持的隱式類型轉(zhuǎn)換
2.6 擴(kuò)展
對于Java原生數(shù)據(jù)類型 or 包裝類型 <-> String之間的轉(zhuǎn)換,我們可以自定義其轉(zhuǎn)換的格式。
當(dāng)我們指定了轉(zhuǎn)換的格式后,MapStruct會使用DecimalFormat進(jìn)行類型之間轉(zhuǎn)換。
更多DecimalFormat的用法可自行百度。這里僅帖個鏈接:DecimalFormat用法舉例
@Mapper public interface SourceTargetMapper {SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);// numberFormat 指定了轉(zhuǎn)換后的字符串格式@Mapping(target = "sameName4", numberFormat = "#.##元")Target s2t(Source source);// @InheritInverseConfiguration 注解表示 繼承并反轉(zhuǎn)Target s2t(Source source)的轉(zhuǎn)換規(guī)則@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之間的轉(zhuǎn)換,我們可以自定義其轉(zhuǎn)換的格式。
@Mapper public interface SourceTargetMapper {SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);// dateFormat 指定了日期轉(zhuǎn)換成字符串后的格式@Mapping(target = "sameName4", numberFormat = "這是格式化的數(shù)字#.##")@Mapping(target = "date", dateFormat = "yyyy-MM-dd")@Mapping(target = "localDateTime", dateFormat = "yyyy-MM-dd HH:mm:ss")Target s2t(Source source);// @InheritInverseConfiguration 注解表示 繼承并反轉(zhuǎn)Target s2t(Source source)的轉(zhuǎn)換規(guī)則@InheritInverseConfigurationSource t2s(Target target);}上面測試程序的輸入如下:
Target(sameName1=1, sameName2=2, sameName3=3, sameName4=這是格式化的數(shù)字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實(shí)現(xiàn)類的代碼,大家可以結(jié)合代碼理解下。
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( "這是格式化的數(shù)字#.##" ).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( "這是格式化的數(shù)字#.##" ).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指定轉(zhuǎn)換規(guī)則
當(dāng)Source類和Target類中,對應(yīng)的字段名不一樣時,我們需要告訴MapStruct,哪兩個屬性之間是對應(yīng)的。我們可以通過@Mapping注解指定屬性之間的對應(yīng)關(guān)系。
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
上述代碼的注釋中已經(jīng)指定了Source與Target中屬性的對應(yīng)關(guān)系。
我們可以通過@Mapping注解的source()指定源類型中的屬性名,target()指定目標(biāo)類型中的屬性名。
@Mapper public interface SourceTargetMapper {SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);@Mapping(source = "s1", target = "t1")@Mapping(source = "s2", target = "t2", numberFormat = "格式化的數(shù)字#.##")@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表示:繼承并反轉(zhuǎn)Target s2t(Source source)的轉(zhuǎn)換規(guī)則** @param target* @return*/@InheritInverseConfigurationSource t2s(Target target);/*** 用于轉(zhuǎn)換 SomeSourceObject -> SomeTargetObject* @param some* @return*/@Mapping(source = "value", target = "targetValue")SomeTargetObject someS2t(SomeSourceObject some);@InheritInverseConfigurationSomeSourceObject someT2s(SomeTargetObject some);}對于Source類中的屬性s5 與 Target類中的屬性t5,MapStruct直接使用值拷貝的方式進(jìn)行轉(zhuǎn)換;
對于Source類中的屬性s5 與 Target類中的屬性t6,由于它們是對象類型,并且類型是不同的,MapStruct是不知道它們之間是如何轉(zhuǎn)換的,所以我們需要單獨(dú)定義它們之間的轉(zhuǎn)換規(guī)則,如:
-
方法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);}}程序的輸出結(jié)果如下:
Target(t1=1, t2=格式化的數(shù)字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 小結(jié)
生成的Mapper實(shí)現(xiàn)類的代碼如下:
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( "格式化的數(shù)字#.##" ).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( "格式化的數(shù)字#.##" ).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;} }總結(jié)
以上是生活随笔為你收集整理的03-MapStruct-基本的映射方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022北京工业互联网安全大赛初赛-wa
- 下一篇: 基于YOLOv4的交通视频监控车辆识别