【Mapstruct】MapStruct实战:简化Java Bean映射

虽然早就在用mapstruct了,但因为要快速原型开发,天天写builder模式,感觉太长了,不好看,(然后最近被同事说丑了 ),感觉还是做个总结,怒转mapstruct

问题背景或前提知识

在现代软件开发中,数据在不同层之间的转换是很常见的场景。这通常包括将数据实体(Entity)转换为数据传输对象(DTO),或反之。手动转换不仅效率低下,而且容易出错。MapStruct作为一种解决方案,通过自动生成映射代码,来减少手动编写的需要。
在 Java 中,除了使用 MapStruct 这样的库来自动化 bean 到 bean 的映射,还有其他几种方式可以实现相同的目标。这些方法各有利弊,适用于不同的场景:

手动映射:
最基本的方法是手动编写代码来映射对象。这意味着为每个需要映射的字段编写 get 和 set 方法调用。这种方法简单直观,但当涉及到大量字段或频繁更改时,会变得冗长且难以维护。

Apache Commons BeanUtils:
使用 BeanUtils 类可以轻松复制属性值。这个库提供了方法来动态地复制对象之间的属性,但它使用反射,可能比编译时生成的代码慢。

技术名词解释
  • DTO(Data Transfer Object): 用于应用层之间数据传输的对象。
  • Entity: 通常对应数据库中的表,用于表示数据的持久化形式。
  • MapStruct: 一种代码生成工具,它遵循约定大于配置的原则,自动化生成类型安全的Bean映射代码。

具体代码与实现方法

User 实体类
import java.time.LocalDateTime;

public class User {
    private String name;
    private String email;
    private String password;
    private LocalDateTime lastLogin;
    private Type type;
    private String streetName;

    // 构造方法,getters 和 setters省略
}
Type 枚举
public enum Type {
    ADMIN,
    USER,
    GUEST;
    // 枚举方法省略
}
Address 类 (假设的)
public class Address {
    private String street;
    private String city;
    private String zipCode;

    // 构造方法,getters 和 setters省略
}
UserDTO 类
import java.time.LocalDate;

public class UserDTO {
    private String name;
    private String email;
    private String userStatus;
    private LocalDate registrationDate;
    private String status;
    private String userType;
    private String addressStreet;

    // 构造方法,getters 和 setters省略
}

MapStruct映射接口示例

接下来,定义一个MapStruct接口UserMapper,展示如何使用@Mapping注解实现上述映射策略。

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

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

    @Mapping(target = "status", defaultValue = "NEW")
    @Mapping(target = "registrationDate", expression = "java(java.time.LocalDate.now())")
    @Mapping(target = "password", ignore = true)
    @Mapping(target = "userStatus", source = "user", qualifiedByName = "isActiveUser")
    @Mapping(target = "userType", source = "type")
    @Mapping(target = "addressStreet", source = "streetName")
    UserDTO userToUserDTO(User user);

    @Named("isActiveUser")
    default String isActiveUser(User user) {
        return user.getLastLogin() != null && user.getLastLogin().isAfter(LocalDateTime.now().minusMonths(1)) ? "Active" : "Inactive";
    }

    // 枚举映射方法和其他自定义逻辑可以根据需要添加
}

在这个UserMapper接口中,我们使用@Mapping注解来实现了几种不同的映射策略:

  • 使用默认值:为status字段设置了默认值"NEW"。
  • 使用表达式:使用Java表达式为registrationDate字段设置当前日期。
  • 忽略字段:忽略了password字段,不将其包含在DTO中。
  • 使用条件映射:通过自定义方法isActiveUseruserStatus字段设置值,基于用户的lastLogin日期。
  • 直接映射和嵌套对象映射:将userType从枚举Type映射,并将streetName映射到addressStreet

复杂点的处理

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

    // 现有的单个User到UserDTO的映射
    @Mapping(target = "addressStreet", source = "address.street")
    UserDTO userToUserDTO(User user, Address address);

    // 添加一个默认方法来处理列表转换
    default List<UserDTO> usersToUserDTOs(List<User> users, List<Address> addresses) {
        List<UserDTO> userDTOs = new ArrayList<>();
        for (int i = 0; i < users.size(); i++) {
            User user = users.get(i);
            Address address = addresses.get(i); // 假设列表是对齐的
            userDTOs.add(userToUserDTO(user, address));
        }
        return userDTOs;
    }
}

未使用@Mapping注解的字段处理:

MapStruct默认会尝试自动映射那些在源对象和目标对象中名称和类型都相同的字段。如果字段名和类型在两个类中完全一致,MapStruct会自动映射这些字段,不需要显式使用@Mapping注解。

对于名称或类型不匹配的字段,如果没有通过@Mapping注解指定映射规则,这些字段将不会被自动映射,也不会影响其他字段的映射。这意味着,如果你不处理这些字段,它们在目标对象中将保持默认值(如null、0或false等)。

原理: MapStruct在编译时生成实现映射接口的类。这个过程中,它会检查源对象和目标对象的字段,基于字段的名称和类型来自动生成映射代码。如果使用了@Mapping注解,MapStruct会根据注解提供的信息来生成相应的映射代码。这个过程完全在编译时完成,因此运行时性能很好,并且没有反射或运行时代理的开销。

总结和额外补充内容

MapStruct通过减少样板代码,提高了开发效率并降低了出错概率。它支持多种复杂的映射情况,包括但不限于默认值、常量、自定义方法以及多

  • 15
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java MapStruct是一个代码生成器,用于处理Java bean之间的映射。它通过在编译时生成映射代码来提高性能,并且可以自定义映射逻辑。以下是使用Java MapStruct的步骤: 1. 添加MapStruct依赖项到Maven或Gradle项目中。 2. 创建一个Java接口,该接口定义了要映射的源和目标bean之间的映射方法。 3. 在接口上使用@Mapper注释,指定MapStruct生成的实现类的名称。 4. 在映射方法上使用@Mapping注释,指定源和目标bean属性之间的映射关系。 5. 在Maven或Gradle项目中运行编译命令,以生成MapStruct实现类。 6. 在代码中使用MapStruct生成的实现类来执行bean之间的映射。 下面是一个使用Java MapStruct的简单示例: 1. 添加MapStruct依赖项到Maven或Gradle项目中。 ```xml <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-jdk8</artifactId> <version>1.4.2.Final</version> </dependency> ``` 2. 创建一个Java接口,该接口定义了要映射的源和目标bean之间的映射方法。 ```java @Mapper public interface CarMapper { CarDto carToCarDto(Car car); } ``` 3. 在接口上使用@Mapper注释,指定MapStruct生成的实现类的名称。 ```java @Mapper(componentModel = "spring") public interface CarMapper { CarDto carToCarDto(Car car); } ``` 4. 在映射方法上使用@Mapping注释,指定源和目标bean属性之间的映射关系。 ```java @Mapper(componentModel = "spring") public interface CarMapper { @Mapping(source = "numberOfSeats", target = "seatCount") CarDto carToCarDto(Car car); } ``` 5. 在Maven或Gradle项目中运行编译命令,以生成MapStruct实现类。 6. 在代码中使用MapStruct生成的实现类来执行bean之间的映射。 ```java @Autowired private CarMapper carMapper; public void example() { Car car = new Car("Morris", 5); CarDto carDto = carMapper.carToCarDto(car); } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值