关注:CodingTechWork
在 Java 开发中,我们经常需要在不同的 Java Bean 之间进行数据映射,比如从实体类(Entity)到数据传输对象(DTO)的转换。传统的做法是手动编写大量的 setter 和 getter 方法来完成属性的赋值,这种方式不仅繁琐,而且容易出错。MapStruct 作为一个基于注解的代码生成工具,为我们提供了一种更加优雅、高效的解决方案。它在编译时自动生成映射代码,避免了运行时反射带来的性能开销,同时保证了类型安全。
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
// 通过 Mappers.getMapper 方法获取映射接口的实例
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
// 定义从 UserEntity 到 UserDTO 的映射方法
UserDTO toDTO(UserEntity entity);
// 定义从 UserDTO 到 UserEntity 的映射方法
UserEntity toEntity(UserDTO dto);
}
不一致
时,可以使用该注解进行显式映射
。import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
// 使用 @Mapping 注解指定 entityId 映射到 id,entityName 映射到 name
@Mapping(source = "entityId", target = "id")
@Mapping(source = "entityName", target = "name")
UserDTO toDTO(UserEntity entity);
// 反向映射
@Mapping(source = "id", target = "entityId")
@Mapping(source = "name", target = "entityName")
UserEntity toEntity(UserDTO dto);
}
多个属性映射
关系。import org.mapstruct.Mapper;
import org.mapstruct.Mappings;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mappings({
@Mapping(source = "entityId", target = "id"),
@Mapping(source = "entityName", target = "name")
})
UserDTO toDTO(UserEntity entity);
@Mappings({
@Mapping(source = "id", target = "entityId"),
@Mapping(source = "name", target = "entityName")
})
UserEntity toEntity(UserDTO dto);
}
import org.mapstruct.Mapper;
import org.mapstruct.Context;
import org.mapstruct.factory.Mappers;
import java.util.Locale;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
// 使用 @Context 注解传递 Locale 对象作为上下文信息
UserDTO toDTO(UserEntity entity, @Context Locale locale);
}
import org.mapstruct.Mapper;
import org.mapstruct.AfterMapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDTO toDTO(UserEntity entity);
// 使用 @AfterMapping 注解定义映射完成后的自定义逻辑
@AfterMapping
default void afterMapping(@MappingTarget UserDTO dto, UserEntity entity) {
// 将源对象的 firstName 和 lastName 拼接后赋值给目标对象的 fullName 属性
dto.setFullName(entity.getFirstName() + " " + entity.getLastName());
}
}
import lombok.Data;
// 用户实体类
@Data
public class UserEntity {
private Long id;
private String name;
private Integer age;
private String firstName;
private String lastName;
private AddressEntity address;
private Long entityId;
private String entityName;
}
// 用户数据传输对象类
@Data
public class UserDTO {
private Long id;
private String name;
private Integer age;
private String fullName;
private AddressDTO address;
private Long entityId;
private String entityName;
}
// 地址实体类
@Data
public class AddressEntity {
private String street;
private String city;
}
// 地址数据传输对象类
@Data
public class AddressDTO {
private String street;
private String city;
}
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
// 获取映射接口的实例
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
// 从 UserEntity 到 UserDTO 的映射方法
UserDTO toDTO(UserEntity entity);
// 从 UserDTO 到 UserEntity 的映射方法
UserEntity toEntity(UserDTO dto);
}
// 测试代码
public class MainSimpleMapping {
public static void main(String[] args) {
UserEntity userEntity = new UserEntity();
userEntity.setId(1L);
userEntity.setName("John");
userEntity.setAge(25);
// 使用映射接口的实例进行映射
UserDTO userDTO = UserMapper.INSTANCE.toDTO(userEntity);
System.out.println("简单映射示例结果:");
System.out.println("UserDTO: id=" + userDTO.getId() + ", name=" + userDTO.getName() + ", age=" + userDTO.getAge());
}
}
输出结果:
简单映射示例结果:
UserDTO: id=1, name=John, age=25
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mapping(source = "entityId", target = "id")
@Mapping(source = "entityName", target = "name")
UserDTO toDTO(UserEntity entity);
@Mapping(source = "id", target = "entityId")
@Mapping(source = "name", target = "entityName")
UserEntity toEntity(UserDTO dto);
}
// 测试代码
public class MainFieldNameMismatch {
public static void main(String[] args) {
UserEntity userEntity = new UserEntity();
userEntity.setEntityId(1L);
userEntity.setEntityName("John");
userEntity.setAge(25);
UserDTO userDTO = UserMapper.INSTANCE.toDTO(userEntity);
System.out.println("字段名不一致映射示例结果:");
System.out.println("UserDTO: id=" + userDTO.getId() + ", name=" + userDTO.getName() + ", age=" + userDTO.getAge());
}
}
输出结果:
字段名不一致映射示例结果:
UserDTO: id=1, name=John, age=25
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDTO toDTO(UserEntity entity);
UserEntity toEntity(UserDTO dto);
AddressDTO toDTO(AddressEntity entity);
AddressEntity toEntity(AddressDTO dto);
}
// 测试代码
public class MainNestedObjectMapping {
public static void main(String[] args) {
AddressEntity addressEntity = new AddressEntity();
addressEntity.setStreet("123 Main St");
addressEntity.setCity("New York");
UserEntity userEntity = new UserEntity();
userEntity.setId(1L);
userEntity.setName("John");
userEntity.setAddress(addressEntity);
UserDTO userDTO = UserMapper.INSTANCE.toDTO(userEntity);
System.out.println("嵌套对象映射示例结果:");
System.out.println("UserDTO: id=" + userDTO.getId() + ", name=" + userDTO.getName());
System.out.println("AddressDTO: street=" + userDTO.getAddress().getStreet() + ", city=" + userDTO.getAddress().getCity());
}
}
输出结果:
嵌套对象映射示例结果:
UserDTO: id=1, name=John
AddressDTO: street=123 Main St, city=New York
import org.mapstruct.Mapper;
import org.mapstruct.AfterMapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDTO toDTO(UserEntity entity);
@AfterMapping
default void afterMapping(@MappingTarget UserDTO dto, UserEntity entity) {
dto.setFullName(entity.getFirstName() + " " + entity.getLastName());
}
}
// 测试代码
public class MainCustomMappingLogic {
public static void main(String[] args) {
UserEntity userEntity = new UserEntity();
userEntity.setId(1L);
userEntity.setFirstName("John");
userEntity.setLastName("Doe");
UserDTO userDTO = UserMapper.INSTANCE.toDTO(userEntity);
System.out.println("自定义映射逻辑示例结果:");
System.out.println("UserDTO: id=" + userDTO.getId() + ", fullName=" + userDTO.getFullName());
}
}
输出结果:
自定义映射逻辑示例结果:
UserDTO: id=1, fullName=John Doe
import org.mapstruct.Mapper;
import org.mapstruct.Context;
import org.mapstruct.factory.Mappers;
import java.util.Locale;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDTO toDTO(UserEntity entity, @Context Locale locale);
default String localize(String value, @Context Locale locale) {
// 根据 locale 进行本地化处理
return value;
}
}
// 测试代码
public class MainWithContext {
public static void main(String[] args) {
UserEntity userEntity = new UserEntity();
userEntity.setId(1L);
userEntity.setName("John");
Locale locale = Locale.US;
UserDTO userDTO = UserMapper.INSTANCE.toDTO(userEntity, locale);
System.out.println("使用上下文示例结果:");
System.out.println("UserDTO: id=" + userDTO.getId() + ", name=" + userDTO.getName());
}
}
输出结果:
使用上下文示例结果:
UserDTO: id=1, name=John
通过以上示例可以看到,使用 MapStruct 能够方便快捷地完成 Java Bean 之间的映射,同时结合 Lombok 的 @Data 注解进一步简化了代码。并且从输出结果可以直观地验证各个映射场景的正确性。