前置条件
maven 引入依赖
<properties>
<java.version>11</java.version>
<java.version>1.8</java.version>
<org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.2</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
1-lombok(若是使用lombok,不仅仅是@Data,还需要别的注解,具体google),2-mapstruct ,3-hutool-all
public class Student {
private String name;
private String sex;
private Integer age;
// 对应person -> tall
private Long height;
private Integer rank;
/** long 形式的时间格式 */
private Long time;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Long getHeight() {
return height;
}
public void setHeight(Long height) {
this.height = height;
}
public Integer getRank() {
return rank;
}
public void setRank(Integer rank) {
this.rank = rank;
}
public Long getTime() {
return time;
}
public void setTime(Long time) {
this.time = time;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", height=" + height +
", rank=" + rank +
", time=" + time +
'}';
}
}
public class Person {
private String name;
private String sex;
private Integer age;
private String tall;
private String time;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getTall() {
return tall;
}
public void setTall(String tall) {
this.tall = tall;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
写MapStruct 接口
注意点
1- student - height 与 person-tall 要相互复制的。类型和名字都不同
2-student-time 与 person-time 名字相同,类型不同
结论
1-对于 string 类型与 基本类型之间,若名字相同,类型不同,可直接转化(不要手动干预)
2-如时间,string与long的转化需要定义方法
import cn.hutool.core.date.DateUtil;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.sql.Time;
/* @Mapping(source = "time",target = "time",expression = "java(StructHelper.DateTimeStrToTimeMillis(person.getTime()))"*/
@Mapper(componentModel = "spring", uses = StructHelper.class)
public interface MapStructTest {
// MapStructTest INSTANCE = Mappers.getMapper(MapStructTest.class);
@Mappings({
@Mapping(source = "tall", target = "height"),
@Mapping(source = "time", target = "time", qualifiedByName = StructConstant.DATE_TIME_STR_TO_TIME_MILLIS)
})
Student personToStudent(Person person);
// @Named(value = "dateTimeStrToTimeMillis")
// default Long DateTimeStrToTimeMillis(String dataTimeStr) {
//
// return DateUtil.parseDateTime(dataTimeStr).getTime();
// }
}
方式一:expression
缺点:需要写 全限定类型。不好维护
@Mapping(source = "time",target = "time",expression = "java(StructHelper.DateTimeStrToTimeMillis(person.getTime()))
**方式二:qualifiedByName **
优点:维护性比较高。
我这里定义了常量。方便 Ctrl+鼠标左键,进行点击。
若定义转换方法不多的话,可以直接写在这个接口里。然后常量类也省去了。
自定义方法
@Component
@Named(value = "StructHelper")
public class StructHelper {
/**
* 时间搓转换程日期格式
* @param timeMillis
* @return
*/
@Named(value = StructConstant.TIME_MILLIS_TO_DATE_STR)
public static String timeMillisToDateStr(Long timeMillis) {
return DateUtil.formatDate(DateUtil.date(timeMillis));
}
/**
* 时间搓转换程日期时间格式
* @param timeMillis
* @return
*/
@Named(value = StructConstant.TIME_MILLIS_TO_DATE_TIME_STR)
public static String timeMillisToDateTimeStr(Long timeMillis) {
return DateUtil.formatDateTime(DateUtil.date(timeMillis));
}
/**
* 日期时间格式转换程时间搓
* @param dataStr
* @return
*/
@Named(value = StructConstant.DATE_STR_TO_TIME_MILLIS)
public static Long DateStrToTimeMillis(String dataStr) {
return DateUtil.parseDateTime(dataStr).getTime();
}
/**
* 日期时间格式转换程时间搓
* @param dataTimeStr
* @return
*/
@Named(value = StructConstant.DATE_TIME_STR_TO_TIME_MILLIS)
// @Named(value = "dateTimeStrToTimeMillis")
public static Long DateTimeStrToTimeMillis(String dataTimeStr) {
return DateUtil.parseDateTime(dataTimeStr).getTime();
}
}
注意点
使用
方式一:apStructTest INSTANCE = Mappers.getMapper(MapStructTest.class);
方式二:直接注入,写的接口,MapStructTest
实际两者一样,获得一个对象,然后调用方法
原理
第一步:执行下
第二步:
第三步:
@Override
public InvoiceApplyVo entityToInvoiceApplyVo(InvoiceApply invoiceApply) {
if ( invoiceApply == null ) {
return null;
}
InvoiceApplyVo invoiceApplyVo = new InvoiceApplyVo();
invoiceApplyVo.setAuditTime( structHelper.timeMillisToDateTimeStr( invoiceApply.getAuditTime() ) );
invoiceApplyVo.setId( invoiceApply.getId() );
invoiceApplyVo.setInvoiceContent( invoiceApply.getInvoiceContent() );
invoiceApplyVo.setInvoiceRiseName( invoiceApply.getInvoiceRiseName() );
invoiceApplyVo.setAdminName( invoiceApply.getAdminName() );
invoiceApplyVo.setStatus( invoiceApply.getStatus() );
return invoiceApplyVo;
}
上面对类型的转换,使用的自定义的函数调用
复杂类型
list --> list
/**
* List<InvoiceApply>--> List<InvoiceApplyVo>
*
* @param invoiceApplies
* @return
*/
List<InvoiceApplyVo> toInvoiceApplyVo(List<InvoiceApply> invoiceApplies);
看自动生成的接口实现
@Override
public List<InvoiceApplyVo> toInvoiceApplyVo(List<InvoiceApply> invoiceApplies) {
if ( invoiceApplies == null ) {
return null;
}
List<InvoiceApplyVo> list = new ArrayList<InvoiceApplyVo>( invoiceApplies.size() );
for ( InvoiceApply invoiceApply : invoiceApplies ) {
list.add( entityToInvoiceApplyVo( invoiceApply ) );
}
return list;
}
这里,会自动调用单个的,然后for循环。
总结
mapStruct 实际上是比较好用的,可能前期写的时候,可能比较繁琐。
但是在 Dto、entity,vo,转换的时候,可以省事一些。
lombok+MapStruct兼容使用
若版本不对应的话,会导致这两个玩意不能兼容
出现这样
public XXXVO toXXXVO(XXXModel model) {
if ( model == null ) {
return null;
}
XXXVO xxxVO = new XXXVO();
return xxxVO;
}
答案是:mapstruct-1.3.0.Final,对应lombok-1.18.10 就可以正常使用了!done.
常见错误
Error:(38, 13) java: Can’t map property “Integer autoTakeTime” to “Integer autoTakeTime”. Consider to declare/implement a mapping method: “Integer map(Integer value)”.
检查你 自定义转换方法
比如
@Named(value = StructConstant.TIME_MILLIS_TO_DATE_TIME_STR)
public String timeMillisToDateTimeStr(Long timeMillis) {
if (ValidateUtil.isEmpty(timeMillis)){
return "";
}
return DateUtil.formatDateTime(DateUtil.date(timeMillis));
}
实际上。自定义方法是将long 转为 String。但是 target对象使用Interger接的。所有抛出了异常。