简体   繁体   English

ModelMapper:将 ID 字段从 DTO 映射到实体上的 ID 字段时出错

[英]ModelMapper: Error mapping ID field from DTO to the ID field on the Entity

I am trying to Map from a ProductReviewDTO which has an int product ID to link to the Product found on the ProductReview entity.我正在尝试从具有 int 产品 ID 的 ProductReviewDTO 映射以链接到在 ProductReview 实体上找到的产品。 However using ModelMapper it can't seem to map correctly.但是使用 ModelMapper 似乎无法正确映射。 Resulting in a failed to set value error message as well as an IllegalArgument Exception saying that the object is not an instance of declaring class when trying to set the value.导致设置值失败错误消息以及 IllegalArgument 异常,说明在尝试设置值时对象不是声明类的实例。

The first mapping of ProductReview to ProductReviewDTO works fine. ProductReview 到 ProductReviewDTO 的第一个映射工作正常。

DTO: DTO:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProductReviewDTO {

    private int id;
    private int productId;
}

Entity:实体:

@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "product_reviews")
public class ProductReview {

    @Id
    @SequenceGenerator(name="product_reviews_generator", sequenceName = "product_reviews_id_seq", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_reviews_generator")
    @Column(name = "id", updatable = false, nullable = false)
    private int id;

    @ManyToOne(cascade=CascadeType.DETACH, fetch = FetchType.LAZY)
    @JoinColumn(name = "product_id", nullable=false)
    private Product product;
}

Mapping:映射:

@NoArgsConstructor
public class ProductReviewMapping implements BaseMapping<ProductReview, ProductReviewDTO> {

    @Override
    public PropertyMap<ProductReview, ProductReviewDTO> MapFromSourceToTarget() {
        return new PropertyMap<ProductReview, ProductReviewDTO>() {
            protected void configure() {
                map().setProductId(source.getProduct().getId());
                map().setUserId(source.getUser().getId());
                map().setRatingId(source.getRating().getId());
            }
        };
    }

    @Override
    public PropertyMap<ProductReviewDTO, ProductReview> MapFromTargetToSource() {
        return new PropertyMap<ProductReviewDTO, ProductReview>() {
            protected void configure() {
                map().setProduct(new Product().builder().id(source.getProductId()).build());
            }
        };
    }
}

Caused by: org.modelmapper.MappingException: ModelMapper mapping errors:

1) Failed to set value '1' on com.pfex.ecom.library.common.entity.Product$ProductBuilder.id()

This is old, but there are no answers and the issue still occurs so I will offer my solution.这是旧的,但没有答案,问题仍然存在,所以我将提供我的解决方案。

I encountered a very similar issue using ModelMapper.我在使用 ModelMapper 时遇到了一个非常相似的问题。 The problem in essence is that ModelMapper does not support any complexity beyond getter/setter invocation inside of the PropertyMap logic (see this link ).问题本质上是 ModelMapper 不支持 PropertyMap 逻辑内部的 getter/setter 调用之外的任何复杂性(请参阅此链接)。 Even the tertiary operator is too much.连三级算子都太多了。 This is bewildering to me, but it is true.这让我感到困惑,但这是事实。

In the OP's case, the fact that 'MapFromTargetToSource' is using a builder is too complex.在 OP 的情况下,“MapFromTargetToSource”使用构建器的事实太复杂了。

