Hibernate: добавьте существующую дочернюю сущность к новой сущности с двунаправленной связью OneToMany и сохраните ее («отдельная сущность передана для сохранения») - PullRequest
0 голосов
/ 16 февраля 2020

Чтобы понять, почему я сохранил дочерние сущности, вот сопоставление.

У меня есть сущности Author (идентификатор, имя, книги) и Book (идентификатор, название, авторы). Их отношения - ManyToMany, поскольку у любого автора может быть несколько книг, а у любой книги может быть несколько авторов. Также у меня есть BookClient (id, name, rentDate, books) - отношения с сущностью Book - OneToMany, поскольку любой клиент может арендовать ноль для многих книг.

Author. java

@Table
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "books_authors",
            joinColumns = { @JoinColumn(name = "author_id") },
            inverseJoinColumns = { @JoinColumn(name = "book_id") }
    )
    private Set<Book> books = new HashSet<>();

Книга. java

@Entity
@Table
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(nullable = false, unique = true)
    private Long id;

    @Column(nullable = false)
    private String title;

    @ManyToMany(mappedBy = "books", fetch = FetchType.EAGER)
    private Set<Author> authors = new HashSet<>();

    @ManyToOne
    @JoinColumn(name = "client_id")
    private BookClient bookClient;

BookClient. java

@Entity
@Table
public class BookClient {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "client_id")
    private Long id;

    @Column(nullable = false)
    private String name;

    @OneToMany(mappedBy = "bookClient", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<Book> books = new HashSet<>();

    private LocalDate rentDate;

Некоторые бизнес-логики c за кадром: есть много книг, написанных разными авторами, которые сохраняются в БД какой-то, скажем, библиотеки. И эта библиотека дает книги клиентам. Любой новый клиент может зарегистрироваться в библиотеке, когда он / она берет книгу.

Клиенты книги сохраняются с помощью Entity Manager:

@Transactional
@Repository("bookClientDao")
public class BookClientDaoImpl implements BookClientDao {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public void save(BookClient bookClient) {
        entityManager.persist(bookClient);
    }


    @Override
    public void saveOrUpdate(BookClient bookClient) {
        if(bookClient.getId() == null) {
            save(bookClient);
        } else {
            entityManager.merge(bookClient);
        }
    }
}

Вот пример того, как это может выглядеть в код:

    public static void main(String[] args) {
        ApplicationContext appContext = new ClassPathXmlApplicationContext("META-INF/context.xml");
        AuthorDao authorDao = (AuthorDao) appContext.getBean("authorDao");
        BookClientDao bookClientDao = (BookClientDao) appContext.getBean("bookClientDao");

        //Create a book and its author
        Book book = new Book();
        book.setTitle("John Doe Book the 1st");

        Author author = new Author();
        author.setName("John Doe");
        author.getBooks().add(book);
        authorDao.save(author);

        //Registering new Book Client
        BookClient bookClient = new BookClient();
        bookClient.setName("First Client");
        bookClient.getBooks().add(book);
        bookClient.setRentDate(LocalDate.now());

        book.setBookClient(bookClient);

        //book is expected to be updated by cascade
        bookClientDao.saveOrUpdate(bookClient); //'detached entity passed to persist' occurs here
    }

После выполнения этого кода я получаю исключение detached entity passed to persist, поскольку экземпляр Book уже был сохранен ранее.

Exception in thread "main" javax.persistence.PersistenceException:
org.hibernate.PersistentObjectException: detached entity passed to persist: entity.manager.example.entity.Book
...
Caused by: org.hibernate.PersistentObjectException:
detached entity passed to persist: entity.manager.example.entity.Book

Если я сохраню BookClient berforehand, то соединение между BookClient и Книга установлена ​​правильно, так как обе сущности существуют в БД. Но мне кажется, что это обходной путь.

Можно ли создать новый объект, подключить к нему уже сохраненную сущность и сохранить этот объект с помощью каскадного обновления всех его дочерних элементов?

1 Ответ

0 голосов
/ 16 февраля 2020

В вашем примере операция сохранения для автора и bookClient выполняется в разных контекстах сохранения. Что касается основного метода, сначала вы должны объединить книги (в метод saveOrUpdate ), которые были отключены во время сохранения автора.

Возможно ли создать новый объект, подключиться уже сохранить объект и сохранить этот объект с помощью каскадного обновления всех его дочерних элементов?

Это может зависеть от требований и функциональности вашего приложения. В этом примере main () похоже, что вы хотите сохранить все эти объекты транзакционно.

...