First, use MapStruct

MapStruct is a type-safe Bean mapping class that generates Java annotation processors. All we need to do is define a mapper interface that declares any necessary mapping methods. During compilation, the MapStruct processor generates the @mapper interface implementation class under target/generated-sources/ Annotations.

Using Mapstruct requires mapstruct dependencies and mapstruct-Processor annotations. There are two ways to introduce dependencies using Maven:

To add mapstruct processor as a plug-in, refer to the official guide of mapstruct.

<properties>
    <org.mapstruct.version>1.3.1. The Final</org.mapstruct.version>
</properties>
 
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>
 
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.5.1 track of</version> <! -- or newer version -->
            <configuration>
                <source>1.8</source> <! -- depending on your project -->
                <target>1.8</target> <! -- depending on your project -->
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                    <! -- other annotation processors -->
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>
Copy the code

Note that if lombok is used in your project, you need to configure lombok in your compiled plug-in as well. Otherwise, the @Mapper interface implementation class may not be generated properly.

             <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
Copy the code

The maven-comiler-plugin plugin must be installed in version 3.6.0 or later. If the maven-comiler-plugin plugin is installed in version 3.6.0 or later, the maven-comiler-plugin plugin will not be found

<exclusions>
    <exclusion>
        <artifactId>mapstruct</artifactId>
        <groupId>org.mapstruct</groupId>
    </exclusion>
</exclusions>
Copy the code

Otherwise it would report: Couldn’t retrieve @Mapper annotation

2. Mapstruct field mapping

The basic mapping

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

    @Mapping(source = "make", target = "manufacturer")
    @Mapping(source = "numberOfSeats", target = "seatCount")
    CarDto carToCarDto(Car car);
	
    @Mapping(source = "name", target = "fullName")
    PersonDto personToPersonDto(Person person);
}
Copy the code

Code used in CarDTO CarDTO = CarMapper. INSTANCE. CarToCarDto (car) type in (the same below). In the generated method implementation, all readable attributes of the source type (for example, Car) are copied to corresponding attributes of the target type (for example, CarDto) : when an attribute is the same name as its target entity, it is implicitly mapped. When an attribute has a different name in the target entity, you can specify its name through the @Mapping annotation.

Mapping of multiple attributes

Scenario: To combine multiple entities into a single data transfer object.

@Mapper
public interface AddressMapper {
	AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class);
	
    @Mapping(source = "person.description", target = "description")
    @Mapping(source = "address.houseNo", target = "houseNumber")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
}
Copy the code

Mapping methods that directly reference source parameters

@Mapper
public interface AddressMapper {
	AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class);
	
    @Mapping(source = "person.description", target = "description")
    @Mapping(source = "address.houseNo", target = "houseNumber")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);
}
Copy the code

Inheritance inversion configuration

Scenario: There is already a method that maps Car to CarDTO, now you need to map CarDTO to Car.

@Mapper
public interface CarMapper {
    CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
	   
    @Mapping(source = "make", target = "manufacturer")
    @Mapping(source = "numberOfSeats", target = "seatCount")
    CarDto carToCarDto(Car car);

	@InheritInverseConfiguration(name = "carToCarDto")
	Car carDTOTocar(CarDto carDto);
}
Copy the code

Default values and constants

You can specify @mapping defaultValue and constant respectively. When the source object’s attribute value is null, if defaultValue is specified, the defaultValue of defaultValue will be injected. The constant attribute is used to inject a constant value into the target attribute.

 @Mapper(uses = StringListMapper.class)
public interface SourceTargetMapper {

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

    @Mapping(target = "stringProperty", source = "stringProp", defaultValue = "undefined")
    @Mapping(target = "longProperty", source = "longProp", defaultValue = "-1")
    @Mapping(target = "stringConstant", constant = "Constant Value")
    @Mapping(target = "integerConstant", constant = "14")
    @Mapping(target = "longWrapperConstant", constant = "3001")
    @Mapping(target = "dateConstant", dateFormat = "dd-MM-yyyy", constant = "09-01-2014")
    @Mapping(target = "stringListConstants", constant = "jack-jill-tom")
    Target sourceToTarget(Source s);
}
Copy the code

