Mapstruct 配置使用详解

一、引入依赖

以 Maven 为例:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.5.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.5.Final</version>
    <scope>provided</scope>
</dependency>

<build>
    <plugins>    
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${lombok.version}</version>
                    </path>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>1.5.5.Final</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build> 
———————————————— 
版权声明:本文为CSDN博主「猩火燎猿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 
原文链接:https://blog.csdn.net/onlymscn/article/details/153517590


二、基本用法

1. 定义源对象和目标对象

public class UserEntity {
    private Long id;
    private String name;
    private Integer age;
    // getter/setter
}

public class UserDTO {
    private Long id;
    private String name;
    private Integer age;
    // getter/setter
}

2. 定义 Mapper 接口

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

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

    UserDTO entityToDto(UserEntity entity);

    UserEntity dtoToEntity(UserDTO dto);
}

3. 使用 Mapper

UserEntity entity = new UserEntity(1L, "Tom", 20);
UserDTO dto = UserMapper.INSTANCE.entityToDto(entity);

三、常用配置详解

1. 属性名不一致时

public class UserEntity {
    private String userName;
    // ...
}

public class UserDTO {
    private String name;
    // ...
}

@Mapper
public interface UserMapper {
    @Mapping(source = "userName", target = "name")
    UserDTO entityToDto(UserEntity entity);
}

2. 忽略某些字段

@Mapper
public interface UserMapper {
    @Mapping(target = "age", ignore = true)
    UserDTO entityToDto(UserEntity entity);
}

3. 字段类型转换

public class UserEntity {
    private String age; // String 类型
}

public class UserDTO {
    private Integer age; // Integer 类型
}

@Mapper
public interface UserMapper {
    @Mapping(source = "age", target = "age")
    UserDTO entityToDto(UserEntity entity);
}

MapStruct 支持常见类型自动转换,也可以自定义转换方法。

4. 多个字段合并/拆分

public class UserEntity {
    private String firstName;
    private String lastName;
}

public class UserDTO {
    private String fullName;
}

@Mapper
public interface UserMapper {
    @Mapping(target = "fullName", expression = "java(entity.getFirstName() + \" \" + entity.getLastName())")
    UserDTO entityToDto(UserEntity entity);
}

5. List、Set、Map 的映射

List<UserDTO> dtoList = UserMapper.INSTANCE.entityListToDtoList(entityList);

@Mapper
public interface UserMapper {
    List<UserDTO> entityListToDtoList(List<UserEntity> entityList);
}

6. 使用 @MapperConfig 做全局配置

@MapperConfig(
    componentModel = "spring",
    unmappedTargetPolicy = ReportingPolicy.IGNORE
)
public interface GlobalConfig {
}

然后在 Mapper 中引用:

@Mapper(config = GlobalConfig.class)
public interface UserMapper { ... }

7. 支持 Spring、Jsr330 等依赖注入

@Mapper(componentModel = "spring")
public interface UserMapper { ... }

这样可以通过 Spring 注入 Mapper。

8. 其他常用注解

  • @Mappings:多个 @Mapping 的集合(新版本可直接多个 @Mapping)
  • @InheritInverseConfiguration:反向映射
  • @AfterMapping@BeforeMapping:映射前后处理

四、常见问题

  1. 编译未生成实现类?
    • 检查是否引入了 mapstruct-processor 依赖,IDE 插件是否支持注解处理。
  2. Lombok 冲突?
    • 引入 lombok-mapstruct-binding
  3. 复杂嵌套映射?
    • 支持嵌套对象映射,定义多个 Mapper 并用 uses 属性引用。

五、进阶用法

1. 自定义方法和表达式

除了基本的字段映射,你可以在 Mapper 中定义自定义方法用于复杂转换:

@Mapper
public interface UserMapper {
    @Mapping(target = "fullName", expression = "java(concatName(entity.getFirstName(), entity.getLastName()))")
    UserDTO entityToDto(UserEntity entity);

    default String concatName(String firstName, String lastName) {
        return firstName + " " + lastName;
    }
}

2. 嵌套对象映射

如果 DTO 中包含嵌套对象,可以通过 uses 属性指定其他 Mapper:

public class AddressEntity { ... }
public class AddressDTO { ... }

