Spring-Data-Jpa Каскадирование от ребенка к родителю - PullRequest
0 голосов
/ 13 ноября 2018

Допустим, у меня есть приложение для обработки коллекции книг.

Мое приложение позволяет добавить новую книгу в библиотеку. При создании книги пользователь может выбрать автора в списке, а если автор еще не существует, он может добавить его в список, указав свое имя в поле формы. Когда форма заполнена, данные отправляются на WS, что-то вроде

{ 
  "name" : "The Book name"
  "author" : {
     "name" : "author's name"
   }
}

Затем я отображаю JSON в мою сущность, которая будет

Книга:

@Entity
@Table(name = "book")
public class Book{
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Author author;
}

Автор

@Entity
@Table(name = "author")
public class Author{
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "author", cascade = { CascadeType.ALL })
    private List<Book> books;
}

Это не будет работать, как если бы пользователь пытался добавить нового автора, когда я попытаюсь .save () я получу ошибку:

org.hibernate.TransientPropertyValueException: объект ссылается на несохраненный временный экземпляр

Есть ли способ обработать случай с помощью Spring-Data-Jpa, или мне нужно вручную проверить, что я получил идентификатор автора в json, а если нет - то есть это новый автор, - вручную запустить создание автора, а затем сохранить новую книгу?

Thx!

1 Ответ

0 голосов
/ 13 ноября 2018

Как вы уже догадались, и, как говорит Javadoc, cascade операции, которые должны быть каскадно направлены к цели ассоциации ". Однако, убедитесь, что вы понимаете, что mappedBy определяет сущность-владельца отношений.Владелец объекта - это объект, который фактически выполняет постоянные операции, если он не переопределен настройкой каскада. В этом случае Child является владельцем объекта.

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

    @OneToMany(mappedBy="parent")
    private Set<Child> children;

Настройка каскада в Parent работает, когдавы создаете Set дочерних элементов и устанавливаете его в Parent, а затем сохраняете Parent. Затем операция сохранения будет каскадно переходить от Parent к children. Это более типичное и ожидаемое использованиев случае настройки * 1013. * Однако это приводит к тому, что операции с базой данных выполняются автоматически, и это не всегда хорошо.

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

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

    @ManyToOne(cascade=CascadeType.ALL)
    private Parent parent;

Вы сохраните и родителя, и ребенка, сохранивchild.

tx.begin();
Parent p = new Parent();
Child c = new Child(); 
c.setParent(p);
em.persist(c);
tx.commit();

, и когда вы удаляете дочерний элемент, он удаляет и родителя, и дочерний элемент.

tx.begin();
Child cFound = em.find(Child.class, 1L);
em.remove(cFound);
tx.commit();
em.clear();

именно здесь у вас возникают проблемы.Что произойдет, если у вас более одного ребенка?

em.clear();
tx.begin();
p = new Parent();
Child c1 = new Child(); 
Child c2 = new Child(); 
c1.setParent(p);
c2.setParent(p);
em.persist(c1);
em.persist(c2);
tx.commit();

Все хорошо, пока вы не удалите одну из children

em.clear();
tx.begin();
cFound = em.find(Child.class, 2L);
em.remove(cFound);
tx.commit();

, тогда вы получите integrity constraint violation, когдакаскад распространяется на Parent, но в базе данных еще есть секунда Child.Конечно, вы могли бы вылечить это, удалив всех детей в одном коммите, но это немного запутанно, не так ли?

Концептуально люди склонны думать, что распространение идет от Parent до Child, и поэтомуочень нелогично иметь это иначе.Далее, как насчет ситуации, когда вы не хотите удалять автора только потому, что магазин продал все его или ее книги?В этом случае вы можете смешивать каскад, иногда от ребенка к родителю, а в других случаях от родителя к ребенку.

Обычно я думаю, что лучше быть очень точным в коде вашей базы данных.Гораздо проще читать, понимать и поддерживать код, который в первую очередь сохраняет родителя, а затем потомка или потомков, чем когда-либо делать аннотацию, о которой я могу знать, а может и не знать, что неявно выполняет дополнительные операции с базой данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...