目錄:
寫完第一篇,好長時間沒寫了。趁着今天有時間,繼續總結一下吧。來吧,我們繼續。今天我們看一個轉化的例子,例子幾乎涉及到了大部分情況。
一、目標及要求
先定義兩個互相賦值的類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的印象更深了。好了,今天就到這里了。下期我們繼續!!!
