需要对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;
    }
}
最后修改:2023 年 12 月 01 日
如果觉得我的文章对你有用,请随意赞赏