MapStruct - 让java实体映射更容易

MapStruct - 让java实体映射更容易

一、什么是MapStruct?

MapStruct 是一个 Java 注释处理器,用于为 Java Bean 类生成类型安全和高性能的映射器。它使您免于手动编写映射代码,这是一项繁琐且容易出错的任务。该生成器具有合理的默认值和许多内置类型转换,也可以通过配置实现一些特殊行为。

与在运行时工作的映射框架相比,MapStruct具有以下优点:

  • 通过使用普通方法调用而不是反射赋值
  • 编译时类型安全。只能映射彼此映射的对象和属性,不会意外地将订单实体映射到客户 DTO 等。
  • 自包含代码 — 无运行时依赖关系
  • 在以下情况下,在构建时清除错误报告
    • 映射不完整(并非所有目标属性都已映射)
    • 映射不正确(找不到正确的映射方法或类型转换)
  • 易于调试的映射代码(或可手动编辑 - 例如,在生成器中出现错误的情况下)

若要在两种类型之间创建映射,请声明如下所示的映射器接口:

@Mapper
public interface CarMapper {

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

    @Mapping(target = "seatCount", source = "numberOfSeats")
    CarDto carToCarDto(Car car);
}

在编译时,MapStruct将生成此接口的实现。生成的实现使用纯 Java 方法调用来映射源对象和目标对象,即不涉及反射。默认情况下,如果属性在源和目标中具有相同的名称,则会映射它们,但也可以使用一些其他注释来控制此方面和许多其他方面。

二、要求

MapStruct 需要 Java 1.8 或更高版本。

三、引入 MapStruct

Maven

对于基于 Maven 的项目,请将以下内容添加到您的 POM 文件中以使用 MapStruct(依赖项可在 Maven Central 获得):

...
<properties>
    <org.mapstruct.version>1.5.5.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.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>
...

IntelliJ

确保至少具有 IntelliJ 2018.2.x。 在 IntelliJ 中启用注释处理(生成、执行、部署 -> 编译器 -> 注释处理器)annotationProcessors``maven-compiler-plugin

idea 插件(可选):MapStruct Support。

springboot集成及常见使用方法

pom.xml
...
		<properties>
			<org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
 		</properties>
...

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

...
	<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                    <annotationProcessorPaths>
                   	<!--兼容lombok-->
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok-mapstruct-binding</artifactId>
                            <version>${lombok-mapstruct-binding.version}</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
    	</plugins>
    </build>
...

创建转换接口类

使用注解@Mapper(componentModel = “spring”)标记接口类,编译将生成实现类;生成的实现类一般是在target目录下的对应接口类的包中。

@Mapper(componentModel = "spring")
public interface CustomerConvert {
    
}
@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-05-08T19:50:07+0800",
    comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_333 (Oracle Corporation)"
)
@Component
public class CustomerConvertImpl implements CustomerConvert {

}

四、常见用法

深克隆
import org.mapstruct.Mapper;
import org.mapstruct.control.DeepClone;

@Mapper(componentModel = "spring", mappingControl = DeepClone.class)
public interface Cloner {
    CustomerDto clone(CustomerDto customerDto);
}
import com.cy.mapstruct.entity.CustomerDto;
import com.cy.mapstruct.entity.OrderItemDto;
import com.cy.mapstruct.entity.OrderItemKeyDto;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-05-09T20:14:48+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 1.8.0_333 (Oracle Corporation)"
)
@Component
public class ClonerImpl implements Cloner {

    @Override
    public CustomerDto clone(CustomerDto customerDto) {
        if ( customerDto == null ) {
            return null;
        }

        CustomerDto customerDto1 = new CustomerDto();

        customerDto1.setId( customerDto.getId() );
        customerDto1.setCustomerName( customerDto.getCustomerName() );
        customerDto1.setOrders( orderItemDtoListToOrderItemDtoList( customerDto.getOrders() ) );
        customerDto1.setStock( orderItemKeyDtoOrderItemDtoMapToOrderItemKeyDtoOrderItemDtoMap( customerDto.getStock() ) );

        return customerDto1;
    }

    protected OrderItemDto orderItemDtoToOrderItemDto(OrderItemDto orderItemDto) {
        if ( orderItemDto == null ) {
            return null;
        }

        OrderItemDto orderItemDto1 = new OrderItemDto();

        orderItemDto1.setName( orderItemDto.getName() );
        orderItemDto1.setQuantity( orderItemDto.getQuantity() );

        return orderItemDto1;
    }

    protected List<OrderItemDto> orderItemDtoListToOrderItemDtoList(List<OrderItemDto> list) {
        if ( list == null ) {
            return null;
        }

        List<OrderItemDto> list1 = new ArrayList<OrderItemDto>( list.size() );
        for ( OrderItemDto orderItemDto : list ) {
            list1.add( orderItemDtoToOrderItemDto( orderItemDto ) );
        }

        return list1;
    }

