MapStruct文件(二)——對映
阿新 • • 發佈:2020-12-14
目錄
1、對映
1.1、自定義方法
若mapstruct滿足不了我們的需求,也可以自己寫轉換方法。
1.1.1、抽象類
@Mapper public abstract class TestMapper { public TestBO testToBO(TestPO testPO) { TestBO testBO = new TestBO(); testBO.setName(testPO.getName() + "BO"); return testBO; } public abstract List<TestBO> testToBOS(List<TestPO> testPOS); } // 編譯生成的子類 @Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2020-10-28T16:51:57+0800", comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_251 (Oracle Corporation)" ) public class TestMapperImpl extends TestMapper { @Override public List<TestBO> testToBOS(List<TestPO> testPOS) { if ( testPOS == null ) { return null; } List<TestBO> list = new ArrayList<TestBO>( testPOS.size() ); for ( TestPO testPO : testPOS ) { list.add( testToBO( testPO ) ); } return list; } }
1.1.2、預設方法
java8以上可以在介面中定義預設方法
@Mapper public interface TestMapper { default TestBO testToBO(TestPO testPO) { TestBO testBO = new TestBO(); testBO.setName(testPO.getName() + "BO"); return testBO; } List<TestBO> testToBOS(List<TestPO> testPOS); } public class TestMapperImpl implements TestMapper { @Override public List<TestBO> testToBOS(List<TestPO> testPOS) { if ( testPOS == null ) { return null; } List<TestBO> list = new ArrayList<TestBO>( testPOS.size() ); for ( TestPO testPO : testPOS ) { list.add( testToBO( testPO ) ); } return list; } }
結果
1.2、多個物件對映成一個
@Mapper public interface TestMapper { @Mapping(source = "testPO.id", target = "id") TestMixBO testToBO(TestPO testPO, TestTwoPO testTwoPO); } public class TestMapperImpl implements TestMapper { @Override public TestMixBO testToBO(TestPO testPO, TestTwoPO testTwoPO) { if ( testPO == null && testTwoPO == null ) { return null; } TestMixBO testMixBO = new TestMixBO(); if ( testPO != null ) { testMixBO.setId( testPO.getId() ); testMixBO.setName( testPO.getName() ); testMixBO.setPrice( testPO.getPrice() ); testMixBO.setCreteTime( testPO.getCreteTime() ); } if ( testTwoPO != null ) { testMixBO.setTitle( testTwoPO.getTitle() ); } return testMixBO; } } // 單元測試 TestPO testPO = new TestPO(); testPO.setId(1L); testPO.setName("haru"); testPO.setCreteTime(new Date(System.currentTimeMillis())); TestTwoPO testTwoPO = new TestTwoPO(); testTwoPO.setId(2L); testTwoPO.setTitle("測試"); testTwoPO.setTotalPrice(10.2F); TestMapper mapper = Mappers.getMapper(TestMapper.class); TestMixBO testMixBO = mapper.testToBO(testPO, testTwoPO); System.out.println(testMixBO);
結果
@Mapping用於指定對映的欄位。
還可以將非物件源對映到目標。
@Mapper
public interface TestMapper {
@Mapping(target = "discount", source = "totalPrice")
TestMixBO testToBO(TestPO testPO, Float totalPrice);
}
public class TestMapperImpl implements TestMapper {
@Override
public TestMixBO testToBO(TestPO testPO, Float totalPrice) {
if ( testPO == null && totalPrice == null ) {
return null;
}
TestMixBO testMixBO = new TestMixBO();
if ( testPO != null ) {
testMixBO.setId( testPO.getId() );
testMixBO.setName( testPO.getName() );
testMixBO.setPrice( testPO.getPrice() );
testMixBO.setCreteTime( testPO.getCreteTime() );
}
if ( totalPrice != null ) {
testMixBO.setDiscount( totalPrice );
}
return testMixBO;
}
}
TestPO testPO = new TestPO();
testPO.setId(1L);
testPO.setName("haru");
testPO.setCreteTime(new Date(System.currentTimeMillis()));
TestMapper mapper = Mappers.getMapper(TestMapper.class);
TestMixBO testMixBO = mapper.testToBO(testPO, 2.4F);
System.out.println(testMixBO);
結果
若有同名欄位,要使用@Mapping(source = "", target = "")指定目標欄位。
1.3、巢狀物件對映
1.3.1、屬性物件欄位全部對映
可以將target設為".",source所對應的屬性物件欄位會全部對映到target中同名欄位上。
@Mapper
public interface TestMapper {
@Mapping(source = "test", target = ".")
TestMixBO testToBO(TestThreePO testPO);
}
public class TestMapperImpl implements TestMapper {
@Override
public TestMixBO testToBO(TestThreePO testPO) {
if ( testPO == null ) {
return null;
}
TestMixBO testMixBO = new TestMixBO();
testMixBO.setId( testPOTestPOId( testPO ) );TestThreePO
testMixBO.setName( testPOTestPOName( testPO ) );
testMixBO.setPrice( testPOTestPOPrice( testPO ) );
testMixBO.setCreteTime( testPOTestPOCreteTime( testPO ) );
return testMixBO;
}
private Long testPOTestPOId(TestThreePO testThreePO) {
if ( testThreePO == null ) {
return null;
}
TestPO testPO = testThreePO.getTestPO();
if ( testPO == null ) {
return null;
}
Long id = testPO.getId();
if ( id == null ) {
return null;
}
return id;
}
private String testPOTestPOName(TestThreePO testThreePO) {
if ( testThreePO == null ) {
return null;
}
TestPO testPO = testThreePO.getTestPO();
if ( testPO == null ) {
return null;
}
String name = testPO.getName();
if ( name == null ) {
return null;
}
return name;
}
private BigDecimal testPOTestPOPrice(TestThreePO testThreePO) {
if ( testThreePO == null ) {
return null;
}
TestPO testPO = testThreePO.getTestPO();
if ( testPO == null ) {
return null;
}
BigDecimal price = testPO.getPrice();
if ( price == null ) {
return null;
}
return price;
}
private Date testPOTestPOCreteTime(TestThreePO testThreePO) {
if ( testThreePO == null ) {
return null;
}
TestPO testPO = testThreePO.getTestPO();
if ( testPO == null ) {
return null;
}
Date creteTime = testPO.getCreteTime();
if ( creteTime == null ) {
return null;
}
return creteTime;
}
}
TestPO testPO = new TestPO();
testPO.setId(1L);
testPO.setName("haru");
testPO.setCreteTime(new Date(System.currentTimeMillis()));
TestThreePO testThreePO = new TestThreePO();
testThreePO.setTest(testPO);
TestMapper mapper = Mappers.getMapper(TestMapper.class);
TestMixBO testMixBO = mapper.testToBO(testThreePO);
System.out.println(testMixBO);
結果
1.3.2、指定欄位對映
@Mapper
public interface TestMapper {
@Mapping(source = "test.id", target = "test.idFive")
@Mapping(target = "test.name", ignore = true)
@Mapping(target = "totalPrice", expression = "java(testThreePO.getTotalPrice().toString() + \"元\")")
@Mapping(target = "test.title", constant = "無名")
TestFourBO toTestBO(TestThreePO testThreePO);
}
@Component
public class TestMapperImpl implements TestMapper {
@Override
public TestFourBO toTestBO(TestThreePO testThreePO) {
if ( testThreePO == null ) {
return null;
}
TestFourBO testFourBO = new TestFourBO();
testFourBO.setTest( testPOToTestFiveBO( testThreePO.getTest() ) );
testFourBO.setTotalPrice( testThreePO.getTotalPrice().toString() + "元" );
return testFourBO;
}
protected TestFiveBO testPOToTestFiveBO(TestPO testPO) {
if ( testPO == null ) {
return null;
}
TestFiveBO testFiveBO = new TestFiveBO();
testFiveBO.setIdFive( testPO.getId() );
if ( testPO.getPrice() != null ) {
testFiveBO.setPrice( testPO.getPrice().toString() );
}
testFiveBO.setCreteTime( testPO.getCreteTime() );
testFiveBO.setTitle( "無名" );
return testFiveBO;
}
}
指定test.id對映到test.idFive,忽略了test.name,@Mapping#expression可以編寫java程式碼或引用一個方法,@Mapping#constant可以設定常亮,其他屬性自動對映。
儘量為每個物件對映編寫單獨的mapper介面,利於複用,減少重複程式碼。
1.4、Builder建立目標物件
支援builder方式建立物件。
@Data
@Builder
@ToString
public class TestMixBO {
private Long id;
private String name;
private BigDecimal price;
private Date creteTime;
private String title;
private Float discount;
}
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2020-10-28T20:08:04+0800",
comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_251 (Oracle Corporation)"
)
public class TestMapperImpl implements TestMapper {
@Override
public TestMixBO testToBO(TestThreePO testPO) {
if ( testPO == null ) {
return null;
}
// builder建立
TestMixBOBuilder testMixBO = TestMixBO.builder();
testMixBO.id( testPOTestPOId( testPO ) );
testMixBO.name( testPOTestPOName( testPO ) );
testMixBO.price( testPOTestPOPrice( testPO ) );
testMixBO.creteTime( testPOTestPOCreteTime( testPO ) );
return testMixBO.build();
}
private Long testPOTestPOId(TestThreePO testThreePO) {
if ( testThreePO == null ) {
return null;
}
TestPO testPO = testThreePO.getTestPO();
if ( testPO == null ) {
return null;
}
Long id = testPO.getId();
if ( id == null ) {
return null;
}
return id;
}
private String testPOTestPOName(TestThreePO testThreePO) {
if ( testThreePO == null ) {
return null;
}
TestPO testPO = testThreePO.getTestPO();
if ( testPO == null ) {
return null;
}
String name = testPO.getName();
if ( name == null ) {
return null;
}
return name;
}
private BigDecimal testPOTestPOPrice(TestThreePO testThreePO) {
if ( testThreePO == null ) {
return null;
}
TestPO testPO = testThreePO.getTestPO();
if ( testPO == null ) {
return null;
}
BigDecimal price = testPO.getPrice();
if ( price == null ) {
return null;
}
return price;
}
private Date testPOTestPOCreteTime(TestThreePO testThreePO) {
if ( testThreePO == null ) {
return null;
}
TestPO testPO = testThreePO.getTestPO();
if ( testPO == null ) {
return null;
}
Date creteTime = testPO.getCreteTime();
if ( creteTime == null ) {
return null;
}
return creteTime;
}
}
結果
1.5、構造方法建立目標物件
@Data
@ToString
@AllArgsConstructor
public class TestMixBO {
private Long id;
private String name;
private BigDecimal price;
private Date creteTime;
private String title;
private Float discount;
}
@Mapper
public interface TestMapper {
TestMixBO testToBO(TestPO testPO);
}
public class TestMapperImpl implements TestMapper {
@Override
public TestMixBO testToBO(TestPO testPO) {
if ( testPO == null ) {
return null;
}
Long id = null;
String name = null;
BigDecimal price = null;
Date creteTime = null;
id = testPO.getId();
name = testPO.getName();
price = testPO.getPrice();
creteTime = testPO.getCreteTime();
String title = null;
Float discount = null;
// 構造方法建立
TestMixBO testMixBO = new TestMixBO( id, name, price, creteTime, title, discount );
return testMixBO;
}
}
TestPO testPO = new TestPO();
testPO.setId(1L);
testPO.setName("haru");
testPO.setCreteTime(new Date(System.currentTimeMillis()));
TestMapper mapper = Mappers.getMapper(TestMapper.class);
TestMixBO testMixBO = mapper.testToBO(testPO);
System.out.println(testMixBO);
結果
1.6、更新資料來源
@MappingTarget註解的引數是被更新的資料來源物件。
@Mapper
public interface TestMapper {
void updateBO(TestPO testPO, @MappingTarget TestBO testBO);
}
@Component
public class TestMapperImpl implements TestMapper {
@Override
public void updateBO(TestPO testPO, TestBO testBO) {
if ( testPO == null ) {
return;
}
testBO.setId( testPO.getId() );
testBO.setName( testPO.getName() );
if ( testPO.getPrice() != null ) {
testBO.setPrice( testPO.getPrice().toString() );
}
else {
testBO.setPrice( null );
}
if ( testPO.getCreteTime() != null ) {
testBO.setCreteTime( new SimpleDateFormat().format( testPO.getCreteTime() ) );
}
else {
testBO.setCreteTime( null );
}
}
}
TestBO testBO = new TestBO();
testBO.setId(1L);
TestPO testPO = new TestPO();
testPO.setName("XX");
testPO.setId(2L);
testPO.setCreteTime(new Date(System.currentTimeMillis()));
testMapper.updateBO(testPO, testBO);
System.out.println(testBO);
結果
更新時也可以使用@Mapping。
@Data
@ToString
public class TestPO {
private Long id;
private String name;
private BigDecimal price;
private Date creteTime;
}
@Data
@ToString
public class TestBO {
private Long id;
private String name;
private String price;
private String nickname;
}
@Mapper
public interface TestMapper {
@Mapping(target = "id", ignore = true)
@Mapping(target = "nickname", source = "name")
void toBOS(TestPO testPO, @MappingTarget TestBO testBO);
}
@Component
public class TestMapperImpl implements TestMapper {
@Override
public void toBOS(TestPO testPO, TestBO testBO) {
if ( testPO == null ) {
return;
}
testBO.setNickname( testPO.getName() );
testBO.setName( testPO.getName() );
if ( testPO.getPrice() != null ) {
testBO.setPrice( testPO.getPrice().toString() );
}
else {
testBO.setPrice( null );
}
}
}
可以看到testBO的id被忽略沒有更新,nickname會被更新為testPO的name。
1.7、*對映屬性欄位
注意在@Mapping#target、source設定的屬性的含義。mapstruct並不是按照設定的屬性找到欄位,在找此欄位的getter/setter方法的,而是直接通過get/set字首 + 屬性值找到相應的方法。
public class TestThreeBO {
private TestBO test;
public TestBO getTestG() {
return test;
}
public void setTestS(TestBO test) {
this.test = test;
}
}
@Mapper
public interface TestMapper {
@Mapping(target = "test", source = "testPO")
TestThreeBO toBO(TestThreePO testThreePO);
@Mapping(target = "testPO", source = "test")
TestThreePO toPO(TestThreeBO testThreeBO);
}
這裡修改了TestThreeBO#test的getter/setter方法名,對映屬性值還是用test。
編譯報錯,這裡提示了直接提示“testG”的屬性名。
@Mapper
public interface TestMapper {
@Mapping(target = "testS", source = "testPO")
TestThreeBO toBO(TestThreePO testThreePO);
@Mapping(target = "testPO", source = "testG")
TestThreePO toPO(TestThreeBO testThreeBO);
}
@Component
public class TestMapperImpl implements TestMapper {
@Override
public TestThreeBO toBO(TestThreePO testThreePO) {
if ( testThreePO == null ) {
return null;
}
TestThreeBO testThreeBO = new TestThreeBO();
testThreeBO.setTestS( testPOToTestBO( testThreePO.getTestPO() ) );
return testThreeBO;
}
@Override
public TestThreePO toPO(TestThreeBO testThreeBO) {
if ( testThreeBO == null ) {
return null;
}
TestThreePO testThreePO = new TestThreePO();
try {
testThreePO.setTestPO( testBOToTestPO( testThreeBO.getTestG() ) );
}
catch ( ParseException e ) {
throw new RuntimeException( e );
}
return testThreePO;
}
protected TestBO testPOToTestBO(TestPO testPO) {
if ( testPO == null ) {
return null;
}
TestBO testBO = new TestBO();
testBO.setId( testPO.getId() );
testBO.setName( testPO.getName() );
if ( testPO.getPrice() != null ) {
testBO.setPrice( testPO.getPrice().toString() );
}
if ( testPO.getCreateTime() != null ) {
testBO.setCreateTime( new SimpleDateFormat().format( testPO.getCreateTime() ) );
}
return testBO;
}
protected TestPO testBOToTestPO(TestBO testBO) throws ParseException {
if ( testBO == null ) {
return null;
}
TestPO testPO = new TestPO();
testPO.setId( testBO.getId() );
testPO.setName( testBO.getName() );
if ( testBO.getPrice() != null ) {
testPO.setPrice( new BigDecimal( testBO.getPrice() ) );
}
if ( testBO.getCreateTime() != null ) {
testPO.setCreateTime( new SimpleDateFormat().parse( testBO.getCreateTime() ) );
}
return testPO;
}
}
再修改屬性值,可以得到正確的實現類。
使用IDEA可以裝上MapStruct support外掛,可以通過屬性值調到相應的getter/setter方法。
原始碼分析會在後續介紹。