Добавить новый дочерний объект в управляемый объект с помощью MapStruct - PullRequest
0 голосов
/ 12 февраля 2020

У меня есть конечная точка REST, которую можно использовать для создания или обновления элементов. REST-представление этих элементов отображается в моих объектах JPA Mapstruct. Теперь, чтобы обновить существующие сущности, я передаю @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 для постоянного запроса. Но в какой момент во время картирования я бы сделал это? Или есть более разумная альтернатива, где мне вообще не пришлось бы это делать?

1 Ответ

0 голосов
/ 12 марта 2020

Этого можно добиться, используя @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;
}
...