简体   繁体   English

使用 MapStruct 向托管实体添加新的子实体

[英]Add new child entity to managed entity with MapStruct

I've got a REST endpoint that can be used to create or update elements.我有一个可用于创建或更新元素的 REST 端点。 The REST representation of these elements is mapped to my JPA Entities by Mapstruct.这些元素的 REST 表示通过 Mapstruct 映射到我的 JPA 实体。 Now in order to update existing entities I'm passing a @MappingTarget into this method:现在为了更新现有实体,我将@MappingTarget传递给此方法:

@Mapping(source = "id", target = "id", ignore = true)
public abstract Element resourceToElement(ElementResource resource, @MappingTarget Element element);

The entity Element has a one-to-many relation to a child entity, let's just call it Child .实体Element与子实体是一对多的关系,我们称之为Child The relation is mapped like this inside Element :关系在Element是这样映射的:

@OneToMany(cascade = ALL, mappedBy = "element", orphanRemoval = true, fetch = EAGER)
private Collection<Child> children = new ArrayList<>();

And like this inside Child , Element is also part of a Child 's primary key:像这样在ChildElement也是Child主键的一部分:

@ManyToOne(fetch = LAZY, optional = false)
@JoinColumn(name = "element_id")
@JsonManagedReference
@Id
private Element element;

Now due to the nature of JPA managed entities, during mapping from resource to entity, I get the following error during insert into the Child table:现在,由于 JPA 托管实体的性质,在从资源到实体的映射期间,在插入到Child表期间出现以下错误:

NULL not allowed for column "ELEMENT_ID"

This seems to be the case because Element is already a managed entity I'm passing in, so anytime I modify it by adding a (unmanaged) child, it'll try to persist the element.情况似乎是这样,因为Element已经是我传入的托管实体,因此无论何时我通过添加(非托管)子项来修改它,它都会尝试保留该元素。 Now, I think what I'll have to do is set the parent Element into each child seperately so that it knows the element_id for the persisit query.现在,我认为我必须做的是将父Element分别设置到每个子Element以便它知道 persisit 查询的element_id But at what point during mapping would I do this?但是在映射过程中的什么时候我会这样做? Or is there a smarter alternative where I wouldn't have to do this at all?或者有没有更聪明的选择,我根本不需要这样做?

You can achieve this by using @AfterMapping .您可以通过使用@AfterMapping来实现这@AfterMapping

@AfterMapping
protected void initializeChildElement(@MappingTarget Element element) {
    element.getChildren().forEach(child -> child.setElement(element));
}

Or another way is using @Context .或者另一种方法是使用@Context It is little bit more complicated, but it is setting element field right after creating Child and not iterating through whole collection.它有点复杂,但它在创建Child后立即设置element字段,而不是遍历整个集合。

public Element resourceToElement(ElementResource resource, @MappingTarget Element element) {
    return resourceToElement(resource, element, element);
}

@Mapping(qualifiedByName = "ChildWithElement", target = "children")
protected abstract Element resourceToElement(ElementResource resource, @MappingTarget Element element, @Context Element context);

protected abstract Child childResourceToChild(ChildResource childResource);

@Named("ChildWithElement")
protected Child childResourceToChild(ChildResource childResource, @Context Element element) {
    Child child = childResourceToChild(childResource);
    child.setElement(element);
    return child;
}

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

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