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

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


当前回答

在您的实体定义中,您没有为联接到事务的帐户指定@JoinColumn。你会想要这样的东西:

@Entity
public class Transaction {
    @ManyToOne(cascade = {CascadeType.ALL},fetch= FetchType.EAGER)
    @JoinColumn(name = "accountId", referencedColumnName = "id")
    private Account fromAccount;
}

编辑:嗯,我想如果您在类上使用@Table注释,那么这将是有用的。哈。:)

其他回答

不要将id(pk)传递给persist方法或尝试save()方法而不是persist()。

如果上述解决方案不工作,只需一次注释实体类的getter和setter方法,并且不设置id的值。(主键) 这样就可以了。

也许这是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;
            }

这是我的药。

Below is my Entity. Mark that the id is annotated with @GeneratedValue(strategy = GenerationType.AUTO), which means that the id would be generated by the Hibernate. Don't set it when entity object is created. As that will be auto generated by the Hibernate. Mind you if the entity id field is not marked with @GeneratedValue then not assigning the id a value manually is also a crime, which will be greeted with IdentifierGenerationException: ids for this class must be manually assigned before calling save()

@Entity
@Data
@NamedQuery(name = "SimpleObject.findAll", query="Select s FROM SimpleObject s")
public class SimpleObject {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column
    private String key;

    @Column
    private String value;

}

这是我的主类。

public class SimpleObjectMain {

    public static void main(String[] args) {

        System.out.println("Hello Hello From SimpleObjectMain");

        SimpleObject simpleObject = new SimpleObject();
        simpleObject.setId(420L); // Not right, when id is a generated value then no need to set this.
        simpleObject.setKey("Friend");
        simpleObject.setValue("Bani");

        EntityManager entityManager = EntityManagerUtil.getEntityManager();
        entityManager.getTransaction().begin();
        entityManager.persist(simpleObject);
        entityManager.getTransaction().commit();

        List<SimpleObject> simpleObjectList = entityManager.createNamedQuery("SimpleObject.findAll").getResultList();
        for(SimpleObject simple : simpleObjectList){
            System.out.println(simple);
        }

        entityManager.close();
        
    }
}

我想救它的时候,它把它扔出去了

PersistentObjectException: detached entity passed to persist.

我所需要修复的是删除主方法中simpleObject的id设置行。

这是一个典型的双向一致性问题。在这个链接和这个链接中都有很好的讨论。

根据前两个链接中的文章,您需要在双向关系的两侧修复您的setter。一方的示例setter在此链接中。

在此链接中有一个用于多方的示例setter。

在你纠正你的setter之后,你想要声明实体访问类型为“属性”。声明“Property”访问类型的最佳实践是将所有注释从成员属性移动到相应的getter。一个重要的警告是不要在实体类中混合使用“Field”和“Property”访问类型,否则JSR-317规范没有定义行为。