JPA / Hibernate:传递给持久化的分离实体

问题

我有一个JPA持久化对象模型,它包含多对一关系:一个帐户有很多交易。交易有一个帐户。

这是代码的片段:

@Entity
public class Transaction {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne(cascade = {CascadeType.ALL},fetch= FetchType.EAGER)
    private Account fromAccount;
....

@Entity
public class Account {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @OneToMany(cascade = {CascadeType.ALL},fetch= FetchType.EAGER, mappedBy = "fromAccount")
    private Set<Transaction> transactions;

我能够创建一个Account对象,向其添加事务,并正确地持久保存Account对象。但是,当我创建一个事务,使用现有已经存在的帐户,并持久保存该事务时,我得到一个例外:

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.paulsanwald.Account
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:141)

因此,我能够持久保存包含交易的账户,但不能持有具有账户的交易。我认为这是因为帐户可能没有附加,但是这段代码仍然给了我同样的例外:

if (account.getId()!=null) {
    account = entityManager.merge(account);
}
Transaction transaction = new Transaction(account,"other stuff");
 // the below fails with a "detached entity" message. why?
entityManager.persist(transaction);

如何正确保存与已持久的Account对象关联的事务?


#1 热门回答(161 赞)

解决方案很简单,只需使用CascadeType.MERGE而不是CascadeType.PERSISTCascadeType.ALL

我遇到了同样的问题,并且我已经为我工作过了。

我希望你排序。


#2 热门回答(87 赞)

这是典型的双向一致性问题。它在this link以及this link.中得到了很好的讨论

根据前两个链接中的文章,你需要在双向关系的两侧修复你的setter。一方的示例设置器是this link.

多边的示例设置器是this link.

在更正setter之后,你要将Entity访问类型声明为"Property"。声明"属性"访问类型的最佳做法是将所有注释从成员属性移动到相应的getter。值得注意的是,不要在实体类中混合使用"Field"和"Property"访问类型,否则JSR-317规范不会定义行为。


#3 热门回答(9 赞)

可能在这种情况下,你使用合并逻辑获取了你的account对象,并且persist用于持久保存新对象,如果层次结构具有已持久化的对象,它将会抱怨。在这种情况下你应该使用saveOrUpdate而不是persist