I am trying to use @MapsId
with @EmbeddedId
on the child entity in a bidirectional @OneToMany
association. But when I try to remove children from the parent entity’s set, and add overlapping children, I get the NonUniqueObjectException[A different object with the same identifier value was already associated with the session :]. Also note, that the exception is thrown at the save step, not at flush.
I would think this happens because the entities cleared from the collection are still in the persistence context. But what’s weird is I don’t get this error when I manually assign the id in the embeddable, even though it is the same value that MapsId would assign.
Below is my mapping and usage. Help appreciated!
Parent
public class Parent {
@Id private UUID parentId;
...SNIP...
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Child> children = new HashSet<>();
public void setChildrenList(Set<Child> newChildren) {
this.getChildren().forEach(child -> child.setParent(null));
this.getChildren().clear();
this.getChildren().addAll(newChildren);
newChildren.forEach(child -> child.setParent(this));
}
}
ChildId
@Embeddable
public class ChildId implements Serializable {
private UUID parentId;
@Column(name = "property2_id")
private String property2;
@Column(name = "property3_id")
private String property3;
@Override
public boolean equals(Object obj) {
if (obj instanceof ChildId childId) {
return Objects.equals(childId.parentId, this.parentId)
&& Objects.equals(childId.property2, this.property2)
&& Objects.equals(childId.property3, this.property3);
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hash(
this.getParentId(), this.getProperty2(), this.getProperty3());
}
}
Child
public class Child {
@EmbeddedId private ChildId childId;
@MapsId(parentId")
@ManyToOne
@JoinColumn(name = "parent_uuid")
private Parent parent;
private String property4;
public Child(
String value2, String value3, String value4
) {
this.childId =
ChildId.builder().property2(value2).property3(value3).build();
this.property4 = value4;
}
}
Usage
parentDto.setChildrenList(Set.of(new Child("value2","value3","value4")));
parentRepository.save(parentDto); // NonUniqueObjectException here