mapstruct会自动完成基本类型的一些隐式转换(包括:boolean
、byte
、char
、int
、long
、float
、double
、String
及其包装类等)
相同基本类型可以直接转换
@Mapping(target = "intValue", source = "intValue")
基本类型都可以转String类型,如int/Integer转String
实际上使用的是string.valueOf()
方法,支持所有基本类型转String。
@Mapping(target = "stringValue", source = "intValue")
@Mapping(target = "stringValue", source = "booleanValue")
所有的数字类型及其包装类都可以直接转换,如int/Integer、long/Long之间转换。
实际上会进行强制转换,长字节类型转短字节类型会发生截断,注意数据溢出,精度丢失等问题。
@Mapping(target = "byteValue", source = "intValue")
// 生成代码示例,257强制转byte后溢出,实际为1
target.setByteValue((byte) intValue);
基本类型与其包装类型可以直接转换,且会自动生成判空代码
@Mapping(target = "intValue", source = "integerValue")
// 生成代码示例,自动生成判空代码
if (source.getIntegerValue() != null) {
target.setIntValue( source.getIntegerValue() );
}
StringBuilder
与String
可以直接转换
大数类型与基本类型及其包装类、String可以直接转换
隐式转换规则
java.time.Instant
与java.util.Date
直接转换java.sql.Date
与java.util.Date
直接转换java.sql.Time
与java.util.Date
直接转换java.sql.Timestamp
与java.util.Date
直接转换java.util.Date
转String
,使用系统默认时区
@Mapper
public interface TimeMapper {
TimeMapper INSTANCE = Mappers.getMapper(TimeMapper.class);
@Mapping(target = "date", source = "date", dateFormat = "yyyy-MM-dd HH:MM:ss")
TimeVo toTimeVo(TimeEntity timeEntity);
}
public class TimeEntity {
private Date date;
}
public class TimeVo {
private String date;
}
java.util.Date
可以与String
转换,使用系统默认时区
@Mapper
public interface TimeMapper {
TimeMapper INSTANCE = Mappers.getMapper(TimeMapper.class);
@Mapping(target = "date", source = "instant", dateFormat = "yyyy-MM-dd HH:MM:ss")
TimeVo toTimeVo(TimeEntity timeEntity);
}
public class TimeEntity {
private Instant instant;
}
public class TimeVo {
private String date;
}
相同类型的集合可以直接转换
@Mapping(target = "list", source = "list")
基本类型(及其包装类)的相同集合可以直接转换
如List
转List
@Mapping(target = "integerList", source = "stringList")
生成代码示例,自动生成一个stringListToIntegerList方法。
target.setList( stringListToIntegerList( source.getList() ) );
protected List<Integer> stringListToIntegerList(List<String> list) {
if ( list == null ) {
return null;
}
List<Integer> list1 = new ArrayList<Integer>( list.size() );
for ( String string : list ) {
list1.add( Integer.parseInt( string ) );
}
return list1;
}
同类型的List
和Set
可以直接转换
@Mapping(target = "set", source = "list")
复杂List、Set的转换
如需实现List
转List
,可以先表示A
转B
的方法toB()
,再表示List
转List
的方法toBList()
,不需要显式调用toB()
方法,mapstruct
会在toBList()
方法中自动循环调用toB()
方法。
@Getter
@Setter
@ToString
public class A {
private String aValue;
}
@Getter
@Setter
@ToString
public class B {
private String bValue;
}
@Mapper
public interface Converter {
Converter INSTANCE = Mappers.getMapper(Converter.class);
@Mapping(target = "bValue", source = "aValue")
B toB(A a);
// 不需要写任何注解,会自动调用toB(A a)方法
List<B> toBList(List<A> aList);
}
生成代码示例:
public List<B> toBList(List<A> aList) {
if ( aList == null ) {
return null;
}
List<B> list = new ArrayList<B>( aList.size() );
for ( A a : aList ) {
list.add( toBaseEntity( a ) );
}
return list;
}
复杂Map的转换
如需要实现Map
转换为Map
,类似与List的转换,需要先分别实现A
转换为B
的toB()
、C
转为D
的toD()
方法,然后再实现Map
转换为Map
的toMap()
方法
@Getter
@Setter
@ToString
public class A {
private String aValue;
}
@Getter
@Setter
@ToString
public class B {
private String bValue;
}
@Getter
@Setter
@ToString
public class C {
private String cValue;
}
@Getter
@Setter
@ToString
public class D {
private String dValue;
}
@Mapper
public interface Converter {
Converter INSTANCE = Mappers.getMapper(Converter.class);
@Mapping(target = "bValue", source = "aValue")
B toB(A a);
@Mapping(target = "dValue", source = "cValue")
D toD(C c);
// 不需要写任何注解,会自动调用toB(A a)方法和D toD(C c)方法
Map<B, D> toMap(Map<A, C> map);
}
生成代码示例:
public Map<B, D> toMap(Map<A, C> map) {
if ( map == null ) {
return null;
}
Map<B, D> map = new HashMap<A, C>( Math.max( (int) ( baseVoMap.size() / .75f ) + 1, 16 ) );
for ( java.util.Map.Entry<B, D> entry : map.entrySet() ) {
B key = toB( entry.getKey() );
D value = toD( entry.getValue() );
map.put( key, value );
}
return map;
}
两个枚举值之间可以使用@ValueMapping
进行转换,举例:对DML语句的进行分类,定义两个枚举,两个对象分别拥有这两个枚举属性
public enum DMLEnum {
SELECT("SELECT * FROM test"),
INSERT("INSERT INTO test VALUES(...)"),
INSERT_OR_UPDATE("INSERT INTO test VALUES(...) ON DUPLICATE KEY UPDATE..."),
UPDATE("UPDATE test SET ... WHERE ..."),
DELETE("DELETE FROM test WHERE ...");
private final String sql;
DMLEnum(String sql) {
this.sql = sql;
}
}
public enum OperationEnum {
CREATE,
UPDATE,
READ,
DELETE
}
public class BaseVo {
private OperationEnum operationEnum;
}
public class BaseEntity {
private DMLEnum dmlEnum;
}
映射如下:
Mapper:
@Mapper
public interface DMLMapper {
DMLMapper INSTANCE = Mappers.getMapper(DMLMapper.class);
@ValueMapping(target = "CREATE", source = "INSERT")
@ValueMapping(target = "CREATE", source = "INSERT_OR_UPDATE")
@ValueMapping(target = "READ", source = "SELECT")
@ValueMapping(target = "UPDATE", source = "UPDATE")
@ValueMapping(target = "DELETE", source = "DELETE")
OperationEnum toOperationEnum(DMLEnum dmlEnum);
@Mapping(target = "operationEnum", source = "dmlEnum")
BaseVo toBaseVo(BaseEntity baseEntity);
}
测试:
public class DMLMapperTest {
@Test
public void test_toOperationEnum() {
System.out.println(DMLMapper.INSTANCE.toOperationEnum(DMLEnum.INSERT_OR_UPDATE));
System.out.println(DMLMapper.INSTANCE.toOperationEnum(DMLEnum.INSERT));
}
@Test
public void test_toBaseVo() {
BaseEntity baseEntity = new BaseEntity();
baseEntity.setDmlEnum(DMLEnum.UPDATE);
BaseV baseVo = DMLMapper.INSTANCE.toBaseVo(baseEntity);
System.out.println(baseVo.getOperationEnum());
}
}
/***************************************output******************************
* CREATE
* CREATE
****************************************************************************
* UPDATE
****************************************************************************
枚举的字面值(name()
方法返回值)和字符串可以互相转换
枚举值->字符串
直接将枚举值映射为字符串值,以上面枚举DMLEnum
为例,映射为字符串String
@Mapper
public interface DMLMapper {
DMLMapper INSTANCE = Mappers.getMapper(DMLMapper.class);
@Mapping(target = "dml", source = "dmlEnum")
BaseVo toBaseVo(BaseEntity baseEntity);
}
public class BaseVo {
private String dml;
}
public class BaseEntity {
private DMLEnum dmlEnum;
}
测试
public class DMLMapperTest {
@Test
public void test_toBaseVo() {
BaseEntity baseEntity = new BaseEntity();
baseEntity.setDmlEnum(DMLEnum.UPDATE);
BaseVo baseVo = DMLMapper.INSTANCE.toBaseVo(baseEntity);
System.out.println(baseVo.getDml());
}
}
/***************************************output******************************
* UPDATE
****************************************************************************
字符串->枚举
@Mapper
public interface DMLMapper {
DMLMapper INSTANCE = Mappers.getMapper(DMLMapper.class);
@Mapping(target = "dmlEnum", source = "dml")
BaseEntity toBaseEntity(BaseVo baseVo);
}
public class BaseVo {
private String dml;
}
public class BaseEntity {
private DMLEnum dmlEnum;
}
测试
public class DMLMapperTest {
@Test
public void test_toBaseEntity() {
BaseVo baseVo = new BaseVo();
baseVo.setDml("UPDATE");
BaseEntity baseEntity = DMLMapper.INSTANCE.toBaseEntity(baseVo);
System.out.println(baseEntity.getDmlEnum());
}
}
/***************************************output******************************
* UPDATE
****************************************************************************
字符->找到对应枚举->得到枚举的字符串属性
dml: String
-> dmlEnum: DMLEnum
->sql: String
@Mapper
public interface DMLMapper {
DMLMapper INSTANCE = Mappers.getMapper(DMLMapper.class);
@Mapping(target = "sql", expression = "java(org.numb.mapstruct.entity.DMLEnum.fromName(baseVo.getDml()).getSql())")
BaseEntity toBaseEntity(BaseVo baseVo);
}
public class BaseVo {
private String dml;
}
public class BaseEntity {
private String sql;
}
测试
public class DMLMapperTest {
@Test
public void test_toBaseEntity() {
BaseVo baseVo = new BaseVo();
baseVo.setDml("UPDATE");
BaseEntity baseEntity = DMLMapper.INSTANCE.toBaseEntity(baseVo);
System.out.println(baseEntity.getSql());
}
}
/***************************************output******************************
* UPDATE test SET ... WHERE ...
****************************************************************************
mapstruct会自动识别并引用当前Mapper中的映射方法,如A对象包含B对象,C对象包含D对象。A对象映射为C对象,同时希望把B对象映射为D对象
可以在Mapper中同时定义A->C、B->D的映射对象,当A->C转换时,会自动调用C->D的映射。
如下,Car
中包含Person
对象,调用carToCarDto
时会自动调用personToPersonDto
@Mapper
public interface CarMapper {
CarDto carToCarDto(Car car);
PersonDto personToPersonDto(Person person);
}
引用对象映射遵循一些规则:
如果source和target类型相同(包括集合类),会调用set、constructor方法直接赋值。
如果source和target类型不同,会在当前Mapper中寻找有相同类型入参和相同返回值类型的映射方法,如果存在则自动引用该方法。
如果不存在上述映射方法,寻找是否有内置转换代码(built-in conversion)可以完成转换
如果上述方法都不存在,mapstruct尝试自动生成转换方法
如果不能完成转换,则报错。
如果Mapstruct有些场景不能完成,可以自定义转化方法
public interface CarMapper{
@Mapping(target = "personDto", source = "person")
CarDto toCarDto(Car car);
// 自定义方法实现
static PersonDto toPersonDto(Person person) {
PersonDto personDto = new PersonDto();
// do something
return personDto;
}
}
也可以使用@Mapping
的表达式属性expression
显式指定某一个参数。注意expression不会自动导入,所以表达式中要使用其他类的方法,要带全类名(包名.类名.函数名)或者使用@Mapper
的imports
显式导入所需要的类。
@Mapper(imports = {com.xxx.xxx.class})
public interface CarMapper{
@Mapping(target = "personDto", expression = "toPersonDto(person)")
CarDto toCarDto(Car car);
// 自定义方法实现
static PersonDto toPersonDto(Person person) {
PersonDto personDto = new PersonDto();
// do something
return personDto;
}
}