我有一个REST端点,可用于创建或更新元素。这些元素的REST表示由Mapstruct映射到我的JPA实体。现在,为了更新现有实体,我将@MappingTarget
传递给此方法:
@Mapping(source = "id", target = "id", ignore = true)
public abstract Element resourceToElement(ElementResource resource, @MappingTarget Element element);
实体Element
与子实体具有一对多关系,我们就称它为Child
。该关系在Element
内部这样映射:
@OneToMany(cascade = ALL, mappedBy = "element", orphanRemoval = true, fetch = EAGER)
private Collection<Child> children = new ArrayList<>();
并且像在Child
中一样,Element
也是Child
主键的一部分:
@ManyToOne(fetch = LAZY, optional = false)
@JoinColumn(name = "element_id")
@JsonManagedReference
@Id
private Element element;
现在由于JPA受管实体的性质,在从资源到实体的映射期间,在插入Child
表期间出现以下错误:
NULL not allowed for column "ELEMENT_ID"
似乎是这种情况,因为Element
已经是我要传入的托管实体,因此,只要我通过添加(非托管)子代来对其进行修改,它将尝试保留该元素。现在,我想我要做的就是将父项Element
单独设置到每个子项中,以便它知道持久查询的element_id
。但是在映射过程中的什么时候我会这样做?还是有一个更聪明的选择,我根本不需要这样做?
您可以通过使用@AfterMapping
来实现。
@AfterMapping
protected void initializeChildElement(@MappingTarget Element element) {
element.getChildren().forEach(child -> child.setElement(element));
}
或另一种方式正在使用@Context
。有点复杂,但是它是在创建element
之后立即设置Child
字段,而不是遍历整个集合。
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;
}