    protected OrderItemKeyDto orderItemKeyDtoToOrderItemKeyDto(OrderItemKeyDto orderItemKeyDto) {
        if ( orderItemKeyDto == null ) {
            return null;
        }

        OrderItemKeyDto orderItemKeyDto1 = new OrderItemKeyDto();

        orderItemKeyDto1.setStockNumber( orderItemKeyDto.getStockNumber() );

        return orderItemKeyDto1;
    }

    protected Map<OrderItemKeyDto, OrderItemDto> orderItemKeyDtoOrderItemDtoMapToOrderItemKeyDtoOrderItemDtoMap(Map<OrderItemKeyDto, OrderItemDto> map) {
        if ( map == null ) {
            return null;
        }

        Map<OrderItemKeyDto, OrderItemDto> map1 = new LinkedHashMap<OrderItemKeyDto, OrderItemDto>( Math.max( (int) ( map.size() / .75f ) + 1, 16 ) );

        for ( java.util.Map.Entry<OrderItemKeyDto, OrderItemDto> entry : map.entrySet() ) {
            OrderItemKeyDto key = orderItemKeyDtoToOrderItemKeyDto( entry.getKey() );
            OrderItemDto value = orderItemDtoToOrderItemDto( entry.getValue() );
            map1.put( key, value );
        }

        return map1;
    }
}
属性映射
import com.cy.mapstruct.entity.Customer;
import com.cy.mapstruct.entity.CustomerDto;
import com.cy.mapstruct.entity.OrderItem;
import com.cy.mapstruct.entity.OrderItemDto;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(componentModel = "spring")
public interface PropConvertor {

    @Mapping(source = "orders", target = "orderItems")
    @Mapping(source = "customerName", target = "name")
    Customer toCustomer(CustomerDto customerDto);

    @InheritInverseConfiguration
    CustomerDto fromCustomer(Customer customer);

    OrderItem toOrder(OrderItemDto orderItemDto);

    @InheritInverseConfiguration
    OrderItemDto fromOrder(OrderItem orderItem);
}
import com.cy.mapstruct.entity.Customer;
import com.cy.mapstruct.entity.CustomerDto;
import com.cy.mapstruct.entity.OrderItem;
import com.cy.mapstruct.entity.OrderItemDto;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-05-09T20:52:30+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 1.8.0_333 (Oracle Corporation)"
)
@Component
public class PropConvertorImpl implements PropConvertor {

    @Override
    public Customer toCustomer(CustomerDto customerDto) {
        if ( customerDto == null ) {
            return null;
        }

        Customer customer = new Customer();

        customer.setOrderItems( orderItemDtoListToOrderItemCollection( customerDto.getOrders() ) );
        customer.setName( customerDto.getCustomerName() );
        customer.setId( customerDto.getId() );

        return customer;
    }

    @Override
    public CustomerDto fromCustomer(Customer customer) {
        if ( customer == null ) {
            return null;
        }

        CustomerDto customerDto = new CustomerDto();

        customerDto.setOrders( orderItemCollectionToOrderItemDtoList( customer.getOrderItems() ) );
        customerDto.setCustomerName( customer.getName() );
        customerDto.setId( customer.getId() );

        return customerDto;
    }

    @Override
    public OrderItem toOrder(OrderItemDto orderItemDto) {
        if ( orderItemDto == null ) {
            return null;
        }

        OrderItem orderItem = new OrderItem();

        orderItem.setName( orderItemDto.getName() );
        orderItem.setQuantity( orderItemDto.getQuantity() );

        return orderItem;
    }

    @Override
    public OrderItemDto fromOrder(OrderItem orderItem) {
        if ( orderItem == null ) {
            return null;
        }

        OrderItemDto orderItemDto = new OrderItemDto();

        orderItemDto.setName( orderItem.getName() );
        orderItemDto.setQuantity( orderItem.getQuantity() );

        return orderItemDto;
    }

    protected Collection<OrderItem> orderItemDtoListToOrderItemCollection(List<OrderItemDto> list) {
        if ( list == null ) {
            return null;
        }

        Collection<OrderItem> collection = new ArrayList<OrderItem>( list.size() );
        for ( OrderItemDto orderItemDto : list ) {
            collection.add( toOrder( orderItemDto ) );
        }

        return collection;
    }

    protected List<OrderItemDto> orderItemCollectionToOrderItemDtoList(Collection<OrderItem> collection) {
        if ( collection == null ) {
            return null;
        }

        List<OrderItemDto> list = new ArrayList<OrderItemDto>( collection.size() );
        for ( OrderItem orderItem : collection ) {
            list.add( fromOrder( orderItem ) );
        }

        return list;
    }
}

