web-dev-qa-db-fra.com

org.hibernate.PersistentObjectException: entité détachée transmise à une exception persistante

Je crée une application simple pour simplement insérer une ligne dans une table (si la table n'existe pas, créez-la) à l'aide de Java JPA.

Je joins un code pour un exemple exécutable. 

Voici l'exception que je reçois et le stacktrace:

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

Et voici mon code:

Classe principale:

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();
            }
        }
    }
}

Et la classe de personne:

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() {
    }
}

Et voici mon fichier 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>

----------------------- MODIFIER ------------------------ ---

Je viens de changer le fournisseur en EclipseLink et sans autre changement, il fonctionne. Je suis confus maintenant. Pourquoi cela fonctionne-t-il avec EclipseLink mais avec Hibernate, il génère une exception?

12
dazito

Essayez avec le code ci-dessous, cela vous permettra de définir manuellement la ID.

Utilisez simplement l'annotation @Id qui vous permet de définir quelle propriété est l'identifiant de votre entité. Vous n'avez pas besoin d'utiliser l'annotation @GeneratedValue si vous ne voulez pas qu'hibernate génère cette propriété pour vous. 

assigné - permet à l'application d'attribuer un identifiant à l'objet avant que save() soit appelé. C'est la stratégie par défaut si aucun élément <generator> n'est spécifié.

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() {
    }
}
14
Ankur Singhal

La raison en est que vous avez déclaré l'identifiant dans la classe Person comme étant généré avec la stratégie automatique, ce qui signifie que JPA tente d'insérer l'identifiant lui-même tout en conservant l'entité. Cependant, dans votre constructor, vous définissez manuellement la variable id. Etant donné que l'ID est attribué manuellement et que l'entité n'est pas présente dans le persistence context, JPA pense alors que vous essayez de conserver une entité détachée du contexte de persistance et donc de l'exception.

Pour résoudre ce problème, ne définissez pas l'identifiant dans le constructeur.

@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;
  }
16
Manjunath Anand

Dans la définition de classe, l'id annoté que vous souhaitez générer est généré par la stratégie (table, séquence, etc.) choisie par le fournisseur de persistance, mais vous initialisez l'ID de l'objet Person via le constructeur. Je pense que laisser id null pourrait résoudre votre problème.

2
Ammaro

Vous pouvez utiliser ce contrustor comme ceci:

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

puis, lorsque JPA persiste dans la base de données, il est automatiquement inséré avec la stratégie AUTO_INCREMENT.

0
Minh Nguyen