简体   繁体   中英

Add new child entity to managed entity with MapStruct

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.

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