【问题标题】:Generic mapping of Value Objects with mapstruct使用 mapstruct 的值对象的通用映射
【发布时间】:2020-03-27 21:26:42
【问题描述】:

尝试在我的业务模型中使用值对象,我遇到了以下代码的问题:

@Mapper
public abstract class TestMapstruct {

    public ValueObjectA mapA(String value){
        return new ValueObjectA(value);
    }

    public abstract BusinessObject map(DTO dto);

    @Value
    public static class ValueObjectA {
        private String a;
    }

    @Value
    public static class ValueObjectB {
        private String b;
    }

    @Data
    public static class BusinessObject {
        private ValueObjectA a;
        private ValueObjectB b;
    }

    @Data
    public static class DTO {
        private String a;
        private String b;
    }
}

缺少映射 (String -> ValueObjectB) 会导致以下编译错误消息:

无法将属性“java.lang.String b”映射到“test.ValueObjectB b”。考虑声明/实现一个映射方法:“test.ValueObjectB map(java.lang.String value)”。

我完全理解这一点,但我不想为我的每个 ValueObjects 声明一个方法(一个项目中可能有几十个)。

有没有通用的方式来声明(String -> ValueObject)映射方法?

【问题讨论】:

    标签: java mapstruct value-objects


    【解决方案1】:

    如果所有方法之间没有通用接口,则没有通用的方法来执行此操作。

    但是,如果您有一个通用接口,那么它是可能的。例如:

    public interface ValueObject<T> {
    
        T getValue();
    
        void setValue(T value);
    
    }
    

    那么你需要一个辅助映射器:

    public interface ValueObjectMapper {
    
    
        static <V extends ValueObject<T>, T> T mapToValue(V valueObject) {
            return valueObject == null ? null : valueObject.getValue();
        }
    
        static <V extends ValueObject<T>, T> V mapFromValueObject(T value, @TargetType Class<V> valueObjectClass) {
            if (value == null) {
                return null;
            }
    
    
            try {
                V valueObject = valueObjectClass.getDeclaredConstructor().newInstance();
                valueObject.setValue(value);
    
                return valueObject;
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }
    
    }
    

    编辑:添加带有不可变值对象的示例。

    如果您希望您的值对象是不可变的,那么您可以执行以下操作:

    public interface ValueObject<T> {
        T getValue();
    }
    
    
    public interface ValueObjectMapper {
    
    
        static <V extends ValueObject<T>, T> T mapToValue(V valueObject) {
            return valueObject == null ? null : valueObject.getValue();
        }
    
        static <V extends ValueObject<T>, T> V mapFromValueObject(T value, @TargetType Class<V> valueObjectClass) {
            if (value == null) {
                return null;
            }
    
    
            try {
                V valueObject = valueObjectClass.getDeclaredConstructor(value.getClass()).newInstance(value);
    
                return valueObject;
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }
    
    }
    

    注意:要完成这项工作,您必须确保所有的 ValueObject 都有一个具有该值的构造函数。

    【讨论】:

    • 我不希望在我的 ValueObjects 中声明 setter 并使用参数化构造函数,但它的工作原理就像一个魅力。注意:由于反射操作而缺少 throw 声明
    • 你也可以让它与不可变的ValueObjects一起工作。我也更新了我的答案。我还通过重新抛出 RuntimeException 添加了对异常的处理
    • 工作完美,我注意到 Mapstruct 能够处理 MappingMethod 抛出异常(catch + rethrow RuntimeException)
    • 是的,我们可以处理。它还可以很好地处理抛出的异常(如果您在界面上定义它们:)
    猜你喜欢
    • 2016-10-14
    • 1970-01-01
    • 1970-01-01
    • 2018-04-02
    • 2021-11-22
    • 1970-01-01
    • 1970-01-01
    • 2021-02-17
    • 1970-01-01
    相关资源
    最近更新 更多