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

时间:2018-08-26 03:36:37

标签: java hibernate jpa java-ee

我正在练习JavaEE技术。现在,我与Hibernate一起专注于JPA。该项目具有以下实体:

图书:

@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 Service类:

BookService:

@Stateless
public class BookService extends BaseEntityService<Long, Book> {

    public void createBook(Book book) {
        super.getEntityManager().persist(book);
    }

    //update, delete, find and findAll methods
}

BookDetailsS​​ervice:

@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
  • Payara 5
  • MySQL 8.0
  • 休眠5.3.4
  • OmniPersistence库。 (用于JPA,JDBC和DataSources的实用程序

您可以查看项目存储库here

3 个答案:

答案 0 :(得分:1)

我已经可以解决问题了,这是由于实体持久的方式以及JavaEE应用程序中方法的事务性所致。

当调用持久化了类的EJB的业务方法时,此方法完成后,事务结束,因此持久性上下文被关闭,附加的类(托管)被分离(非托管)。参见this

因此,在坚持Book b之后:

bookService.createBook (b);

那笔交易结束了,这就是为什么保留这本书的原因,此外,它也不再受到管理。因此,当我将书链接到详细信息时:

d.setBook (b);

要保留一个共享ID的实体,必须执行此操作,必须管理父实体(在这种情况下为b)。


我可以找到两种解决方案:

Solución1:

将书b附加到详细信息创建事务的持久性上下文。参见this

b = getEntityManager.merge(b);

Solución2:(我选择的那个)

BookService的业务方法中调用BookDetailsService业务方法,这意味着转移依赖项注入。因此,通过持久保留两个实体来进行单个交易。

//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的服务。

答案 1 :(得分:0)

您必须在书籍详细信息上添加层叠类型:

@OneToOne(mappedBy = "book", cascade = CascadeType.ALL)
private BookDetails details;

有关更多详细信息,请参见here 保存图书实体时,CascadeType.ALL将执行所有操作,例如保存,删除和合并。  现在,当我们在Book实体中添加CascadeType.ALL时,您必须在book中设置bookdetails对象。见下文:

Book b = new Book();
BookDetails d = new BookDetails();
b.setBookDetails(d);
b.setId(123L);
b.setName("Bible");

d.setId(111L); //random id that is not existing if d is new record
d.setNumberOfPages(999);
bookService.createBook(b);

保存书籍实体将保存书籍以及书籍详细信息实体。您还可以尝试hibernate.show_sql=true来查看保存图书实体时执行的所有查询。

答案 2 :(得分:0)

Book类的BookDetails属性上的

@OneToOne指示Book与BookDetails之间存在一对一的关联。此外,请注意使用 cascade = CascadeType.ALL 。由于没有Book实体的BookDetails实体不存在,因此使用此级联设置,BookDetails实体将在随后对Book实体的更新/删除时被更新/删除。 / p>