我正在练习JavaEE技术。现在我专注于 JPA 和 Hibernate。该项目有以下实体:
书:
@Entity @Table(name = "book")
public class Book extends BaseEntity<Long> {
@Id
private Long id;
@NotNull
private String name;
@OneToOne(mappedBy = "book", cascade = CascadeType.ALL)
private BookDetails details;
//getters/setters
}
书籍详细信息:
@Entity
@Table(name = "book_details")
public class BookDetails extends BaseEntity<Long> {
@Id
private Long id;
@MapsId
@OneToOne
private Book book;
@NotNull
@Column(name = "number_of_pages")
private int numberOfPages;
//getters/setters
}
现在,各个 EJB 服务类:
图书服务:
@Stateless
public class BookService extends BaseEntityService<Long, Book> {
public void createBook(Book book) {
super.getEntityManager().persist(book);
}
//update, delete, find and findAll methods
}
图书详细信息服务:
@Stateless
public class BookDetailsService extends BaseEntityService<Long, BookDetails> {
public void createBookDetails(BookDetails details) {
super.getEntityManager().persist(details);
//super.persist(details); //This method would not work to persisting entity with shared Id, because it verifies if ID is null
}
//update, delete and find methods
}
<小时/>
问题:
当我尝试保留一本新书及其详细信息时,如下所示:
Book b = new Book();
b.setId(123L);
b.setName("Bible");
bookService.createBook(b);
//The entity Book is correctly persisted in the DB.
BookDetails d = new BookDetails();
d.setNumberOfPages(999);
d.setBook(b);
//b.setDetails(d); //don't do this
bookDetailsService.createBookDetails(d);
//BookDetails is not persisted in the DB, throws exception....
抛出以下异常:
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '123' for key 'PRIMARY'
Book
实体会被持久化,但 BookDetails
不会被持久化。
我遵循了本教程:
- Hibernate Tips: How to Share the Primary Key in a One-to-One Association
- The best way to map a @OneToOne relationship with JPA and Hibernate
其他信息:
- JDK 1.8
- 帕亚拉5
- MySQL 8.0
- hibernate 5.3.4
- OmniPersistence图书馆。 (JPA、JDBC 和数据源实用程序)
可以查看项目仓库here .
最佳答案
我已经可以解决这个问题,这是由于 JavaEE 应用程序中实体的持久化方式和方法的事务性造成的。
当调用持久化类的 EJB 的业务方法时,一旦该方法完成,事务就会结束,因此持久化上下文将关闭,并且附加的类(托管)将被分离(非托管)。请参阅this .
所以在坚持 Book
b
之后:
bookService.createBook (b);
该事务结束,这就是该书被持久化的原因,此外,它不再受到管理。因此,当我将这本书链接到详细信息时:
d.setBook (b);
为了持久保存共享 Id 的实体,必须执行此操作,必须管理父实体(在本例中为 b
)。
我可以找到两种解决方案:
解决方案1:
将书 b 附加到详细信息创建事务的持久化上下文中。请参阅this .
b = getEntityManager.merge(b);
解决方案 2:(我选择的)
在BookDetailsService
的业务方法中调用BookService
业务方法,这意味着传输依赖注入(inject)。因此,通过持久化两个实体来完成单个事务。
//BookDetailsService class
@Inject
BookService bookService;
public void createBookDetails(BookDetails details) {
Book b = new Book();
b.setId(details.getId());
b.setName("Bible");
bookService.createBook(b);
details.setBook(b);//b still managed
super.persist(details);
}
我认为这个解决方案更加清晰和连贯,因为 BookDetailsService
将始终需要 BookService
中的服务。
关于java - 无法保留具有共享 ID 的实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52022985/