java - 无法保留具有共享 ID 的实体

标签 java hibernate jpa jakarta-ee

我正在练习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 不会被持久化。

<小时/>

我遵循了本教程:

其他信息:

  • 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:(我选择的)

BookDetailsS​​ervice的业务方法中调用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);
}

我认为这个解决方案更加清晰和连贯,因为 BookDetailsS​​ervice 将始终需要 BookService 中的服务。

关于java - 无法保留具有共享 ID 的实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52022985/

相关文章:

java - 如何告诉 Maven 和 TestNG 运行特定的测试类或 suite.xml 文件?

java - 不用Entity类通过Hibernate获取数据

Hibernate 在运行时更改获取模式

java - @NamedQuery INNER JOIN 不起作用

java - 标准 Java 中是否有用于此特定任务的集合?

java - 与客户端 PC 上的 MS Office 通信

java - 谁在篡改我的数据流?

java - 如何使用 Hibernate 有效地获取表中的所有行?

java - Hibernate 中的未知实体

java - 为 PostgreSQL 使用种子文件时 JPA 中的异常