I've got a REST endpoint that can be used to create or update elements. The REST representation of these elements is mapped to my JPA Entities by Mapstruct. Now in order to update existing entities I'm passing a @MappingTarget
into this method:
@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
. The relation is mapped like this inside 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:
@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:
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. 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. 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
protected void initializeChildElement(@MappingTarget Element element) {
element.getChildren().forEach(child -> child.setElement(element));
}
Or another way is using @Context
. It is little bit more complicated, but it is setting element
field right after creating Child
and not iterating through whole collection.
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;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.