@Mapper
public interface AddressMapper {
    AddressDTO entityToDto(AddressEntity entity);
}

public class UserEntity {
    private AddressEntity address;
    // ...
}

public class UserDTO {
    private AddressDTO address;
    // ...
}

@Mapper(uses = AddressMapper.class)
public interface UserMapper {
    UserDTO entityToDto(UserEntity entity);
}

3. @InheritConfiguration 和 @InheritInverseConfiguration

用于复用映射规则,减少重复代码:

@Mapper
public interface UserMapper {
    @Mapping(source = "userName", target = "name")
    UserDTO entityToDto(UserEntity entity);

    @InheritInverseConfiguration
    UserEntity dtoToEntity(UserDTO dto);
}

4. 默认值和常量

可以为目标字段设置默认值或常量:

@Mapping(target = "status", constant = "ACTIVE")
@Mapping(target = "age", defaultValue = "18")
UserDTO entityToDto(UserEntity entity);

5. 枚举类型映射

MapStruct 支持自动枚举类型映射,也可自定义:

public enum UserStatusEntity { ENABLED, DISABLED }
public enum UserStatusDTO { ACTIVE, INACTIVE }

@Mapper
public interface UserMapper {
    @Mapping(source = "status", target = "status")
    UserDTO entityToDto(UserEntity entity);

    default UserStatusDTO mapStatus(UserStatusEntity status) {
        if (status == UserStatusEntity.ENABLED) return UserStatusDTO.ACTIVE;
        else return UserStatusDTO.INACTIVE;
    }
}

6. @AfterMapping 和 @BeforeMapping

在映射前后做处理:

@Mapper
public interface UserMapper {
    UserDTO entityToDto(UserEntity entity);

    @AfterMapping
    default void afterMapping(@MappingTarget UserDTO dto, UserEntity entity) {
        dto.setName(dto.getName().toUpperCase());
    }
}

六、常用配置参数说明

参数名说明
componentModelMapper 实现类的注入方式(default、spring、jsr330、cdi)
uses引用其他 Mapper
unmappedTargetPolicy未映射字段的处理策略(IGNORE, WARN, ERROR)
mappingInheritanceStrategy映射继承策略(AUTO, EXPLICIT, NONE)

示例:

@Mapper(
    componentModel = "spring",
    uses = {AddressMapper.class},
    unmappedTargetPolicy = ReportingPolicy.WARN
)
public interface UserMapper { ... }

七、MapStruct 与 Spring 集成

如果你用 Spring Boot,可以这样:

@Mapper(componentModel = "spring")
public interface UserMapper { ... }

然后直接注入:

@Autowired
private UserMapper userMapper;

八、批量映射

MapStruct 支持集合类型的批量映射:

List<UserDTO> entityListToDtoList(List<UserEntity> entityList);
Set<UserDTO> entitySetToDtoSet(Set<UserEntity> entitySet);

九、常见实战场景

1. DTO 与 Entity 双向转换

定义双向方法,并用 @InheritInverseConfiguration 简化:

@Mapper
public interface UserMapper {
    @Mapping(source = "userName", target = "name")
    UserDTO entityToDto(UserEntity entity);

    @InheritInverseConfiguration
    UserEntity dtoToEntity(UserDTO dto);
}

2. 分页对象转换

@Mapper
public interface UserMapper {
    Page<UserDTO> entityPageToDtoPage(Page<UserEntity> entityPage);
}

(前提是你的分页对象有合适的 getter/setter)

3. 多层嵌套复杂对象转换

通过 uses 属性引用多个 Mapper,逐层转换。


十、MapStruct 性能与优点

  • 编译期生成代码,性能极高。
  • 类型安全,编译报错,减少运行期异常。
  • 易于维护,减少手写转换代码。

十一、常见问题排查

  1. 实现类未生成?
    • 检查 IDE 是否开启注解处理器(annotation processor)。
  2. Lombok 与 MapStruct 冲突?
    • 引入 lombok-mapstruct-binding,并确保 getter/setter 正确。
  3. 复杂类型映射失败?
    • 检查 uses 配置,确认所有 Mapper 都已声明。

十二、MapStruct 高级用法

1. 多源对象合并映射(多个参数)

