I have the following spring app
package ru.salamon.hiber.model;
import javax.persistence.*;
import javax.validation.constraints.Size;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@Entity
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "parent_ids")
@SequenceGenerator(name = "parent_ids", sequenceName = "parent_id_seq", allocationSize = 1)
private Long id;
@Size(min = 1)
@OneToMany(mappedBy = "parent", orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Child> children = new HashSet<>();
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
@Size(min = 1)
public Set<Child> getChildren() {
return children;
}
public Parent setChildren(Set<Child> children) {
this.children = children;
return this;
}
public void removeChild(Child child) {
if (child == null) return;
children.remove(child);
child.setParent(null);
}
public void addChildren(Set<Child>children) {
children.addAll(children);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Parent parent = (Parent) o;
if (!Objects.equals(id, parent.id)) return false;
return Objects.equals(children, parent.children);
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (children != null ? children.hashCode() : 0);
return result;
}
}
package ru.salamon.hiber.model;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.Objects;
@Entity
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "child_ids")
@SequenceGenerator(name = "child_ids_ids", sequenceName = "child_ids_id_seq", allocationSize = 1)
private Long id;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
public String getName() {
return name;
}
public Child setName(String name) {
this.name = name;
return this;
}
@NotNull
private String name;
public Child setId(Long id) {
this.id = id;
return this;
}
public Long getId() {
return id;
}
public Parent getParent() {
return parent;
}
public Child setParent(Parent parent) {
this.parent = parent;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Child child = (Child) o;
if (!Objects.equals(id, child.id)) return false;
if (parent != child.parent && parent.getId() != child.id) {
return parent.getId().equals(child.id);
}
return name.equals(child.name);
}
//Due to natural key is absent
@Override
public int hashCode() {
return 1001;
}
}
@GetMapping("/store")
@Transactional
public void store() {
var p = new Parent();
var c = new HashSet<Child>();
c.add(new Child().setParent(p).setName("777"));
childRepository.saveAll(c);
parentRepo.save(p);
}
@GetMapping("/update")
@Transactional
public void update() {
var p = parentRepo.findAllParentsWithChildren().get(0);
var c = p.getChildren().iterator().next();
p.removeChildren(c);
parentRepo.save(p);
}
At first I am invoking store
method to initialize data. When I’m invoking update
expected that after parentRepo.save(p)
validation exception will be raised due to I have constraint @Size(min = 1)
on Set<Child> children
I’ve tried to debug DefaultFlushEntityEventListener::dirtyCheck
, I see that loadedState
initialized with empty set. That strange due to as I read about dirty checking, it should to keep the previous state of the object