列表复制
import com.cy.mapstruct.entity.OrderItem;
import com.cy.mapstruct.entity.OrderItemDto;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;

import java.util.List;

@Mapper(componentModel = "spring")
public interface ListConvertor {
    List<OrderItem> toOrders(List<OrderItemDto> orderItemDtos);

    @InheritInverseConfiguration
    List<OrderItemDto> fromOrders(List<OrderItem> orderItems);
}

import com.cy.mapstruct.entity.OrderItem;
import com.cy.mapstruct.entity.OrderItemDto;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-05-12T20:55:39+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 1.8.0_333 (Oracle Corporation)"
)
@Component
public class ListConvertorImpl implements ListConvertor {

    @Override
    public List<OrderItem> toOrders(List<OrderItemDto> orderItemDtos) {
        if ( orderItemDtos == null ) {
            return null;
        }

        List<OrderItem> list = new ArrayList<OrderItem>( orderItemDtos.size() );
        for ( OrderItemDto orderItemDto : orderItemDtos ) {
            list.add( orderItemDtoToOrderItem( orderItemDto ) );
        }

        return list;
    }

    @Override
    public List<OrderItemDto> fromOrders(List<OrderItem> orderItems) {
        if ( orderItems == null ) {
            return null;
        }

        List<OrderItemDto> list = new ArrayList<OrderItemDto>( orderItems.size() );
        for ( OrderItem orderItem : orderItems ) {
            list.add( orderItemToOrderItemDto( orderItem ) );
        }

        return list;
    }

    protected OrderItem orderItemDtoToOrderItem(OrderItemDto orderItemDto) {
        if ( orderItemDto == null ) {
            return null;
        }

        OrderItem orderItem = new OrderItem();

        orderItem.setName( orderItemDto.getName() );
        orderItem.setQuantity( orderItemDto.getQuantity() );

        return orderItem;
    }

    protected OrderItemDto orderItemToOrderItemDto(OrderItem orderItem) {
        if ( orderItem == null ) {
            return null;
        }

        OrderItemDto orderItemDto = new OrderItemDto();

        orderItemDto.setName( orderItem.getName() );
        orderItemDto.setQuantity( orderItem.getQuantity() );

        return orderItemDto;
    }
}

带子集列表复制
import com.cy.mapstruct.entity.Customer;
import com.cy.mapstruct.entity.CustomerDto;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.IterableMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.Qualifier;

import java.util.List;

@Mapper(componentModel = "spring")
public interface ListChildrenConvertor {

    @IterableMapping(qualifiedByName = "toCustomer")
    List<Customer> toCustomers(List<CustomerDto> customerDtos);

    @Named("toCustomer")
    @Mappings({
            @Mapping(source = "orders", target = "orderItems"),
            @Mapping(source = "customerName", target = "name"),
    })
    Customer toCustomer(CustomerDto customerDto);
}

import com.cy.mapstruct.entity.Customer;
import com.cy.mapstruct.entity.CustomerDto;
import com.cy.mapstruct.entity.OrderItem;
import com.cy.mapstruct.entity.OrderItemDto;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2023-05-12T21:05:27+0800",
    comments = "version: 1.5.3.Final, compiler: javac, environment: Java 1.8.0_333 (Oracle Corporation)"
)
@Component
public class ListChildrenConvertorImpl implements ListChildrenConvertor {

    @Override
    public List<Customer> toCustomers(List<CustomerDto> customerDtos) {
        if ( customerDtos == null ) {
            return null;
        }

        List<Customer> list = new ArrayList<Customer>( customerDtos.size() );
        for ( CustomerDto customerDto : customerDtos ) {
            list.add( toCustomer( customerDto ) );
        }

        return list;
    }

    @Override
    public Customer toCustomer(CustomerDto customerDto) {
        if ( customerDto == null ) {
            return null;
        }

        Customer customer = new Customer();

        customer.setOrderItems( orderItemDtoListToOrderItemCollection( customerDto.getOrders() ) );
        customer.setName( customerDto.getCustomerName() );
        customer.setId( customerDto.getId() );

        return customer;
    }

    protected OrderItem orderItemDtoToOrderItem(OrderItemDto orderItemDto) {
        if ( orderItemDto == null ) {
            return null;
        }

        OrderItem orderItem = new OrderItem();

        orderItem.setName( orderItemDto.getName() );
        orderItem.setQuantity( orderItemDto.getQuantity() );

        return orderItem;
    }