有时目标对象需要来自多个源对象的数据。例如:

public class UserEntity { private String name; }
public class AddressEntity { private String city; }
public class UserDTO { private String name; private String city; }

@Mapper
public interface UserMapper {
    @Mapping(source = "user.name", target = "name")
    @Mapping(source = "address.city", target = "city")
    UserDTO toDto(UserEntity user, AddressEntity address);
}

2. 映射方法重载和条件映射

可以根据不同参数类型定义多个方法:

UserDTO toDto(UserEntity entity);
UserDTO toDto(UserEntity entity, String extraInfo);

@Mapping(target = "extraInfo", expression = "java(extraInfo)")
UserDTO toDto(UserEntity entity, String extraInfo);

3. 自定义类型转换器(Type Conversion)

对复杂类型或特殊格式进行转换:

@Mapper
public interface UserMapper {
    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
    UserDTO entityToDto(UserEntity entity);
}

或者使用自定义方法:

default String mapDate(LocalDate date) {
    return date != null ? date.format(DateTimeFormatter.ISO_DATE) : null;
}

4. 分组映射(@MappingTarget)

用于更新已存在的对象(例如数据库更新):

@Mapper
public interface UserMapper {
    void updateUserFromDto(UserDTO dto, @MappingTarget UserEntity entity);
}

这样可以只更新部分字段,而不是创建新对象。


十三、调试与代码生成检查

1. 查看 MapStruct 生成的实现类

MapStruct 在编译时生成实现类,一般在 target/generated-sources/annotations 目录下。
你可以直接查看生成的代码,了解转换细节,定位问题。

2. IDE 设置注解处理器

  • IntelliJ IDEA:Settings -> Build, Execution, Deployment -> Compiler -> Annotation Processors,勾选“Enable annotation processing”。
  • Eclipse:Project -> Properties -> Java Compiler -> Annotation Processing,开启注解处理。

3. 日志与错误提示

MapStruct 的错误和警告会在编译期给出,建议 unmappedTargetPolicy 设置为 WARN 或 ERROR,及时发现未映射字段。


十四、与常用技术框架集成

1. 与 Spring Boot 集成

如前所述,设置 @Mapper(componentModel = "spring"),然后用 @Autowired 注入即可。

2. 与 MyBatis、JPA/Hibernate 集成

MapStruct 主要用于 DTO 和 Entity 的转换,和 MyBatis Mapper 或 JPA Repository 配合非常方便。
你可以在 Service 层用 MapStruct 进行数据转换,然后调用持久层操作。

3. 与 Lombok 集成

  • 推荐引入 lombok-mapstruct-binding
  • 保证实体类 @Data@Getter@Setter 注解齐全。
  • 避免用 Lombok 的 @Builder 造成构造器冲突。

十五、最佳实践与建议

  1. 保持 Mapper 接口简洁,只做映射,不做业务逻辑。
  2. 复杂转换用自定义方法或表达式,避免在 Mapper 里写复杂代码。
  3. 充分利用全局配置(@MapperConfig),统一 unmappedTargetPolicy、componentModel 等参数。
  4. 嵌套对象用 uses 引用其他 Mapper,保持结构清晰。
  5. 及时查看和维护 MapStruct 生成的代码,发现潜在问题。

十六、常见业务场景示例

1. 表单对象(Form)与 DTO/Entity 转换

@Mapper(componentModel = "spring")
public interface UserFormMapper {
    UserDTO formToDto(UserForm form);
    UserForm dtoToForm(UserDTO dto);
}

2. 响应对象(VO)与 DTO 转换

@Mapper(componentModel = "spring")
public interface UserVoMapper {
    UserVO dtoToVo(UserDTO dto);
}

3. 批量数据处理

List<UserDTO> entitiesToDtos(List<UserEntity> entities);

4. 分页结果转换

假设你用的是 MyBatis Plus 或 Spring Data 的分页对象:

@Mapper(componentModel = "spring")
public interface PageMapper {
    default <T, U> Page<U> pageToPage(Page<T> page, Function<T, U> converter) {
        List<U> records = page.getRecords().stream().map(converter).collect(Collectors.toList());
        return new PageImpl<>(records, page.getPageable(), page.getTotalElements());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猩火燎猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值