简体   繁体   English

Hibernate:将现有的子实体添加到具有 OneToMany 双向关系的新实体并持久化(“分离的实体传递给持久化”)

[英]Hibernate: add existing child entity to new entity with OneToMany bidirectional relationship and persist it ('detached entity passed to persist')

In order to understand why I have persisted child entities, here is the mapping.为了理解我为什么要持久化子实体,这里是映射。

I have Author (id, name, books) and Book (id, title, authors) entities.我有 Author(id、name、books)和 Book(id、title、authors)实体。 Their relationship is ManyToMany since any Author may have more than one Book, and any Book may have more than one Author.它们的关系是多对多的,因为任何作者可能有不止一本书,而任何一本书可能有不止一个作者。 Also I have BookClient (id, name, rentDate, books) - relationship with Book entity is OneToMany since any Client may rent zero to many books.我还有 BookClient(id、name、rentDate、books)——与 Book 实体的关系是 OneToMany,因为任何客户都可以租用零到多本书。

Author.java作者.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<>();

Book.java书.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图书客户端.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;

Some business logic behind the scenes: there're a lot of books written by different authors which are persisted in DB of some, let's say, library.幕后的一些业务逻辑:有很多不同作者写的书,它们被持久化在一些数据库中,比如图书馆。 And this library gives books to clients.这个图书馆为客户提供书籍。 Any new client may register in the library when he/she takes a book.任何新客户在取书时都可以在图书馆注册。

Book clients are persisted using 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);
        }
    }
}

Here is an example of how it may look like in code:这是它在代码中的外观示例:

    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
    }

After running this code I get detached entity passed to persist exception since Book instance has already been persisted earlier.运行此代码后,我将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

If I persist BookClient berforehand, then connection between BookClient and Book is set correctly since both entities are existed in DB.如果我坚持 BookClient berforehand,那么 BookClient 和 Book 之间的连接设置正确,因为这两个实体都存在于 DB 中。 But it seems to me as some workaround.但在我看来,这是一些解决方法。

Is it possible to create new object, connect already persisted entity to it and persist this object with cascade update of all its children?是否可以创建新对象,将已经持久化的实体连接到它并通过其所有子级的级联更新来持久化该对象?

In your example, save operation for the author and the bookClient executes in different persistence contexts.在您的示例中,作者和 bookClient 的保存操作在不同的持久性上下文中执行。 So as for the main method, first you should merge books (into saveOrUpdate method) that were detached during saving the author.所以对于main方法,首先应该合并保存作者时分离的书籍(进入saveOrUpdate方法)。

Is it possible to create new object, connect already persisted entity to it and persist this object with cascade update of all its children?是否可以创建新对象,将已经持久化的实体连接到它并通过其所有子级的级联更新来持久化该对象?

It may depend on your application requirements and functionality.这可能取决于您的应用程序要求和功能。 In this main() example, it looks like you want to save all these entities transactionally.在这个 main() 示例中,您似乎希望以事务方式保存所有这些实体。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM