我有一个包含多对一关系的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);

如何正确地保存与已经持久化的帐户对象相关联的事务?


当前回答

这是一个老问题,但最近又遇到了同样的问题。在这里分享我的经验。

实体

@Data
@Entity
@Table(name = "COURSE")
public class Course  {

    @Id
    @GeneratedValue
    private Long id;
}

保存实体(JUnit)

Course course = new Course(10L, "testcourse", "DummyCourse");
testEntityManager.persist(course);

Fix

Course course = new Course(null, "testcourse", "DummyCourse");
testEntityManager.persist(course);

结论:如果实体类的主键(id)有@GeneratedValue,那么确保您没有传递主键(id)的值

其他回答

我基于Spring Data jpa的回答是:我只是在外部方法中添加了一个@Transactional注释。

为什么它有效

由于没有活动的Hibernate Session上下文,子实体立即被分离。提供一个Spring (Data JPA)事务可以确保存在Hibernate会话。

参考:

https://vladmihalcea.com/a-beginners-guide-to-jpa-hibernate-entity-state-transitions/

您需要为每个帐户设置事务。

foreach(Account account : accounts){
    account.setTransaction(transactionObj);
}

或者在许多方面将id设置为null就足够了(如果合适的话)。

// list of existing accounts
List<Account> accounts = new ArrayList<>(transactionObj.getAccounts());

foreach(Account account : accounts){
    account.setId(null);
}

transactionObj.setAccounts(accounts);

// just persist transactionObj using EntityManager merge() method.

在我的情况下,我正在提交事务时,持久化方法被使用。 在更改persist to save方法时,该问题得到了解决。

通过在下一个对象之前保存依赖对象来解决。

这发生在我身上,因为我没有设置Id(这不是自动生成的)。并试图拯救@ManytoOne的关系

即使正确地声明了注释以正确地管理一对多关系,您仍然可能遇到这种异常。当向附加的数据模型添加新的子对象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值。