我有一个包含多对一关系的jpa持久化对象模型:一个Account有多个transaction。一个事务有一个帐户。
下面是一段代码:
@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对象。但是,当我创建一个事务,使用现有的已经持久化的帐户,并持久化的事务,我得到一个异常:
导致:org.hibernate.PersistentObjectException:传递给persist: com.paulsanwald.Account的分离实体
org.hibernate.event.internal.DefaultPersistEventListener.onPersist (DefaultPersistEventListener.java: 141)
因此,我能够持久化一个包含事务的Account,但不能持久化一个具有Account的Transaction。我认为这是因为帐户可能没有附加,但这段代码仍然给了我相同的异常:
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);
如何正确地保存与已经持久化的帐户对象相关联的事务?
如果没有任何帮助,并且您仍然得到这个异常,请检查equals()方法—并且不要在其中包含子集合。特别是当你有嵌入式集合的深层结构时(例如A包含B, B包含c,等等)。
以Account ->为例:
public class Account {
private Long id;
private String accountName;
private Set<Transaction> transactions;
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Account))
return false;
Account other = (Account) obj;
return Objects.equals(this.id, other.id)
&& Objects.equals(this.accountName, other.accountName)
&& Objects.equals(this.transactions, other.transactions); // <--- REMOVE THIS!
}
}
在上面的例子中,从equals()检查中删除事务。这是因为hibernate将暗示您不尝试更新旧对象,而是在更改子集合上的元素时传递一个新对象来持久化。
当然,这种解决方案并不适用于所有应用程序,您应该仔细设计想要包含在equals和hashCode方法中的内容。
即使正确地声明了注释以正确地管理一对多关系,您仍然可能遇到这种异常。当向附加的数据模型添加新的子对象Transaction时,您需要管理主键值—除非您不需要这样做。如果在调用persist(T)之前为如下声明的子实体提供主键值,则会遇到此异常。
@Entity
public class Transaction {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
....
在本例中,注释声明数据库将在插入时管理实体主键值的生成。自己提供一个(比如通过Id的setter)会导致此异常。
或者,但实际上是一样的,这个注释声明会导致相同的异常:
@Entity
public class Transaction {
@Id
@org.hibernate.annotations.GenericGenerator(name="system-uuid", strategy="uuid")
@GeneratedValue(generator="system-uuid")
private Long id;
....
因此,当应用程序代码已经被管理时,不要在应用程序代码中设置id值。