簡體   English   中英

ModelMapper:將 ID 字段從 DTO 映射到實體上的 ID 字段時出錯

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

我正在嘗試從具有 int 產品 ID 的 ProductReviewDTO 映射以鏈接到在 ProductReview 實體上找到的產品。 但是使用 ModelMapper 似乎無法正確映射。 導致設置值失敗錯誤消息以及 IllegalArgument 異常,說明在嘗試設置值時對象不是聲明類的實例。

ProductReview 到 ProductReviewDTO 的第一個映射工作正常。

DTO:

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

    private int id;
    private int productId;
}

實體:

@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;
}

映射:

@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()

這是舊的,但沒有答案,問題仍然存在,所以我將提供我的解決方案。

我在使用 ModelMapper 時遇到了一個非常相似的問題。 問題本質上是 ModelMapper 不支持 PropertyMap 邏輯內部的 getter/setter 調用之外的任何復雜性(請參閱此鏈接)。 連三級算子都太多了。 這讓我感到困惑,但這是事實。

在 OP 的情況下,“MapFromTargetToSource”使用構建器的事實太復雜了。

我找到了 3 個選項來解決這個問題。 我在下面列出了它們以及每個 JUnit 示例。
(注意:我的 ModelMapper(2.4.4)版本中沒有 BaseMapping 類,所以我有一個更簡單的單向實現,但它的工作原理類似。在實踐中,我將 ModelMapper 的使用包裝在 Converter Objects 中所以這些選項都封裝在convert()接口方法后面。)

選項 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()));
}

選項 2:您可以在源類中編寫一個內部方法來派生該值。 根據用例、組織標准等,這可能有意義,也可能沒有意義。恕我直言,對於像 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();
    }

}

選項 3:您可以使用 ModelMapper 的“使用轉換器”語法。 我覺得這很笨重,但它確實有效。 這是一個參考

@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