[英]Hibernate: add existing child entity to new entity with OneToMany bidirectional relationship and persist it ('detached entity passed to persist')
为了理解我为什么要持久化子实体,这里是映射。
我有 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<>();
书.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;
图书客户端.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 berforehand,那么 BookClient 和 Book 之间的连接设置正确,因为这两个实体都存在于 DB 中。 但在我看来,这是一些解决方法。
是否可以创建新对象,将已经持久化的实体连接到它并通过其所有子级的级联更新来持久化该对象?
在您的示例中,作者和 bookClient 的保存操作在不同的持久性上下文中执行。 所以对于main方法,首先应该合并保存作者时分离的书籍(进入saveOrUpdate方法)。
是否可以创建新对象,将已经持久化的实体连接到它并通过其所有子级的级联更新来持久化该对象?
这可能取决于您的应用程序要求和功能。 在这个 main() 示例中,您似乎希望以事务方式保存所有这些实体。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.