org.hibernate.PersistentObjectException: detached entity passed to persist异常 (注:该异常通常出现在使用Hibernate框架进行对象持久化操作时)

17

我正在使用Java JPA创建一个简单的应用程序,仅向表中插入一行数据(如果表不存在则创建它)。

我附上了一些代码作为可运行示例。

这是我得到的异常和堆栈跟踪:

EXCEPTION -- > org.hibernate.PersistentObjectException: detached entity passed to persist: view.Person
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: view.Person
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1187)
    at view.TestJPA.main(TestJPA.java:34)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: view.Person
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181)
    ... 1 more

以下是我的代码:

主类:

package view;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class TestJPA {

    public static void main(String[] args) {

        Person p = new Person(1, "Peter", "Parker");

        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("TesePersistentUnit");
        EntityManager entityManager = entityManagerFactory.createEntityManager();

        EntityTransaction transaction = entityManager.getTransaction();
        try {
            transaction.begin();

            entityManager.persist(p);
            entityManager.getTransaction().commit();
        } 
        catch (Exception e) {
            if (transaction != null) {
                transaction.rollback();
            }
            System.out.println("EXCEPTION -- > " + e.getMessage());
            e.printStackTrace();
        } 
        finally {
            if (entityManager != null) {
                entityManager.close();
            }
        }
    }
}

而且Person类:

package view;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "People")
public class Person {

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

    private String name;
    private String lastName;

    public Person(int id, String name, String lastName) {
        this.id = id;
        this.name = name;
        this.lastName = lastName;
    }

    public Person() {
    }
}

这是我的persistence.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="TesePersistentUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>view.Person</class>
        <properties>
            <!-- SQL dialect -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>

            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/tese_tabelas?zeroDateTimeBehavior=convertToNull"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.password" value=""/>

            <!-- Create/update tables automatically using mapping metadata -->
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>

----------------------- 编辑 ---------------------------

我刚刚将提供程序更改为 EclipseLink,而没有进行任何其他更改,它就可以工作了。我现在感到困惑。为什么使用 EclipseLink 可以工作,但使用 Hibernate 会生成异常?

4个回答

20
这是因为您在Person类中声明了使用自动生成策略生成的id,这意味着在持久化实体时JPA会尝试插入id本身。但是在您的constructor中手动设置了id变量。由于ID被手动分配,并且实体不存在于persistence context中,这会导致JPA认为您正在尝试将一个已从persistence context中脱离的实体持久化,从而引发异常。
要解决此问题,请勿在构造函数中设置id。
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;



public Person(int id, String name, String lastName) {
       // this.id = id;
        this.name = name;
        this.lastName = lastName;
  }

如果我真的需要在构造函数中设置id呢? - dazito
在这种情况下,您不应将其声明为具有AUTO策略的@GeneratedValue。 - Manjunath
@ManjunathAnand,你的回答也很棒。+1 :) - Ankur Singhal
2
当您事先知道ID时,可以使用EntityManager.merge操作而不是EntityManager.persist操作... - Carlitos Way
好的解释。 - Abdul

18

尝试使用以下代码,它将允许您手动设置ID

只需使用@Id注释即可定义实体的标识符属性。如果您不想Hibernate为您生成此属性,则无需使用@GeneratedValue注释。

assigned - 让应用程序在调用save()之前分配一个标识符给对象。如果未指定<generator>元素,则这是默认策略。

package view;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "People")
public class Person {
    @Id
    //@GeneratedValue(strategy = GenerationType.AUTO) // commented for manually set the id
    private int id;

    private String name;
    private String lastName;

    public Person(int id, String name, String lastName) {
        this.id = id;
        this.name = name;
        this.lastName = lastName;
    }

    public Person() {
    }
}

2
在类定义中,您已注释 id 由持久性提供程序选择的策略(table、sequence 等)生成,但您正在通过构造函数初始化 Person 对象的 id。我认为将 id 保留为空可能会解决您的问题。

是的,它解决了我的问题。但它又产生了另一个问题,如果我真的需要在对象构造函数上设置一个ID呢? - dazito
为什么需要手动设置ID?ID必须由持久化提供者分配。 - Ammaro
因为我将在JTable中显示数据,然后将其插入到数据库中。我希望保持JTable中的数据与数据库表中的数据同步。我不想去检索已经拥有的所有数据(我已经拥有)以便我可以更新JTable中的ids并且不失去同步(id@JTable != id@DBTable)。 - dazito
1
看看这个答案:(stackoverflow.com/questions/23607087/hibernate-error-detached-entity-passed-to-persist?rq=1) - Ammaro
我认为当您的“声望”较低时,您不被允许发布链接,因此我的猜测是在此处放置它,而不包括http*://www部分 :) - dazito

0

您可以像这样使用此构造函数:

Person p = new Person(0, "Peter", "Parker");

然后当JPA持久化到数据库时,它将自动使用AUTO_INCREMENT策略插入。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接