SpringBoot JPA Many2One, One2Many не работают должным образом в двух направлениях - PullRequest
0 голосов
/ 14 декабря 2018

Я создал программу с использованием JPA и SpringBoot, база данных - Postgresql, у меня есть две сущности: Parent и Child:

@Entity
@Table(name = "parent")
public class Parent {

  @Id
  @Column(name = "id")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;
  @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
  private Set<Child> children = new HashSet<>();
}

И дочерняя сущность:

@Entity
@Table(name = "child")
public class Child {
  @Id
  @Column(name = "id")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private long id;

  @ManyToOne
  @JoinColumn(name = "parent")
  private Parent parent;
}

Затем в приложении я автоматически подключил два репозитория для выполнения некоторых тестов: Это работает, когда я делаю:

Child child1 = new Child("Lucas", new Date(2012, 12,12));
Parent parent1 = new Parent("Jack", "Bauer");
child1.setParent(parent1);
childRepository.save(child1);

В таблице Child установлен родительский идентификаторправильно.

Но если я создаю с другой стороны, это не сработает:

Child child1 = new Child("Lucas", new Date(2012, 12,12));
Parent parent1 = new Parent("Jack", "Bauer");
childRepository.save(child1);
parent1.getChildren().add(child1);
parentRepository.save(parent1);

Не появляется ошибка и нет связиобновляется в таблице Child

Можете ли вы сказать мне, почему?

Спасибо.

Ответы [ 2 ]

0 голосов
/ 15 декабря 2018

У вас есть вопрос, почему операция Cascade не работает, когда вы добавляете Child к Parent и имеете каскадную аннотацию на Parent.

Обычно владелец отношения, в данном случае Child, как указано в аннотации mappedBy="parent", отвечает за сохранение отношения.Вы продемонстрировали это с помощью однонаправленного сопоставления для Child - с аннотацией ManyToOne.

Child child = new Child();
Parent parent = new Parent();
child.setParent(parent);
parentRepo.save(parent);
childRepo.save(child);

Затем вы попробовали то же самое с двунаправленным сопоставлением в Parent -сделано с аннотацией OneToMany.Поскольку эта аннотация включает аннотацию mappedBy="parent", она не является владельцем, и обычно все, что добавляется к Set<Child> children, игнорируется.Однако вы добавили аннотацию cascade = CascadeType.ALL, чтобы она переопределяла настройки владения и позволяла сущности Parent сохранять отношения для подмножества операций и конкретных условий, определяемых значением CascadeType.

Но какродитель, чтобы знать, какие дети сохранятся?Я предполагаю, что он проверяет, сохранился ли дочерний экземпляр.Если это так, то каскадная операция не потребуется.Когда вы сохранили дочерний экземпляр самостоятельно, вы обошли каскадную операцию.

Child child = new Child();
Parent parent = new Parent();
Set<Child> children = new HashSet<>();
childRepo.save(child);
children.add(child);
parent.setChildren(children);
parentRepo.save(parent);

Этот конкретный код выдает мне ошибку, поскольку дочерний экземпляр был сохранен и отключен, а затем попросил сохранить его снова.Условие ошибки не всегда возникает - я думаю, в зависимости от того, является ли родительский объект новым или был получен из базы данных.

org.hibernate.PersistentObjectException: отдельная сущность передана для сохранения:

Поэтому, если вы хотите, чтобы объект Parent делал каскад, вы должны передать ему экземпляр Child, который еще не был сохранен.Обратите внимание, что вам все равно нужно установить родителя дочернего элемента, чтобы создать отношение, иначе родительский объект останется бездетным дочерним элементом.

Child child = new Child();
Parent parent = new Parent();
child.setParent(parent);
Set<Child> children = new HashSet<>();
children.add(child);
parent.setChildren(children);
parentRepo.saveAndFlush(parent);

И это прекрасно работает для меня.Обратите внимание, что я сам создаю Set детей вместо того, чтобы создавать его каждый раз, когда создается экземпляр Parent.Как правило, вы будете выполнять запросы к базе данных гораздо чаще, чем обновления, и для каждого запроса провайдер JPA будет помещать свой собственный класс Collection в свойство children Parent, и поэтому набор, который вы создали, обычно заканчиваетсяна куче мусора - несколько неэффективно.

@Entity
public class Child {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    private Parent parent;

@Entity
public class Parent  {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval=true)
    private Set<Child> children;
0 голосов
/ 14 декабря 2018
  1. Двунаправленный @OneToMany:

Лучший способ отобразить ассоциацию @OneToMany - это использовать сторону @ManyToOne для распространения всех изменений состояния сущности.:

Родительский класс:

@OneToMany(
        mappedBy = "post", 
        cascade = CascadeType.ALL, 
        orphanRemoval = true
    )
    private List<Child> childs = new ArrayList<>();

//Constructors, getters and setters removed for brevity

    public void addChild(Child child) {
        childs.add(child);
        comment.setChild(this);
    }

    public void removeChild(Child child) {
        childs.remove(child);
        child.setPost(null);
    }

Дочерний класс:

@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id")
    private Parent parent;

Ассоциация @ManyToOne использует FetchType.LAZY, потому что в противном случае мы воспользуемся извлечением EAGER, что негативно сказывается на производительности.

Родительский объект имеет два служебных метода (например, addChild и removeChild).которые используются для синхронизации обеих сторон двунаправленной ассоциации.Вы должны всегда предоставлять эти методы, когда работаете с двунаправленной ассоциацией, так как в противном случае вы рискуете получить очень тонкие проблемы распространения состояния.

Для теста:

Parent parent1=new Parent();
// set datas into parent1 and to put childs we can use the utility method addChild
parent1.addChild(new Child(datas...))
parent1.addChild(new Child(datas...)) //etc
parentRepository.save(parent1);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...