前言
最近发现了个很好玩的工具包org.mapstruct ,里面处理类型转换的功能总觉得很高大上。特此写一篇博客记录测试、使用心得。
简介
平时的开发中,针对与数据库做数据交互 操作时,一般定义一个vo 或者pojo 类,在接收前端页面的参数信息时,会采取定义一个dto 类的形式。
但是在开发中,难免会碰见需要将 dto 类转换为对应的 vo 类 ,达到和数据库数据交互的目的。
以前,我最喜欢采取自己手写get/set 的代码,手动将数据信息进行转换。稍微用得高大上点的就是采取BeanUtils.copyProperties(source,target) 。
但机缘巧合下,发现了更好用的工具类org.mapstruct:mapstruct 。
他能够让类型转换之间更简单快捷,其次还支持部分参数调用指定Java代码解析!
接下来就一起见证下他的神奇之处。
依赖导入
本次测试环境采取Springboot 2.1.4.RELEASE 和mapstruct-processor 1.3.0.Final 。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.3.0.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.0.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
常见的几种处理方式
原始方式 get/set
既然文章开头说到pojo 和dto 两种类型的转换操作,接下来就新建这两个类。 User.java :
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
UserDto.java :
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDto {
private Long id;
private String name;
private Integer age;
private String email;
}
一般的类型转换,参考下列测试类:
import cn.xj.StartApplication;
import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = StartApplication.class)
@Slf4j
public class Test1 {
UserDto userDto = null;
@Before
public void before(){
userDto = new UserDto(1L,"xiangjiao dto",22,null);
}
@Test
public void test1(){
User user = new User();
user.setId(userDto.getId());
user.setName(userDto.getName());
user.setAge(userDto.getAge());
user.setEmail(userDto.getEmail());
System.out.println(user);
}
}
采取一般的get/set 方式,手动 将指定的类中的数据转换至指定的类中 。
BeanUtils.copyProperties 实现
BeanUtils.copyProperties 也能实现类似上面的功能,如下所示:
UserDto userDto = null;
@Before
public void before(){
userDto = new UserDto(1L,"xiangjiao dto",22,null);
}
@Test
public void test2(){
User user = new User();
BeanUtils.copyProperties(userDto,user);
System.out.println(user);
}
主角 mapstruct 登场
1、简单使用
mapstruct 如果需要实现上面的功能,需要编写一个接口 ,如下所示:
package cn.xj.interfaces;
import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface MapStructInf {
MapStructInf instances = Mappers.getMapper(MapStructInf.class);
@Mappings({
@Mapping(source = "id",target = "id"),
@Mapping(source = "name",target = "name"),
@Mapping(source = "age",target = "age"),
@Mapping(source = "email",target = "email")
})
User tranceToUser(UserDto userDto);
}
编写一个测试类,进行测试:
import cn.xj.StartApplication;
import cn.xj.interfaces.MapStructInf;
import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = StartApplication.class)
@Slf4j
public class MapstructTest {
UserDto userDto = null;
@Before
public void before(){
userDto = new UserDto(1L,"xiangjiao dto",22,null);
}
@Test
public void test1(){
User user = MapStructInf.instances.tranceToUser(userDto);
System.out.println(user);
}
}
初步一看,有些观众大姥爷可能会说:
写这么一大篇的接口 ,就为了转换一个类,吃多了吧!
上面只是简单的使用测试,接下来看点有意思的。
2、转换集合类
继续在指定的cn.xj.interfaces.MapStructInf 接口类中,定义一个转换集合的方法。如下所示:
import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface MapStructInf {
MapStructInf instances = Mappers.getMapper(MapStructInf.class);
List<User> tranceToUserList(List<UserDto> userDtoList);
}
编写测试类进行测试:
import cn.xj.StartApplication;
import cn.xj.interfaces.MapStructInf;
import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = StartApplication.class)
@Slf4j
public class MapstructTest {
List<UserDto> userDtoList;
@Before
public void before(){
userDtoList = Arrays.asList(
new UserDto(1L,"xiangjiao dto",11,null),
new UserDto(2L,"xiangjiao dto",22,null),
new UserDto(3L,"xiangjiao dto",33,null),
new UserDto(4L,"xiangjiao dto",44,null));
}
@Test
public void test2(){
List<User> users = MapStructInf.instances.tranceToUserList(userDtoList);
users.forEach(e->{
System.out.println(e);
});
}
}
结果却是可行的。
【注意:】这里有个小细节!
如果两个类的属性名是一样的,可以执行成功! 如果两个类的属性名 不同,则数据转换失败!!
为了测试这个问题,则需要将User.java 类中的属性名和UserDto.java 类中的属性名区分开。如下所示:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long ids;
private String names;
private Integer ages;
private String emails;
}
再次指定上面的test2 测试方法,控制台打印日志信息如下所示: 【疑问:】 当这种问题出现,如何解决呢?
只需要增加一个配置方法!
import cn.xj.pojo.User;
import cn.xj.pojo.UserDto;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface MapStructInf {
MapStructInf instances = Mappers.getMapper(MapStructInf.class);
@Mappings({
@Mapping(source = "id",target = "ids"),
@Mapping(source = "name",target = "names"),
@Mapping(source = "age",target = "ages"),
@Mapping(source = "email",target = "emails")
})
User tranceToUser(UserDto userDto);
List<User> tranceToUserList(List<UserDto> userDtoList);
}
再次执行test2() ,可以看到控制台打印日志如下所示: 【原因:】
当两个类之间 的属性别名不一致 时,可以采取定义单个类转换的关系 ,实现集合数据的转换!
3、进阶技能 expression
除了上面的基本操作之外,mapstruct 还能支持转换时,采取Java代码方式转换。
比如:
定义一个性别枚举类,但是User.java 类接收的是性别名称,UserDto.java 类中却传递的是性别编号 。
首先,定义一个枚举类,提供可以根据编号查询名称的函数 :
package cn.xj.pojo2;
public enum SexEnum {
man(1,"男"),
woman(2,"女");
private Integer value;
private String name;
public static String getValByName(Integer value){
String names = null;
for (SexEnum sexEnum : values()){
Integer value1 = sexEnum.getValue();
if(value1.equals(value)){
names = sexEnum.getName();
break;
}
}
return names;
}
SexEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
再修改对应的User.java 类:
保证User.java 类接收性别别名 。
package cn.xj.pojo2;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long ids;
private String names;
private Integer ages;
private String emails;
private String sex;
}
定义前端页面数据接收类UserDto.java :
保证性别信息采取编号接收 。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDto {
private Long id;
private String name;
private Integer age;
private String email;
private Integer sexNum;
}
定义转换方式:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface MapStructInf2 {
MapStructInf2 instances = Mappers.getMapper(MapStructInf2.class);
@Mappings({
@Mapping(source = "id",target = "ids"),
@Mapping(source = "name",target = "names"),
@Mapping(source = "age",target = "ages"),
@Mapping(source = "email",target = "emails"),
@Mapping(target = "sex",expression = "java(cn.xj.pojo2.SexEnum.getValByName(userDto.getSexNum()))")
})
User tranceToUser(UserDto userDto);
}
执行后的结果如下所示:
参考资料
org.mapstruct:mapstruct 包的使用
mapstruct 实体转换及List转换
代码下载
gitee 仓库地址
|