1. 程式人生 > 其它 >MapStruct文件(二)——對映

MapStruct文件(二)——對映

技術標籤:mapstructjava

目錄

1、對映

1.1、自定義方法

1.1.1、抽象類

1.1.2、預設方法

1.2、多個物件對映成一個

1.3、巢狀物件對映

1.3.1、屬性物件欄位全部對映

1.3.2、指定欄位對映

1.4、Builder建立目標物件

1.5、構造方法建立目標物件

1.6、更新資料來源

1.7、*對映屬性欄位


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 );
        }
    }
}

可以看到testBOid被忽略沒有更新,nickname會被更新為testPOname


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#testgetter/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方法。

原始碼分析會在後續介紹。