"Entidad separada pasó a error persistente" con código JPA / EJB

81

Estoy tratando de ejecutar este código básico JPA / EJB:

public static void main(String[] args){
         UserBean user = new UserBean();
         user.setId(1);
         user.setUserName("name1");
         user.setPassword("passwd1");
         em.persist(user);
  }

Me sale este error:

javax.ejb.EJBException: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.JPA.Database

¿Algunas ideas?

Busqué en Internet y la razón por la que encontré fue:

Esto se debió a cómo creó los objetos, es decir, si estableció la propiedad ID explícitamente. La eliminación de la asignación de identificación lo solucionó.

Pero no lo entendí, ¿qué tendré que modificar para que funcione el código?

Zengr
fuente

Respuestas:

50

ERD

Digamos que tiene dos entidades Albumy Photo. El álbum contiene muchas fotos, por lo que es una relación de uno a muchos.

Clase de álbum

@Entity
public class Album {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    Integer albumId;

    String albumName;

    @OneToMany(targetEntity=Photo.class,mappedBy="album",cascade={CascadeType.ALL},orphanRemoval=true)
    Set<Photo> photos = new HashSet<Photo>();
}

Clase de fotografía

@Entity
public class Photo{
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    Integer photo_id;

    String photoName;

    @ManyToOne(targetEntity=Album.class)
    @JoinColumn(name="album_id")
    Album album;

}

Lo que debe hacer antes de persistir o fusionar es establecer la referencia del Álbum en cada foto.

        Album myAlbum = new Album();
        Photo photo1 = new Photo();
        Photo photo2 = new Photo();

        photo1.setAlbum(myAlbum);
        photo2.setAlbum(myAlbum);       

Así es como adjuntar la entidad relacionada antes de persistir o fusionar.

zawhtut
fuente
2
Si usa genéricos, no es necesario usar "targetEntity = Photo.class"
Rollerball
hola, después de configurar el objeto del álbum en foto1 y foto2 ... ¿qué objeto tenemos para guardar la lista de fotos o álbum?
Dilanka Rathnayake
130

El error se produce porque se establece el ID del objeto. Hibernate distingue entre objetos transitorios y separados y persistfunciona solo con objetos transitorios. Si persistconcluye que el objeto está separado (lo que sucederá porque el ID está establecido), devolverá el error "objeto separado pasado para persistir". Puede encontrar más detalles aquí y aquí .

Sin embargo, esto solo se aplica si ha especificado que la clave principal se genere automáticamente: si el campo está configurado para establecerse siempre manualmente, entonces su código funciona.

Tomislav Nakic-Alfirevic
fuente
¿Estaré técnicamente en lo cierto cuando diga que estoy usando JPA y no Hibernate, por lo que la declaración anterior no debería aplicarse correctamente? Soy novato en JPA (3 años para ser precisos: P)
zengr
JPA Javadoc de Sun ( java.sun.com/javaee/5/docs/api/javax/persistence/… ) y el de Toplink son exactamente iguales y sugieren que técnicamente tienes razón. Sin embargo, realmente se reduce a lo que la especificación dice que persist () debería comportarse y, lamentablemente, no sé qué es eso.
Tomislav Nakic-Alfirevic
Esta solución funcionó para mí. Estaba persiguiendo un objeto, guardando la identificación almacenada en algún lugar. Luego quiero sobrescribir ese objeto existente con un nuevo valor, así que creé el objeto, copié la ID de nuevo y explotó cuando intenté guardar. Al final, tuve que volver a consultar la base de datos (findById), realizar los cambios y luego conservar ese objeto.
cs94njw
24

eliminar

user.setId(1);

porque se genera automáticamente en la base de datos y continúa con el comando persistir.

Ammar Bozorgvar
fuente
12

Obtuve la respuesta, estaba usando:

em.persist(user);

Usé fusionar en lugar de persistir:

em.merge(user);

Pero ni idea, por qué persistir no funcionó. :(

Zengr
fuente
11
¡esta no es la solución!
Ammar Bozorgvar
1
Lo sé, pero esto funcionó para mí. ¿Alguna otra respuesta aquí que funcionó para usted? Si es así, estaré más que feliz de seleccionarlo.
zengr
4
Funcionó porque su objeto se separó de la sesión de hibernación o el objeto es transitorio, pero Hibernate lo ve como desconectado porque obtuvo la clave principal declarada.Con el uso de fusionar, adjunta el objeto a la sesión de nuevo, esto le permite actualizar objetos en su base de datos ; ver: docs.jboss.org/hibernate/core/3.3/reference/en/html/…
Ben
10

si utilizas para generar la id = GenerationType.AUTOestrategia en tu entidad.

Reemplaza user.setId (1)por user.setId (null)y el problema está resuelto.

Sergio Ordóñez
fuente
5

Sé que es demasiado tarde y probablemente todos obtuvieron la respuesta. Pero un poco más para agregar a esto: cuando se establece GenerateType, se espera que persist () en un objeto obtenga una identificación generada.

Si ya hay un valor establecido en el Id por el usuario, hibernate lo trata como un registro guardado y, por lo tanto, se trata como separado.

si la identificación es nula, en esta situación se genera una excepción de puntero nulo cuando el tipo es AUTO o IDENTIDAD, etc.a menos que la identificación se genere a partir de una tabla o una secuencia, etc.

diseño: esto sucede cuando la tabla tiene una propiedad de bean como clave primaria. GenerateType debe establecerse solo cuando una identificación se genera automáticamente. elimine esto y la inserción debería funcionar con la identificación especificada por el usuario. (es un mal diseño tener una propiedad asignada al campo de clave principal)

haripriya
fuente
5

Aquí .persist () solo insertará el registro.Si usamos .merge () verificará si existe algún registro con la ID actual, si existe, se actualizará de lo contrario insertará un nuevo registro

PSR
fuente
Me costó mucho tiempo hasta que obtuve tu respuesta. ¡Muchas gracias!
Thach Van
3

Si establece id en su base de datos para que sea clave principal y autoincremento, entonces esta línea de código es incorrecta:

user.setId(1);

Prueba con esto:

public static void main(String[] args){
         UserBean user = new UserBean();
         user.setUserName("name1");
         user.setPassword("passwd1");
         em.persist(user);
  }
Nemus
fuente
Lo intenté y sé que detached entity passed to persist
aparece
2

Tuve este problema y fue causado por el caché de segundo nivel:

  1. Persistí una entidad usando hibernación
  2. Luego eliminé la fila creada a partir de un proceso separado que no interactuó con el caché de segundo nivel
  3. Persistí en otra entidad con el mismo identificador (mis valores de identificador no se generan automáticamente)

Por lo tanto, debido a que el caché no se invalida, hibernate asumió que se trataba de una instancia separada de la misma entidad.

Hertzsprung
fuente