#java #hibernate #spring-data-jpa
Вопрос:
я, наверное, делаю что-то не так, но не могу понять, что именно. у меня есть 2 таблицы с 2 объектами со следующим отображением
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "parent")
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@JsonIgnore
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
@Getter
private List<ChildBase> childs;
public Parent(){
this.childs = new ArrayList ();
}
@Entity
@Table(name = "childs")
@Getter
@Setter
@ToString
@Accessors(chain = true)
@NoArgsConstructor
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class ChildBase {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
protected long id;
@JoinColumn(nullable = false)
@ManyToOne
@ToString.Exclude
protected Parent parent;
public ChildBase(Parent parent){
this.parent = parent;
}
}
@Entity
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@Getter
@Setter
@Accessors(chain = true)
public class MyChild extends ChildBase {
@Enumerated(EnumType.STRING)
@Column(name = "name")
private String name;
public MyChild(String name, Parent parent) {
super(parent);
this.name = name;
}
проблема возникает, как только я пытаюсь отключить родителя от ребенка с помощью следующего теста:
@Test
public void test(){
Parent parent = new Parent();
MyChild child1 = new MyChild("a", parent);
MyChild child2 = new MyChild("a", parent);
parent.getChilds.add(child1);
parentRepository.save(parent);
parent.getChilds.remove(child1);
parent.getChilds.add(child2);
parentRepository.save(parent);
}
я ожидаю увидеть только вторую дочернюю строку в таблице «дети», но я вижу их обоих.
Редактировать:
я также пытался ссылаться на null из дочернего файла перед вторым сохранением следующим образом: child1.SetParent(null). это тоже не помогает
Комментарии:
1. прежде чем родительский объект будет сохранен снова, вы проверили, что в списке дочерних объектов есть только объект child2.
2. конечно, я это проверил..
Ответ №1:
Это двунаправленное отношение и MyChild
, соответственно ChildBase
, ваша сторона владения отношением.
Чтобы инициировать удаление сироты , вы child1.setParent(null)
тоже должны это сделать.
ОБНОВЛЕНИЕ: выполнила настройку, предоставленную из чата. вот классы, и это работает совершенно нормально.
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "parent")
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@JsonIgnore
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
@Getter
private List<ChildBase> childs;
public Parent(){
this.childs = new ArrayList<>();
}
}
@Entity
@Table(name = "childs")
@Getter
@Setter
@ToString
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Accessors(chain = true)
@NoArgsConstructor
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class ChildBase {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@EqualsAndHashCode.Include
protected long id;
@JoinColumn(nullable = false)
@ManyToOne
@ToString.Exclude
protected Parent parent;
public ChildBase(Parent parent){
this.parent = parent;
}
}
@Entity
@NoArgsConstructor
@Getter
@Setter
@Accessors(chain = true)
public class MyChild extends ChildBase {
@Column(name = "name")
private String name;
public MyChild(String name, Parent parent) {
super(parent);
this.name = name;
}
}
И вот вам испытание. После выполнения есть только childB
в базе данных.
@Test
void test() {
Parent parent = new Parent();
MyChild child1 = new MyChild("childA", parent);
MyChild child2 = new MyChild("childB", parent);
parent.getChilds().add(child1);
parentRepository.save(parent);
parent.getChilds().remove(child1); // first remove
child1.setParent(null); // set parent null
parent.getChilds().add(child2);
parentRepository.save(parent);
}
Комментарии:
1. ОК. Измените аннотацию дочерней базы с
@Entity
на@MappedSuperclass
и добавьте@EqualsAndHashcode
, но исключитеparent
поле.2. добавьте
@EqualsAndHashcode
вChildBase
?3. правильный. если вы его опустите, то будут использоваться euquals java по умолчанию. но в этом случае вы должны проверять равенство только по идентификатору. дополнительно удалить
@EqualsAndHashcode
из MyChild4. Ах! И я не знаю, какой случай вы тестируете, но обычно родитель сохраняется вместе с ребенком в первом действии (например, веб-запрос), а во втором действии вы удаляете первого ребенка, а второй добавляется. таким образом, между этими действиями родитель загружается из базы данных. вы должны добавить
parent = parentRepository.findById(parent.getId())
, а затем удалить и добавить детей.5. 2 вещи. во-первых, я не могу использовать
MappedSuperclass
аннотацию, потому что родительское сопоставлениеOneToMany
хранит коллекцию базового класса, оно просто не работает при создании компонента EntityManagerFactory. во-вторых, я попытался использоватьparentRepository.findById(parent.getId())
сразу после первого сохранения и присвоил результат отцу, как вы предложили, но происходит что-то странное.. вместо удаления первого дочернего элемента в конце теста я вижу только первого дочернего элемента в базе данных