17

I'm creating a simple app to just insert a row to a table (if table does not exist, create it) using Java JPA.

I'm attaching some code for a runnable example of it.

Here's the exception I'm getting and the 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

And here is my code:

Main class:

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

And the Person class:

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

And here's my persistence.xml file

<?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>

----------------------- EDIT ---------------------------

I just changed the provider to EclipseLink and with no further changes it is working. I'm confused now. Why does it works with EclipseLink but with Hibernate it generates an exception?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
dazito
  • 7,740
  • 15
  • 75
  • 117

4 Answers4

20

The reason for this is that you have declared the id in Person class as generated with auto strategy meaning JPA tries to insert the id itself while persisting the entity. However in your constructor you are manually setting the id variable . Since the ID is manually assigned, and that the entity is not present in the persistence context this causes JPA to think that you are trying to persist an entity which is detached from persistence context and hence the exception.

To fix it dont set the id in the constructor.

@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;
  }
Manjunath
  • 1,685
  • 9
  • 9
18

Try with the below code then, it will allow you to set the ID manually.

Just use the @Id annotation which lets you define which property is the identifier of your entity. You don't need to use the @GeneratedValue annotation if you do not want hibernate to generate this property for you.

assigned - lets the application to assign an identifier to the object before save() is called. This is the default strategy if no <generator> element is specified.

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() {
    }
}
Reg
  • 10,717
  • 6
  • 37
  • 54
Ankur Singhal
  • 26,012
  • 16
  • 82
  • 116
2

In the class definition you have annotated id to be generated by strategy (table, sequence, etc.) chosen by the persistence provider, but you are initializing the id of the Person object through constructor. I think leaving id null may solve your problem.

Ammaro
  • 370
  • 2
  • 14
  • Yes it does solve my problem. But it generated another problem, what if I really need to set an id on the object constructor? – dazito Sep 03 '14 at 04:28
  • why do you need to set an id manually? The id must be assigned by the persistence provider. – Ammaro Sep 03 '14 at 04:30
  • Because I'll be showing the data in a `JTable` and then insert it to the database. And I want to keep the data in the `JTable` synced with the data in the databse table. I don't want to go to the database to retrieve all the data (that I already have) just so I can have the ids in the `JTable` updated and not out of sync (id@JTable != id@DBTable) – dazito Sep 03 '14 at 04:35
  • 1
    look at this answer : (stackoverflow.com/questions/23607087/hibernate-error-detached-entity-passed-to-persist?rq=1) – Ammaro Sep 03 '14 at 04:39
  • I think you are not allowed to post links when you have a low "reputation" so my guess is to put it here without the http*://www part :) – dazito Sep 03 '14 at 04:40
0

You can use this contrustor like this:

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

then when JPA persist to database it'll automatically insert with AUTO_INCREMENT strategy.

AbcAeffchen
  • 14,400
  • 15
  • 47
  • 66
Maxx
  • 421
  • 5
  • 9