    protected Collection<OrderItem> orderItemDtoListToOrderItemCollection(List<OrderItemDto> list) {
        if ( list == null ) {
            return null;
        }

        Collection<OrderItem> collection = new ArrayList<OrderItem>( list.size() );
        for ( OrderItemDto orderItemDto : list ) {
            collection.add( orderItemDtoToOrderItem( orderItemDto ) );
        }

        return collection;
    }
}

五、常用注解

注解

注解描述
@Mapper指示此接口将用于生成映射实现
@Mapping将源和目标属性之间的映射指定为参数
@Mappings允许使用多个 @Mapping 注释指定映射
@InheritConfiguration继承父映射配置
@BeanMapping自定义要应用于映射器方法的 Bean 映射选项
@BeforeMapping在映射开始时执行方法
@AfterMapping在映射结束时执行方法
@IterableMapping将迭代器类型映射指定为参数
@ValueMapping为枚举类型或其他可枚举类型提供自定义值映射
@Named为参数提供一个自定义名称
@Context将用户提供的上下文参数注入 Mapper 方法
@AfterMappingConversion在映射后进行类型转换
@Qualifier为不同的 Mapper 或 Mapping 提供自定义限定符
@MapperDependency指定该 Mapper 接口依赖于另一个 Mapper 接口

例子

1. @Mapper 用于指示此接口将用于生成映射实现。
public interface PersonMapper {
    PersonDto personToPersonDto(Person person);
    List<PersonDto> personsToPersonDtos(List<Person> persons);
}

2. @Mapping 将源类型和目标类型之间的映射指定为参数。
public interface PersonMapper {
    @Mapping(source = "firstName", target = "name")
    PersonDto personToPersonDto(Person person);
}

3. @Mappings 允许使用多个 @Mapping 注释来指定映射关系。
public interface PersonMapper {
    @Mappings({
        @Mapping(source = "firstName", target = "name"),
        @Mapping(source = "age", target = "ageGroup")
    })
    PersonDto personToPersonDto(Person person);
}

4. @InheritConfiguration 继承父映射配置。
@Mapper(config = ParentMapper.class)
public interface ChildMapper {
    @InheritConfiguration
    ChildDto childToChildDto(Child child);
}

5. @BeanMapping 自定义要应用于映射器方法的 Bean 映射选项。
public interface PersonMapper {
    @BeanMapping(ignoreByDefault = true)
    @Mapping(source = "firstName", target = "name")
    PersonDto personToPersonDto(Person person);
}

6. @BeforeMapping 在映射开始时执行方法。
public interface PersonMapper {
    @BeforeMapping
    default void beforePersonToPersonDto(Person person, @MappingTarget PersonDto personDto) {
        // do something before mapping
    }
    PersonDto personToPersonDto(Person person);
}

7. @AfterMapping 在映射结束时执行方法。
public interface PersonMapper {
    @AfterMapping
    default void afterPersonToPersonDto(Person person, @MappingTarget PersonDto personDto) {
        // do something after mapping
    }
    PersonDto personToPersonDto(Person person);
}

8. @IterableMapping 将迭代器类型映射指定为参数。
public interface PersonMapper {
    @IterableMapping(dateFormat = "yyyy-MM-dd")
    List<String> datesToStrings(List<Date> dates);
}

9. @ValueMapping 为枚举类型或其他可枚举类型提供自定义值映射。
public interface ColorMapper {
    @ValueMapping(source = "RED", target = "0")
    @ValueMapping(source = "GREEN", target = "1")
    @ValueMapping(source = "BLUE", target = "2")
    int colorToInt(Color color);
}

10. @Named 为参数提供一个自定义名称。
public interface PersonMapper {
    PersonDto personToPersonDto(@Named("firstName") String name, @Named("old") int age);
}

11. @Context 将用户提供的上下文参数注入 Mapper 方法。
public interface PersonMapper {
    PersonDto personToPersonDto(Person person, @Context SecurityContext securityContext);
}

12. @AfterMappingConversion 在映射后进行类型转换。
public interface HumanMapper {
 
    @AfterMappingConversion
    default void afterPersonToPersonDtoConversion(Date birthDate, @MappingTarget HumanDto humanDto) {
        humanDto.setAge(getAge(birthDate));
    }
 
    default int getAge(Date birthDate) {
        // calculate age
        return 0;
    }
 
    HumanDto humanToHumanDto(Human human);
}

13. @Qualifier 为不同的 Mapper 或 Mapping 提供自定义限定符。
@Mapper(qualifier = "Child")
public interface PersonMapper {
    PersonDto personToPersonDto(Person person);
}

14. @MapperDependency 指定该 Mapper 接口依赖于另一个 Mapper 接口。
@Mapper(uses = {ParentMapper.class})
public interface ChildMapper {
    ChildDto childToChildDto(Child child);
}

使用 MapStruct 的这些注解,可以很方便地进行 Java Bean 的映射。

六、链接

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值