I'm having trouble to remove a child entity from a one-to-many relationship. I managed to narrow it down to HashSet.contains()
returning false only after the parent (and child) entity is persisted.
The following code
Parent parent = ParentFactory.parent();
Child child = ChildFactory.child();
parent.addChild(child);
System.out.println("contains? " + parent.getChilds().contains(child));
System.out.println("child.hashCode " + child.hashCode());
System.out.println("parent.child.hashCode " + parent.getChilds().iterator().next().hashCode());
System.out.println("equals? " + parent.getChilds().iterator().next().equals(child));
parentDao.save(parent);
System.out.println("contains? " + parent.getChilds().contains(child));
System.out.println("child.hashCode " + child.hashCode());
System.out.println("parent.child.hashCode " + parent.getChilds().iterator().next().hashCode());
System.out.println("equals? " + parent.getChilds().iterator().next().equals(child));
Will print:
contains? true
child.hashCode 911563320
parent.child.hashCode 911563320
equals? true
contains? false
child.hashCode -647032511
parent.child.hashCode -647032511
equals? true
I read in similar questions, that it could be caused by overloading instead of overriding equals()
, but I think that that can't be the problem because then it'd print false the first time I check contains()
. By the way, I'm using Lombok's EqualsAndHashCode
.
My entities:
@Entity
@Table(name = "parents")
@Getter
@Setter
@EqualsAndHashCode
@ToString
public class Parent implements Serializable {
private static final long serialVersionUID = 6063061402030020L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Boolean isActive;
@OneToMany(fetch = EAGER, mappedBy = "parent", cascade = {ALL}, orphanRemoval = true)
private final Set<Child> childs = new HashSet<>();
public void addChild(Child child) {
this.childs.add(child);
child.setParent(this);
}
}
@Entity
@Table(name = "childs")
@Getter
@Setter
@EqualsAndHashCode(exclude = "parent")
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = "parent")
public class Child implements Serializable {
private static final long serialVersionUID = 5086351007045447L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@ManyToOne(fetch = EAGER)
@JoinColumn(name = "parentId_fk")
private Parent parent;
}
The only thing that changes after persist is the child
's id, but that both child
and parent.child
reference to the same instance, so the id is the same. This is proven by hashCode()
and equals()
returning true.
Why does this happen? How can I fix it?