![](/img/trans.png)
[英]ModelMapper & JPA: How to map DTO with id to Entity with oneToMany field
[英]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.