If s.getStringProp() == null, set the target property stringProperty to “undefined” instead of applying the value from s.getStringProp(). If s.gelongProperty () == null, the target longProperty will be set to -1. Set String “Constant Value” to the target property stringConstant. The value “3001” is cast to the longWrapperConstant property of the Targe class. This constant “jack-Jilly-Tom” maps dash-separated lists to lists.

The date type

 @Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss", expression = "java(new Date())")
 CarDTO doToDTO(CarDO carDO);
Copy the code

Numeric types

Conversion from int to String

@Mapper
public interface CarMapper {

    @Mapping(source = "price", numberFormat = "$#.00")
    CarDto carToCarDto(Car car);

    @IterableMapping(numberFormat = "$#.00")
    List<String> prices(List<Integer> prices);
}
Copy the code

Conversion from BigDecimal to String

@Mapper
public interface CarMapper {

    @Mapping(source = "manufacturingDate", dateFormat = "dd.MM.yyyy")
    CarDto carToCarDto(Car car);

    @IterableMapping(dateFormat = "dd.MM.yyyy")
    List<String> stringListToDateList(List<Date> dates);
}
Copy the code

Control the mapping of nested beans

Scenario: Multiple internal nesting of objects.

@Mapper
public interface FishTankMapper {
    @Mapping(target = "fish.kind", source = "fish.type")
    @Mapping(target = "fish.name", ignore = true)
    @Mapping(target = "ornament", source = "interior.ornament")
    @Mapping(target = "material.materialType", source = "material")
    @Mapping(target = "quality.report.organisation.name", source = "quality.report.organisationName")
    FishTankDto map( FishTank source );
}
Copy the code

Mapping expression

Scenario: The target attribute needs to be computed. This functionality is powerful for many scenarios, such as converting an Integer class to an enumerated class, unit conversion, and so on.

@Mappings({ @Mapping(target = "extensionInsuranceAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getExtensionInsuranceAmount()))"),  @Mapping(target = "insuranceAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getInsuranceAmount()))"), @Mapping(target = "purchaseTax", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getPurchaseTax()))"), @Mapping(target = "decorationAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getDecorationAmount()))"), @Mapping(target = "boutiqueAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getBoutiqueAmount()))"), @Mapping(target = "maintainPackageAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getMaintainPackageAmount()))"), @Mapping(target = "repairAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getRepairAmount()))"), @Mapping(target = "renewalFundAmount", expression = "java(com.souche.connector.common.util.AmountConvertUtil.convertToFen(vo.getRenewalFundAmount()))") })
    AdditionalDetailDTO map(AdditionalDetailVO vo);
Copy the code

Map collections

Scenario: Map one collection to another.

@Mapper
public interface CarMapper {

    Set<String> integerSetToStringSet(Set<Integer> integers);

    List<CarDto> carsToCarDtos(List<Car> cars);

    CarDto carToCarDto(Car car);
}
Copy the code

Mapping the map

Scenario: Map one Map object to another Map object

public interface SourceTargetMapper {

    @MapMapping(valueDateFormat = "dd.MM.yyyy")
    Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
}
Copy the code

Mapping enumeration type

Constants in the source enumeration can be mapped to constants with other names with the help of the @valuemapping annotation. Multiple constants from the source enumeration can be mapped to the same constant in the target type.

@Mapper
public interface OrderMapper {

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

    @ValueMappings({ @ValueMapping(source = "EXTRA", target = "SPECIAL"), @ValueMapping(source = "STANDARD", target = "DEFAULT"), @ValueMapping(source = "NORMAL", target = "DEFAULT") })
    ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
}
Copy the code