问题
我有一个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.PERSIST
或CascadeType.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
。