[英]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:像这样在Child
, Element
也是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.