初识org.mapstruct:mapstruct
作者:互联网
文章目录
前言
最近发现了个很好玩的工具包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 /> <!-- lookup parent from repository -->
</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 // 和 Mybatis的很像哦
public interface MapStructInf {
/**
* 获取该类自动生成的实现类的实例
* 接口中的属性都是 public static final 的 方法都是public abstract的
*/
MapStructInf instances = Mappers.getMapper(MapStructInf.class);
/**
* 这个方法就是用于实现对象属性复制的方法
*
* @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
*
* @param userDto 这个参数就是 源对象,也就是需要 被复制 的对象
* @return 返回的是 目标对象,就是最终的结果对象
*/
@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 // 和 Mybatis的很像哦
public interface MapStructInf {
/**
* 获取该类自动生成的实现类的实例
* 接口中的属性都是 public static final 的 方法都是public abstract的
*/
MapStructInf instances = Mappers.getMapper(MapStructInf.class);
/**
* 这个方法就是用于实现对象属性复制的方法
*
* @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
*
* @param userDto 这个参数就是 源对象,也就是需要 被复制 的对象
* @return 返回的是 目标对象,就是最终的结果对象
*/
// @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);
}
编写测试类进行测试:
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 // 和 Mybatis的很像哦
public interface MapStructInf {
/**
* 获取该类自动生成的实现类的实例
* 接口中的属性都是 public static final 的 方法都是public abstract的
*/
MapStructInf instances = Mappers.getMapper(MapStructInf.class);
/**
* 这个方法就是用于实现对象属性复制的方法
*
* @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
*
* @param userDto 这个参数就是 源对象,也就是需要 被复制 的对象
* @return 返回的是 目标对象,就是最终的结果对象
*/
@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;
// 注意这里一定要是 static
// expression 只能调用静态方法
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 // 和 Mybatis的很像哦
public interface MapStructInf2 {
/**
* 获取该类自动生成的实现类的实例
* 接口中的属性都是 public static final 的 方法都是public abstract的
*/
MapStructInf2 instances = Mappers.getMapper(MapStructInf2.class);
/**
* 这个方法就是用于实现对象属性复制的方法
*
* @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
*
* @param userDto 这个参数就是 源对象,也就是需要 被复制 的对象
* @return 返回的是 目标对象,就是最终的结果对象
*/
@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);
}
执行后的结果如下所示:
参考资料
代码下载
标签:mapstruct,Mapping,初识,import,org,UserDto,public 来源: https://blog.csdn.net/qq_38322527/article/details/123647995