20211012 MapStruct

环境信息

  • 最新版本:1.4.2.Final - 2021年4月19日
  • 笔记版本:1.4.2.Final

MapStruct

  • MapStruct 是一个代码生成器,它基于约定优于配置的方法极大地简化了 Java bean 类型之间映射的实现
  • MapStruct 在编译时生成 Bean 映射(类似 Lombok)
  • MapStruct 是一个注释处理器(annotation processor )

与 Lombok 共用

需要确定好 lombok 和 mapstruct 的版本,部分版本不能一起使用

参考官方文档

<org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.16</org.projectlombok.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
        

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>${org.mapstruct.version}</version>
</dependency>

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>${org.mapstruct.version}</version>
</dependency>

<!-- lombok dependencies should not end up on classpath -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>${org.projectlombok.version}</version>
    <scope>provided</scope>
</dependency>



<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <!-- See https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html -->
                    <!-- Classpath elements to supply as annotation processor path. If specified, the compiler   -->
                    <!-- will detect annotation processors only in those classpath elements. If omitted, the     -->
                    <!-- default classpath is used to detect annotation processors. The detection itself depends -->
                    <!-- on the configuration of annotationProcessors.                                           -->
                    <!--                                                                                         -->
                    <!-- According to this documentation, the provided dependency processor is not considered!   -->
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${org.projectlombok.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok-mapstruct-binding</artifactId>
                            <version>${lombok-mapstruct-binding.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

代码示例

简单使用

// Source 类
@Data
@AllArgsConstructor
public class Car {

    private String make;
    private int numberOfSeats;
    private CarType type;

}
// Target 类
@Data
public class CarDto {

    private String make;
    private int seatCount;
    private String type;

}
// Mapper 类
@Mapper
public interface CarMapper {

    CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

    @Mapping(source = "numberOfSeats", target = "seatCount")
    CarDto carToCarDto(Car car);
}
// 测试类
public class CarMapperTest {
    @Test
    public void shouldMapCarToDto() {
        //given
        Car car = new Car("Morris", 5, CarType.SEDAN);

        //when
        CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);

        //then
        System.out.println(carDto == null); // false
        System.out.println(carDto.getMake().equals("Morris"));  // true
        System.out.println(carDto.getSeatCount() == 5); // true
        System.out.println(carDto.getType().equals("SEDAN")); // true
    }
}

基本映射

package study.mapstruct.demo2.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Test;
import org.mapstruct.*;
import org.mapstruct.factory.Mappers;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;



public class PersonConverterTest {
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    static class Person {
        private Long id;
        private String name;
        private String email;
        private Date birthday;
        private User user;
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    static class User {
        private Integer age;
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    static class PersonDTO {
        private String id;
        private String name;
        /**
         * 对应 Person.user.age
         */
        private Integer age;
        private String email;
        /**
         * 与 DO 里面的字段名称(birthDay)不一致
         */
        private Date birth;
        /**
         * 对 DO 里面的字段(birthDay)进行拓展,dateFormat 的形式
         */
        private String birthDateFormat;
        /**
         * 对 DO 里面的字段(birthDay)进行拓展,expression 的形式
         */
        private String birthExpressionFormat;

    }

    @Mapper
    interface PersonConverter {
        PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);

        @Mappings({
                @Mapping(source = "birthday", target = "birth"),
                @Mapping(source = "birthday", target = "birthDateFormat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
                @Mapping(target = "birthExpressionFormat", expression = "java(study.mapstruct.demo2.dto.PersonConverterTest.PersonConverter.format(person.getBirthday(),"yyyy-MM-dd HH:mm:ss"))"),
                @Mapping(source = "user.age", target = "age"),
                @Mapping(target = "email", ignore = false)
        })
        PersonDTO domain2dto(Person person);

        List<PersonDTO> domain2dtoList(List<Person> people);

        static String format(Date date, String format) {
            return new SimpleDateFormat(format).format(date);
        }

        /**
         * 自定义方法
         * @param value
         * @return
         */
        default Boolean convert2Bool(Integer value) {
            if (value == null || value < 1) {
                return Boolean.FALSE;
            } else {
                return Boolean.TRUE;
            }
        }

        /**
         * 继承配置
         * @param person
         * @param personDTO
         */
        @InheritConfiguration(name = "domain2dto")
        void update(Person person, @MappingTarget PersonDTO personDTO);
    }

    @Test
    public void test() {
        Person person = new Person(1L, "zhige", "zhige.me@gmail.com", new Date(), new User(1));
        System.out.println(person);
        PersonDTO personDTO = PersonConverter.INSTANCE.domain2dto(person);
        System.out.println(personDTO);


        List<Person> people = new ArrayList<>();
        people.add(person);
        List<PersonDTO> personDTOs = PersonConverter.INSTANCE.domain2dtoList(people);
        System.out.println(personDTOs);


        System.out.println(PersonConverter.INSTANCE.convert2Bool(1));

        PersonDTO personDTO2 = new PersonDTO();
        PersonConverter.INSTANCE.update(person, personDTO2);
        System.out.println(personDTO2);
    }
}

多对一

package study.mapstruct.demo2.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Test;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;


public class ItemConverterTest {
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    static class Item {
        private Long id;
        private String title;
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    static class Sku {
        private Long id;
        private String code;
        private Integer price;
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    static class SkuDTO {
        private Long skuId;
        private String skuCode;
        private Integer skuPrice;
        private Long itemId;
        private String itemName;
    }

    @Mapper
    public interface ItemConverter {
        ItemConverter INSTANCE = Mappers.getMapper(ItemConverter.class);

        @Mappings({
                @Mapping(source = "sku.id", target = "skuId"),
                @Mapping(source = "sku.code", target = "skuCode"),
                @Mapping(source = "sku.price", target = "skuPrice"),
                @Mapping(source = "item.id", target = "itemId"),
                @Mapping(source = "item.title", target = "itemName")
        })
        SkuDTO domain2dto(Item item, Sku sku);
    }

    @Test
    public void test() {
        Item item = new Item(1L, "iPhone X");
        Sku sku = new Sku(2L, "phone12345", 1000000);
        SkuDTO skuDTO = ItemConverter.INSTANCE.domain2dto(item, sku);
        System.out.println(skuDTO);
    }
}

Spring Boot 集成

唯一需要改变的是注解

@Mapper(componentModel="spring")

生成的实现类上会带上注解 @Component

参考资料

原文地址:https://www.cnblogs.com/huangwenjie/p/15396342.html