需要对MapStruct有一定的了解。
通过一个简单的例子说明如何自定义映射规则。
后端有一张存储文件的数据表,数据表中存放的是文件在对象存储中的路径,在返回给前端的时候,我们希望能返回完整的URL,包含对象存储的endpoint和bucket。
例如source/image/001.png
,返回https://minio.example.com/bucket_static/source/image/001.png
。这样前端就可以直接通过此地址渲染页面。
下面说下怎么处理。
首先创建一个基本的映射
包含Entity、VO、Mapper,为了减少冗余代码,使用了Lombok,不喜欢的可自行生成Getter、Setter方法。
@Data
public class AttachmentEntity {
private Long id; // 主键
private String fileName; // 文件名
private Long fileSize; // 文件大小
private Date fileTime; // 文件时间
private String keyPath; // 对象存储地址
}
public class AttachmentVO {
private String fileName; // 文件名
private Long fileSize; // 文件大小
private Date fileTime; // 文件时间
private String keyPath; // 对象存储地址
}
@Mapper
public interface AttachmentConvert {
AttachmentConvert INSTANCE = Mappers.getMapper(AttachmentConvert.class);
AttachmentEntity convert(AttachmentVO vo);
AttachmentVO convert(AttachmentEntity entity);
List<AttachmentVO> convertList(List<AttachmentEntity> list);
}
通过方法名称选择映射规则
在AttachmentConvert中添加一个静态方法,方法入参为源字段值,返回值为目标字段值。通过@Named注解给这个方法加上一个标识。在需要执行转换的方法上,通过@Mapping注解指定要使用的映射方法。
@Mapper
public interface AttachmentConvert {
// snip
@Name("appendPrefix")
default static String appendPrefix(String keyPath) {
return String.join("/", MinioConstants.DOMAIN, MinioConstants.BUCKET, keyPath);
}
@Mapping(source = "keyPath", target = "keyPath", qualifiedByName = "appendPrefix")
AttachmentVO convert(AttachmentEntity entity);
}
interface MinioConstants {
String BUCKET = "bucket_static";
String DOMAIN = "https://minio.example.com";
}
通过注解方式选择映射规则
如果不止一个文件需要用到这种映射规则,那么每个Convert接口都写一个这样的映射方法,有点啰嗦,我们可以将此方法提取到外部,通过@Mapper注解的uses参数引入来调用。
像上面那样用@Named形式指定也是可以的,但是我们更加彻底一点,使用注解会更加优雅。
import org.mapstruct.Qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface MinioPrefixMapper {
}
public interface MinioPrefixMappers {
@MinioPrefixMapper
default static String appendPrefix(String keyPath) {
return String.join("/", MinioConstants.DOMAIN, MinioConstants.BUCKET, keyPath);
}
}
interface MinioConstants {
String BUCKET = "bucket_static";
String DOMAIN = "https://minio.example.com";
}
通过注解指定使用的映射方法。
@Mapper(uses = {MinioPrefixMappers.class})
public interface AttachmentConvert {
// snip
@Mapping(source = "keyPath", target = "keyPath", qualifiedBy = MinioPrefixMapper.class)
AttachmentVO convert(AttachmentEntity entity);
}
其他有趣的转换方法
自定义常量
@Mapper
public interface ExampleConvert {
// snip
@Mapping(target="enabled", constant="true")
ExampleVO convert(ExampleEntity entity);
}
表达式转换
@Mapper
public interface ExampleConvert {
// snip
@Mapping(target="createTime", dateFormat="yyyy-MM-dd HH:mm:ss", expression="java(new Date())")
ExampleVO convert(ExampleEntity entity);
}
自定义方法
对一些非常复杂的,可以编写default方法,自己实现转换过程。
@Mapper
public interface ExampleConvert {
// snip
default ExampleVO convert(ExampleEntity entity) {
if (entity == null) {
return null;
}
ExampleVO vo = new ExampleVO();
vo.setXX(entity.getXX);
// 复杂的转换逻辑
return vo;
}
}