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