I found 3 options to work around this.我找到了 3 个选项来解决这个问题。 I have listed them below with JUnit examples of each.我在下面列出了它们以及每个 JUnit 示例。
(Note: I don't have the BaseMapping class available in my version of ModelMapper (2.4.4), so I have a simpler one-way implementation but it works similarly. In practice, I wrap my use of ModelMapper inside of Converter Objects so these options are all encapsulated behind the convert() interface method.) (注意:我的 ModelMapper(2.4.4)版本中没有 BaseMapping 类,所以我有一个更简单的单向实现,但它的工作原理类似。在实践中,我将 ModelMapper 的使用包装在 Converter Objects 中所以这些选项都封装在convert()接口方法后面。)

Option 1: You can do all the derivation beforehand and set a simple value inside the configure() method.选项 1:您可以预先进行所有推导,并在 configure() 方法中设置一个简单的值。

@Test
public void modelMapper_complexMapping_option_1_externalDerivation_so() {
    ProductReviewDTO source = ProductReviewDTO.builder().id(123).productId(456).build();

    ModelMapper mapper = new ModelMapper();
    TypeMap<ProductReviewDTO, ProductReview> typeMap = mapper.typeMap(ProductReviewDTO.class, ProductReview.class);

    Product derivedField = Product.builder().id(source.getProductId()).build();

    PropertyMap<ProductReviewDTO, ProductReview> targetToSource = new PropertyMap<>() {
        protected void configure() {
            map().setProduct(derivedField);
        }
    };

    typeMap.addMappings(targetToSource);
    ProductReview actual = mapper.map(source, ProductReview.class);

    assertThat(actual.getId(), is(source.getId()));
    assertThat(actual.getProduct().getId(), is(source.getProductId()));
}

Option 2: You can write an internal method in your source class to derive the value.选项 2:您可以在源类中编写一个内部方法来派生该值。 This may or may not make sense depending on use case, organization standards, etc. IMHO it does not make sense for a DTO like the OP is using, but may in other cases.根据用例、组织标准等,这可能有意义,也可能没有意义。恕我直言,对于像 OP 正在使用的 DTO 来说,这没有意义,但在其他情况下可能。

@Test
public void modelMapper_complexMapping_option_2_internalDerivation_so() {
    ProductReviewDTO source = ProductReviewDTO.builder().id(123).productId(456).build();

    ModelMapper mapper = new ModelMapper();
    TypeMap<ProductReviewDTO, ProductReview> typeMap = mapper.typeMap(ProductReviewDTO.class, ProductReview.class);

    PropertyMap<ProductReviewDTO, ProductReview> targetToSource = new PropertyMap<>() {
        protected void configure() {
            map().setProduct(source.synthesizeProduct());
        }
    };

    typeMap.addMappings(targetToSource);
    ProductReview actual = mapper.map(source, ProductReview.class);

    assertThat(actual.getId(), is(source.getId()));
    assertThat(actual.getProduct().getId(), is(source.getProductId()));
}

public class ProductReviewDTO {

    private int id;
    private int productId;

    public Product synthesizeProduct() {
        return Product.builder().id(getProductId()).build();
    }

}

Option 3: You can use ModelMapper's "using converter" syntax.选项 3:您可以使用 ModelMapper 的“使用转换器”语法。 I find this clunky, but it works.我觉得这很笨重,但它确实有效。 Here is a reference .这是一个参考

@Test
public void modelMapper_complexMapping_option_3_usingConverter_so() {
    ProductReviewDTO source = ProductReviewDTO.builder().id(123).productId(456).build();

    ModelMapper mapper = new ModelMapper();
    TypeMap<ProductReviewDTO, ProductReview> typeMap = mapper.typeMap(ProductReviewDTO.class, ProductReview.class);

    PropertyMap<ProductReviewDTO, ProductReview> targetToSource = new PropertyMap<>() {
        protected void configure() {
            using(new Converter<ProductReviewDTO, Product>() {
                public Product convert(MappingContext<ProductReviewDTO, Product> context) {
                    ProductReviewDTO src = context.getSource();
                    return Product.builder().id(src.getProductId()).build();
                }
            }).map(source).setProduct(null);
        }
    };

    typeMap.addMappings(targetToSource);
    ProductReview actual = mapper.map(source, ProductReview.class);

    assertThat(actual.getId(), is(source.getId()));
    assertThat(actual.getProduct().getId(), is(source.getProductId()));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM