开发中我们可能使用JPA、通用Mapper或者MyBatis-Plus去查询数据,而这类框架都是返回的实体,实体是和数据库中的表是一一对应的,而作为接口的返回值我们无需把整个实体都暴露给前端,通常会将实体转换成另一个DTO对象来返回,通常有以下转换方式:
- 自己写代码手动转换(这种方式代码量多,而且不优雅)
- BeanUtils.copyProperties(entity, dto),这种方式是通过反射来实现的,一般反射效率相对有点低
- 使用ModelMapper框架(这种方式是通过反射来实现的,一般反射效率相对有点低)
- 使用MapStruct框架(这种方式是在编译器自动生成转换代码,将原来的手动改为自动,相对于使用反射实现的此种方式效率更好)
综合比较性能、问题排查、文档、成熟度、扩展性等因素来考虑,MapStruct 是一个不错的选择,实体映射转换各个工具比较 https://java.libhunt.com/compare-mapstruct-vs-selma
- MapStruct官网地址: http://mapstruct.org/,
- GitHub示例程序:https://github.com/mapstruct/mapstruct-examples
1. 添加依赖 和 配置插件
注意:如果使用了lombok应尽量使用比较高的版本,maven-compiler-plugin 插件也最好使用较高的版本。否则有可能报这个错:
Error:(12, 5) java: No property named “xxx” exists in source parameter(s). Did you mean “null”?
maven pom.xml:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<org.mapstruct.version>1.4.1.Final</org.mapstruct.version>
<org.projectlombok.version>1.18.12</org.projectlombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${org.projectlombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<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>
<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>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
2. 实体
@Getter
public enum UserStatusEnum {
NORMAL(0, "正常"),
LOCK(1, "锁定");
private Integer code;
private String desc;
UserStatusEnum(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
}
@Data
public class UserInfo {
private String address;
private String remark;
}
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private Long id;
private String name;
private String password;
private UserStatusEnum userStatusEnum;
private Date createTime;
private UserInfo userInfo;
}
3. DTO
@NoArgsConstructor
@AllArgsConstructor
@Data
public class UserDTO {
private Long id;
private String realName;
private Integer status;
private String address;
private String password;
private String createTimeFormat;
}
4. 实体与DTO属性映射配置
@Mapper(componentModel="spring")
public interface UserConverter {
@Mappings({
@Mapping(source = "name", target = "realName"),
@Mapping(target = "status", expression = "java(user.getUserStatusEnum().getCode())"),
@Mapping(source = "createTime", target = "createTimeFormat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
@Mapping(source = "userInfo.address", target = "address"),
@Mapping(target = "password", ignore = true)
})
UserDTO entity2dto(User user);
}
- @Mapper 只有在接口加上这个注解,MapStruct 才会去实现该接口,@Mapper 里有个 componentModel 属性,主要是指定实现类的类型,一般用到两个:
- default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象。
- spring:在接口的实现类上自动添加注解 @Component,可通过 @Autowired 方式注入。
- @Mappings:配置多个@Mapping
- @Mapping:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性
- source:源属性
- target:目标属性
- dateFormat:String 到 Date 日期之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat的日期格式
- expression:使用Java方法来格式化值
- ignore: 忽略这个字段
@Mapper可以单独放在一个类中配置,也可以在JPA或者MyBatis中的Mapper类中来配置。
手工编译(mvn compile)或者启动 IDE 的时候, 会自动在 target/classes 下生成对应的实现类。
5. Test
@SpringBootTest
class SpringbootMapstructApplicationTests {
@Autowired
private UserConverter userConverter;
@Test
public void testMapStruct() {
UserInfo userInfo = new UserInfo();
userInfo.setAddress("上海市");
userInfo.setRemark("此人非常懒");
User user = new User();
user.setId(1L);
user.setName("周某人");
user.setPassword("123456");
user.setUserStatusEnum(UserStatusEnum.LOCK);
user.setCreateTime(new Date());
user.setUserInfo(userInfo);
UserDTO userDTO = userConverter.entity2dto(user);
System.out.println(userDTO);
}
}