Hibernate:将现有子实体添加到具有 OneToMany 双向关系的新实体并将其持久化(“分离的实体传递给持久化”)
Posted
技术标签:
【中文标题】Hibernate:将现有子实体添加到具有 OneToMany 双向关系的新实体并将其持久化(“分离的实体传递给持久化”)【英文标题】:Hibernate: add existing child entity to new entity with OneToMany bidirectional relationship and persist it ('detached entity passed to persist') 【发布时间】:2020-02-16 15:02:37 【问题描述】:为了理解我为什么要保留子实体,这里是映射。
我有 Author (id, name, books) 和 Book (id, title, authors) 实体。他们的关系是多对多,因为任何作者都可能有不止一本书,而任何一本书可能有不止一个作者。我也有 BookClient (id, name,rentDate, books) - 与 Book 实体的关系是 OneToMany,因为任何客户都可以租零到多本书。
作者.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
@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;
幕后的一些业务逻辑:有很多不同作者写的书都保存在一些数据库中,比如图书馆。这个图书馆为客户提供书籍。任何新客户在取书时都可以在图书馆注册。
图书客户端使用实体管理器进行持久化:
@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,则 BookClient 和 Book 之间的连接设置正确,因为这两个实体都存在于 DB 中。但在我看来,这是一种解决方法。
是否可以创建新对象,将已持久化的实体连接到它,并通过级联更新其所有子对象来持久化该对象?
【问题讨论】:
【参考方案1】:在您的示例中,作者的保存操作和 bookClient 在不同的持久性上下文中执行。所以对于main方法,首先要合并保存作者时分离的书籍(到saveOrUpdate方法中)。
是否可以创建新对象,将已持久化的实体连接到它,并通过级联更新其所有子对象来持久化该对象?
这可能取决于您的应用程序要求和功能。在这个 main() 示例中,您似乎希望以事务方式保存所有这些实体。
【讨论】:
以上是关于Hibernate:将现有子实体添加到具有 OneToMany 双向关系的新实体并将其持久化(“分离的实体传递给持久化”)的主要内容,如果未能解决你的问题,请参考以下文章
如何将项目添加到具有Android Room中父实体的外键引用的子实体?