@OneToMany with @MapsId for composite PK: NonUniqueObjectException

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