MapStruct文档(二)——映射

 

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

结果

MapStruct文档(二)——映射


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

结果

MapStruct文档(二)——映射

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

结果

MapStruct文档(二)——映射

若有同名字段,要使用@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);

结果

MapStruct文档(二)——映射

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

结果

MapStruct文档(二)——映射


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

结果

MapStruct文档(二)——映射


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

结果

MapStruct文档(二)——映射

 

更新时也可以使用@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

MapStruct文档(二)——映射

编译报错,这里提示了直接提示“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方法。

源码分析会在后续介绍。 

上一篇:java – 为mapstruct创建的抽象类内部自动装配


下一篇:java – 使用MapStruct进行转换时防止循环引用