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

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


当前回答

可能在这种情况下,您使用merge逻辑获得了帐户对象,而persist用于持久化新对象,如果层次结构有一个已经持久化的对象,它将报错。在这种情况下,应该使用saveOrUpdate,而不是持久化。

其他回答

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

我也遇到过同样的问题和CascadeType。MERGE对我很有效。

我希望你已经整理好了。

也许这是OpenJPA的bug,当回滚时它重置了@Version字段,但pcVersionInit保持true。我有一个声明@Version字段的AbstraceEntity。我可以通过重置pcVersionInit字段来解决它。但这不是一个好主意。我认为它不工作时,级联坚持实体。

    private static Field PC_VERSION_INIT = null;
    static {
        try {
            PC_VERSION_INIT = AbstractEntity.class.getDeclaredField("pcVersionInit");
            PC_VERSION_INIT.setAccessible(true);
        } catch (NoSuchFieldException | SecurityException e) {
        }
    }

    public T call(final EntityManager em) {
                if (PC_VERSION_INIT != null && isDetached(entity)) {
                    try {
                        PC_VERSION_INIT.set(entity, false);
                    } catch (IllegalArgumentException | IllegalAccessException e) {
                    }
                }
                em.persist(entity);
                return entity;
            }

            /**
             * @param entity
             * @param detached
             * @return
             */
            private boolean isDetached(final Object entity) {
                if (entity instanceof PersistenceCapable) {
                    PersistenceCapable pc = (PersistenceCapable) entity;
                    if (pc.pcIsDetached() == Boolean.TRUE) {
                        return true;
                    }
                }
                return false;
            }

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

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

所以我偶然发现了这个问题和答案,因为我得到了相同的错误,但一个非常基本的对象,只有字符串和整数。

但在我的情况下,我试图将一个值设置为一个带@Id注释的字段。

所以,如果你使用@Id,似乎你不能在一个类上创建一个新的对象,并自己设置一个Id,并将其持久化到数据库。然后,您应该将Id留空。我不知道,也许这对其他人有帮助。

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

为什么它有效

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

参考:

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