说在最前面的事
下面的代码实现在文章末,如果觉得复制粘贴太麻烦,可以直接下载代码导入
故事
又是无所事事的一天,现在趁着下班时间把 MapStruct 写一下,为什么会有这东西呢?其实之前我没接触过这玩意儿,这不是到公司开始实习了嘛,然后就在一次培训时候说到这个玩意儿,之后下去学习了一下,你还别说,真的好用。现在我们来看看这是个啥玩意。
之前我们开始写类需要大量的 get、set 方法,之后 lombok 出现了,然后开始写 SQL,慢慢的出现了 mybatis,再之后呢,用 @Mapper 注解代替了 xml 配置文件注入,接着是个啥,mybatis-plus 出现了,直接再 mapper 接口上 extends BaseMapper< Bean >,在 service 接口上 extends IService< Bean > ,然后在实现类上 extends ServiceImpl< BeanMapper,Bean > implements interface 就 OK。
但是我们在很多的开发中,会有 entity,但是并不是所有的 entity 都能够适应程序中所需的对象,这样,VO、DO、BO、POJO 等等,他们都有一个共性就是都可能包含其他对象的多个字段。这时候我们需要将一个对象属性值 copy 到另一个对象里就需要大量的 get、set,这时候 MapStruct 就出现了
POJO :plain ordinary java object 无规则简单 java 对象
PO:persistent object 持久对象
VO:返回表示层对象
DO(Data Object):此对象与数据库表结构
DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象
BO(Business Object):业务对象,由 Service 层输出的封装业务逻辑的对象
AO(ApplicationObject):应用对象,在 Web 层与 Service 层之间抽象的复用对象模型, 极为贴近展示层,复用度不高
现在应该大概就明白了这是个啥玩意儿,它就是把一个对象的或者多个对象的属性值赋值给另一个对象,为了省去我们写大量 get、set 方法而出现的映射工具。这玩意儿,哎,啥也不是,接下来开干。
引入依赖
构建一个空的 maven 项目,之后引入依赖,install 后开干。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>mapStructDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.2.0.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.2.0.Final</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
简单 demo
好了,现在我们开始干,构建一个三个 bean 实例。分别为 user、user2、userDO
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.Date;
/**
* @author (https://www.wslhome.top)
* @description
* @date 2020-10-11 17:05
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
Integer id;
String name;
String sex;
String className;
String school;
String birthday;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author (https://www.wslhome.top)
* @description
* @date 2020-10-11 17:05
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User2 {
Integer id;
String name;
String sex;
String className;
String school;
String birthday;
}
最后一个 userDO
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.Date;
/**
* @author(https://www.wslhome.top)
* @description
* @date 2020-10-11 17:07
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class UserDO {
String userName;
String userId;
String userSex;
String userClassName;
String userSchool;
String phone;
Date date;
}
然后基本工作做郝了,见证奇迹的时候到了,写一个 conver 接口
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author (https://www.wslhome.top)
* 实体转换
* @date 2020-10-11 17:09
**/
@Mapper
public interface Converter {
Converter INSTANCE = Mappers.getMapper(Converter.class);
/**
* user转user2
**/
User2 UserToUser2(User user);
/**
* User转UserDO实现
* */
@Mappings(value = {
@Mapping(source = "id",target = "userId"),
@Mapping(source = "name", target = "userName"),
@Mapping(source = "sex", target = "userSex"),
@Mapping(source = "className",target = "userClassName" ),
@Mapping(source = "school",target = "userSchool"),
//使用expression指定处理target
@Mapping(target = "date",expression = "java(new java.util.Date())")
})
UserDO ConverUser2UserDO(User user);
}
看到没,看到没,就是这么简单。
在接口上打上 @Mapper 标记,注意包别引入错了,我们要引入的是 import org.mapstruct.Mapper;千万不要引成 mybatis 的。没错,就是这样就实现了。
然后 Converter INSTANCE = Mappers.getMapper(Converter.class);创建一个实例,也可在要用的地方创建。
也许你会说上面的为什么没有 @Mapping 或者 @Mapping 注解,下面的却要有。其实这就是做的一个映射。如果字段名不一致就需要用 @Mapping 来指定那个属性映射哪个属性,如果一个 bean 里面有很多属性,只要有超过两个属性名称字段不一样就用 @Mappings,如果字段名一样就什么都不用写
@Mapping 中有很多属性,作为入门 demo 就只需要知道 source 和 target 就可以了,source 指原来 bean 属性,target 即将要赋值的属性。从接口上可以容易看出属性是谁的转谁的。
接下来测试一下
import org.junit.jupiter.api.Test;
import java.util.Date;
/**
* @author (https://www.wslhome.top)
* @description 实体转化测试类
* @date 2020-10-11 17:10
**/
public class ConverterTest {
/**
* user转user2
* */
@Test
void userToUser2Test(){
User user = new User(123,"sirwsl","男","计算机科学与技术","YMU","1997-01-16");
User2 user2 = Converter.INSTANCE.UserToUser2(user);
System.out.println(user2);
}
/**
* user转userDO测试
* */
@Test
void user2UserDOTest(){
User user = new User(123,"sirwsl","男","计算机科学与技术","YMU","1997-01-16");
UserDO userDO = Converter.INSTANCE.ConverUser2UserDO(user);
System.out.println(userDO.toString());
}
}
多转一
当然在实际的开发的时候,往往是多个对象的属性转为一个特定的 bean。这时候就需要用多个转一个。直接来代码,废话不多说,照葫芦画瓢,要学习的文章末尾有压缩包,直接下来导入运行,不理解的自己敲一遍也就明白了。
建立四个实体集 Result、SC、StuAndRe、Student
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author(https://www.wslhome.top)
* @description 成绩表
* @date 2020-10-11 20:11
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Result {
private Integer id;
private String name;
private String type;
private Integer days;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author (https://www.wslhome.top)
* @description
* @date 2020-10-11 20:22
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class SC {
private Integer score;
private String level;
private String test;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author wangshilei (https://www.wslhome.top)
* @description 学生与成绩合并
* @date 2020-10-11 20:12
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class StuAndRe {
private Integer SId;
private String SName;
private String RName;
private String RType;
private Integer RDays;
private Integer score;
private String level;
private String test;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author (https://www.wslhome.top)
* @description 学生表
* @date 2020-10-11 20:10
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private Integer id;
private String name;
private String sex;
}
接下来还是一样,写一个 Conver 接口,名称自己随便名命一个就好了,然后记得加上 @Mapper 注解,别导错包。
import org.mapstruct.*;
import org.mapstruct.factory.Mappers;
/**
* @author wangshilei (https://www.wslhome.top)
* @description 转换接口
* @date 2020-10-11 20:12
**/
@Mapper
public interface Converter {
Converter DISTANCE = Mappers.getMapper(Converter.class);
/**
* Result与Student合并为StuAndRe
* */
@Mappings(value = {
@Mapping(source = "result.name",target = "RName"),
@Mapping(source = "result.type",target = "RType"),
@Mapping(source = "result.days",target = "RDays"),
@Mapping(source = "student.id",target = "SId"),
@Mapping(source = "student.name", target = "SName"),
// constant注入产量值
@Mapping(target = "score", constant = "80"),
@Mapping(target = "level", constant = "C4")
})
StuAndRe ResStu2StuAndRe(Result result,Student student);
/**
* 三个实体合并属性
* @param result :
* @param student :
* @param v1 :
* @param v2 :
* @return demo2.StuAndRe
* @author wangshilei
* @date 2020/10/12 10:29
**/
@InheritConfiguration(name = "ResStu2StuAndRe")
@Mappings(value = {
@Mapping( source = "v1",target = "score"),
@Mapping( source = "v2",target = "level")
})
StuAndRe AllString2StuAndRe(Result result,Student student,String v1,String v2);
/**
* Result、SC与Student合并为StuAndRe
**/
@Mappings(value = {
@Mapping(source = "result.name",target = "RName"),
@Mapping(source = "result.type",target = "RType"),
@Mapping(source = "result.days",target = "RDays"),
@Mapping(source = "student.id",target = "SId"),
@Mapping(source = "student.name", target = "SName"),
// 设置默认值
@Mapping(source = "sc.test",target = "test",defaultValue = "this is test")
})
StuAndRe All2StuAndRe(Result result,Student student,SC sc);
/**
* 跟新bean,@MappingTarget后面的对象不变**/
@InheritConfiguration(name = "All2StuAndRe")
void update(@MappingTarget StuAndRe stuAndRe,SC sc);
/**
* Result与字符串合并为StuAndRe
**/
@Mappings(value = {
@Mapping(source = "result.name",target = "RName"),
@Mapping(source = "result.type",target = "RType"),
@Mapping(source = "result.days",target = "RDays"),
@Mapping(source = "ids",target = "SId"),
@Mapping(source = "names", target = "SName"),
})
StuAndRe Result2StuAndRe(Result result,String ids,String names);
}
如果你看到这里有些地方要注意一下了
PS:我们建立接口的时候,其实也可以建立抽象类,interface 和 abstract 其实是一样的。
PS2:@InheritConfiguration 其实就是为了不重复,减少代码量。采用 @InheritConfiguration 然后指定方法名,然后就可以将指定的哪个方法的 @Mappers 中的实现全部复制下来。
PS3:@Mapper 中有 constant 属性,是用来给定常量值的
PS4:defaultValue 属性是当它为 null 时候会进行赋值
PS5:@MappingTarget 是用来更新 bean 用的,它后面的 bean 值不改变,另一个更新。(可能说错了,有大佬还希望指出。自己写 demo 时候发现就是这样的)
接下来开始写测试
import org.junit.jupiter.api.Test;
/**
* @author (https://www.wslhome.top)
* @description
* @date 2020-10-11 20:13
**/
public class DemoTest {
/**
* Result与Student实体合并
* */
@Test
void StuAndReTest(){
Student student = new Student(123,"sirwsl","男");
Result result = new Result(1,"MapStruct","Java",3);
StuAndRe stuAndRe = Converter.DISTANCE.ResStu2StuAndRe(result,student);
System.out.println(stuAndRe.toString());
}
/**
* 接收原有参数
* Result与Student实体、字符串合并
* */
@Test
void AllStringAndReTest(){
Student student = new Student(123,"sirwsl","男");
Result result = new Result(1,"MapStruct","Java",3);
StuAndRe stuAndRe = Converter.DISTANCE.AllString2StuAndRe(result,student,"80","A1");
System.out.println(stuAndRe.toString());
}
/**
* Result\SC\Student三个实体合并
* **/
@Test
void StuAndReTest2(){
Student student = new Student(123,"sirwsl","男");
Result result = new Result(1,"MapStruct","Java",3);
SC sc = new SC(9,"B4",null);
StuAndRe stuAndRe = Converter.DISTANCE.All2StuAndRe(result,student,sc);
System.out.println(stuAndRe.toString());
}
/**
* 实体集更新
* **/
@Test
void update(){
Student student = new Student(100,"sirwsl","男");
Result result = new Result(1,"MapStruct","Java",3);
SC sc = new SC(9,"B4",null);
StuAndRe stuAndRe = Converter.DISTANCE.All2StuAndRe(result,student,sc);
System.out.println("更新前:"+stuAndRe.toString());
sc = new SC(100,"666666","test123456");
Converter.DISTANCE.update(stuAndRe,sc);
System.out.println("更新后:"+stuAndRe.toString());
}
/**
* Result实体与字符串
* **/
@Test
void StuAndReTest3(){
Result result = new Result(1,"MapStruct","Java",3);
StuAndRe stuAndRe = Converter.DISTANCE.Result2StuAndRe(result,"123","sirwsl");
System.out.println(stuAndRe.toString());
}
}
指定类型转化
当我们要转化一个类,但是其中一些属性需要经过计算之类的得到,大概就是这样。这里我们将 user1、user2 相互转化时有字符串转 bool 类型,自定义转换类 change 进行转换。
构建两个 bean 为 user1,user2
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author
* @date 2020/10/12-9:09
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User1 {
private Integer id;
private boolean flag;
private String str;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author
* @date 2020/10/12-9:09
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User2 {
private Integer id;
private String flag;
private String text;
}
然后再来一个转换类。
/**
* @author
* @date 2020/10/12-9:15
**/
public class Exchange {
public String boolToString(boolean flag){
if (flag) {
return "yes";
}else {
return "no";
}
}
public boolean stringToBool(String str){
if (str.equals("yes")){
return true;
}else if(str.equals("no")){
return false;
}
return false;
}
}
继续刚刚的操作,定义接口
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* @author
* @date 2020/10/12-9:33
**/
@Mapper(uses = {Exchange.class})
public interface Converter {
Converter DESTAINCE = Mappers.getMapper(Converter.class);
/**
* User1转User2
* @param user :
* @return demo3.User2
* @author wangshilei
* @date 2020/10/12 10:25
**/
@Mapping(source = "str",target = "text")
User2 user1ToUser2(User1 user);
}
来个 demoTest 看看
import org.junit.jupiter.api.Test;
/**
* @author WangShilei
* @date 2020/10/12-9:46
**/
public class DemoTest {
@Test
void user1ToUser2(){
User1 user1 = new User1(1,true,"tests");
User2 user2 = Converter.DESTAINCE.user1ToUser2(user1);
System.out.println(user2.toString());
}
}
集合转化
除了上面的当然还有集合转化
构建两个 bean 为 User1,User2.
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author
* @date 2020/10/12-10:38
**/
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Data
public class User1 {
private Integer id;
private String name;
private Integer age;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author
* @date 2020/10/12-10:38
**/
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Data
public class User2 {
private Integer id;
private String name;
}
然后接口
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* @author
* @date 2020/10/12-10:41
**/
@Mapper
public interface Converter {
Converter INSATNCE = Mappers.getMapper(Converter.class);
/**
* user1转User2 列表
* @param user : User1
* @return User2
* @author wangshilei
* @date 2020/10/12 10:42
**/
List<User2> user1ToUser2(List<User1> user);
}
然后测试
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
/**
* @author
* @date 2020/10/12-10:40
**/
public class DemoTest {
/**User1转User2**/
@Test
void user1ToUser2Test(){
List<User1> user1 = new ArrayList<>();
user1.add(new User1(1,"zhangSang",18));
user1.add(new User1(2,"sirWSL",22));
user1.add(new User1(3,"wangWu",13));
user1.add(new User1(4,"liSi",45));
List<User2> user2s = Converter.INSATNCE.user1ToUser2(user1);
System.out.println(user2s.toString());
}
}
枚举类型转化
构建三个实体 User、UserDO、UserPojo
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author
* @date 2020/10/12-11:14
**/
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Data
public class User {
public enum Grade{
//
BAD,GOODS,BEET,GRADE;
}
private Grade grade;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author WangShilei
* @date 2020/10/12-11:14
**/
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Data
public class UserDO {
public enum Level{
//
BAD,GOODS,BEET,GRADE;
}
private Level level;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author
* @date 2020/10/12-11:14
**/
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Data
public class UserPojo {
public enum Level{
//
BAD123,GOODS123,BEET123,GRADE123;
}
private Level level;
}
然后接口
import org.mapstruct.*;
import org.mapstruct.factory.Mappers;
/**
* @author WangShilei
* @date 2020/10/12-11:19
**/
@Mapper
public interface Converter {
Converter INSTANCE = Mappers.getMapper(Converter.class);
/**
* User转UserDO枚举类 枚举名称不同,值相同
* @param user :
* @return null
* @author wangshilei
* @date 2020/10/12 11:21
**/
@Mapping(source="grade", target="level")
UserDO userToUserDo(User user);
/**
* User转UserPOJO枚举类 枚举名称不同,值不同
* BAD,GOODS,BEET,GRADE;BAD123,GOODS123,BEET123,GRADE123;
* @param user :
* @return null
* @author wangshilei
* @date 2020/10/12 11:21
**/
@ValueMappings(value = {
@ValueMapping(source = "BAD",target = "BAD123"),
@ValueMapping(source = "GOODS",target = "GOODS123"),
@ValueMapping(source = "BEET",target = "BEET123"),
@ValueMapping(source = "GRADE",target = "GRADE123"),
//映射失败时值为null
@ValueMapping(source= MappingConstants.ANY_UNMAPPED, target=MappingConstants.NULL)
})
UserPojo.Level userToUserPojo(User.Grade user);
}
然后测试
import org.junit.jupiter.api.Test;
/**
* @author
* @date 2020/10/12-11:25
**/
public class DemoTest {
@Test
void userEnumTest(){
User user = new User();
user.setGrade(User.Grade.GRADE);
UserDO userDO = Converter.INSTANCE.userToUserDo(user);
System.out.println(userDO.toString());
}
@Test
void userEnumTest2(){
User user = new User();
user.setGrade(User.Grade.GRADE);
UserPojo.Level use= Converter.INSTANCE.userToUserPojo(user.getGrade());
System.out.println(use.toString());
}
}
写累了,md,之前的几个应该对这个东东有所了解了,如果枚举没看懂,总之也不常用,要学去问度娘去。
如何实现
其实看到这里要是使用,你肯定会使用了,但是如何实现的,你肯定还很懵逼,现在随便说说,帮助理解,我本就是一介莽夫,说不出什么好点的解释来,需要更好的自己看文档去。
在实现上,我们打上 mapper 注解之后,在我们运行时候,由于我们定义的是接口,所以在实现上它会对接口进行一个实现,例如接口时 Conver 那么他的实现类就是 ConverImpl,这个东西你可以在编译后的文件中看到,这里我们随便拉一个看看,帮助理解。
以第一个简单 demo 实现后的来看
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package demo1;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ConverterImpl implements Converter {
public ConverterImpl() {
}
public User2 UserToUser2(User user) {
if (user == null) {
return null;
} else {
User2 user2 = new User2();
user2.setId(user.getId());
user2.setName(user.getName());
user2.setSex(user.getSex());
user2.setClassName(user.getClassName());
user2.setSchool(user.getSchool());
user2.setBirthday(user.getBirthday());
return user2;
}
}
public UserDO ConverUser2UserDO(User user) {
if (user == null) {
return null;
} else {
UserDO userDO = new UserDO();
userDO.setUserSex(user.getSex());
userDO.setUserClassName(user.getClassName());
userDO.setUserName(user.getName());
if (user.getId() != null) {
userDO.setUserId(String.valueOf(user.getId()));
}
userDO.setUserSchool(user.getSchool());
userDO.setDate(new Date());
return userDO;
}
}
public User ConverUserDO2User(UserDO userDO) {
if (userDO == null) {
return null;
} else {
User user = new User();
if (userDO.getDate() != null) {
user.setBirthday((new SimpleDateFormat("yy-MM-dd hh:mm:ss")).format(userDO.getDate()));
}
user.setSex(userDO.getUserSex());
user.setName(userDO.getUserName());
user.setClassName(userDO.getUserClassName());
user.setId(this.getage(userDO.getUserId()));
return user;
}
}
public UserVO User2UserVO(User user) {
if (user == null) {
return null;
} else {
UserVO userVO = new UserVO();
userVO.setId(user.getId());
userVO.setName(user.getName());
userVO.setAge(this.getage(user.birthday));
return userVO;
}
}
}
大概就是这样,它编译时候会进行一个 get、set 实现就是这么简单。
nnd,就写到这里了,毕竟时利用下班后的休息时间写的。over
这里时上面的代码的压缩包:mapStruct.zip
这里时上面的代码的压缩包:mapStruct.zip
这里时上面的代码的压缩包:mapStruct.zip
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于