Cómo solucionar el error de Hibernate "el objeto hace referencia a una instancia transitoria no guardada - guarde la instancia transitoria antes del vaciado"

610

Recibo el siguiente error cuando guardo el objeto usando Hibernate

object references an unsaved transient instance - save the transient instance before flushing
Tushar Ahirrao
fuente
1
¿En contexto quiere este error? ¿Es con o sin variable transitoria?
vijay

Respuestas:

803

Debe incluir cascade="all"(si usa xml) o cascade=CascadeType.ALL(si usa anotaciones) en su mapeo de colección.

Esto sucede porque tiene una colección en su entidad, y esa colección tiene uno o más elementos que no están presentes en la base de datos. Al especificar las opciones anteriores, le dice a hibernate que las guarde en la base de datos cuando guarde su padre.

Bozho
fuente
77
¿No es esto implícito? ¿No siempre querrías que Hibernate los salvara?
Marcus Leon
29
@ Marcus: no, no lo es. Es posible que desee manejarlos manualmente.
Bozho
55
Bozho tiene razón. Encontré circunstancias en las que tuve colecciones que quería administrar manualmente debido a su tamaño, o debido a reglas comerciales que no permiten que todos los objetos de una colección se guarden al mismo tiempo.
Alex Marshall
26
No sucede solo para colecciones, sino también para asignaciones simples uno a uno
Sebastien Lorber
12
¿No sería mejor comenzar con CascadeType.PERSIST y usar persist para guardar?
Sergii Shevchyk
249

Creo que esto podría ser solo una respuesta repetida, pero solo para aclarar, obtuve esto en un @OneToOnemapeo y a @OneToMany. En ambos casos, fue el hecho de que el Childobjeto que estaba agregando Parentno estaba guardado en la base de datos todavía. Así que cuando he añadido el Childde la Parent, entonces guardado el Parent, Hibernate lanzaba el "object references an unsaved transient instance - save the transient instance before flushing"mensaje al guardar el Padre.

Añadiendo el cascade = {CascadeType.ALL}en la Parent'sreferencia al Childresuelto el problema en ambos casos. Esto salvó el Childy el Parent.

Perdón por las respuestas repetidas, solo quería aclarar más para la gente.

@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
    return performanceLog;
}
McDonald Noland
fuente
77
¿Qué sucede si no quiero poner en cascada los ahorros en una relación @OneToOne? Al crear ambos objetos por primera vez, ¿cómo puedo guardarlos en la base de datos sin activar la excepción?
xtian
44
¿Qué sucede si quiero salvar al niño para algunos casos y no para otros?
Omaruchan
@xtian: Bueno, entonces tienes que cuidar el orden correcto de guardar en la base de datos persistiendo los objetos con un EntityManager. En básico solo dices em.persist (object1); em.persist (objeto2); etc.
kaba713
Recibí este problema específicamente cuando utilicé @Inheritance, en este caso TABLE_PER_CLASS, estaba haciendo referencia a una subclase. CascadeType.ALL lo arregló.
Jim ReesPotter
O ha creado su objeto de entidad con new MyEntity(sin sincronizarlo con la base de datos, vaciado), en lugar de obtener su instancia sincronizada de la base de datos. Hacer consultas de Hibernate usando esa instancia le informa que lo que espera que esté en la base de datos difiere de lo que tiene en la memoria de su aplicación. En este caso, simplemente sincronice / obtenga la instancia de su entidad de DB y úsela. No se necesita CascadeType.ALL entonces.
Zon
67

Esto sucede al guardar un objeto cuando Hibernate cree que necesita guardar un objeto asociado con el que está guardando.

Tuve este problema y no quería guardar los cambios en el objeto referenciado, así que quería que el tipo de cascada sea NINGUNO.

El truco es asegurarse de que la ID y la VERSIÓN en el objeto referenciado estén configuradas para que Hibernate no piense que el objeto referenciado es un objeto nuevo que necesita guardarse. Esto funcionó para mí.

Mire todas las relaciones en la clase que está guardando para resolver los objetos asociados (y los objetos asociados de los objetos asociados) y asegúrese de que la ID y la VERSIÓN estén establecidas en todos los objetos del árbol de objetos.

Cookalino
fuente
44
Este comentario me puso en el camino correcto. Estaba asignando una nueva instancia del padre a una propiedad de su hijo. Entonces NH pensó que eran instancias diferentes.
elvin
2
Si. Esto sucede si, por ejemplo, la identificación del objeto asociado no está incluida (por ejemplo, ha sido ignorada por @JsonIgnore). Hibernate no tiene forma de identificar la entidad asociada, por lo que quiere guardarla.
Rori Stumpf
36

Introducción

Como expliqué en este artículo cuando uso JPA e Hibernate, una entidad puede estar en uno de los siguientes 4 estados:

  • Nuevo : un objeto recién creado que nunca se ha asociado con una sesión de hibernación (también conocido como contexto de persistencia) y que no está asignado a ninguna fila de la tabla de la base de datos se considera en estado nuevo o transitorio.

    Para ser persistente, necesitamos llamar explícitamente el persistmétodo o hacer uso del mecanismo de persistencia transitiva.

  • Persistente : una entidad persistente se ha asociado con una fila de la tabla de la base de datos y está siendo administrada por el Contexto de persistencia actualmente en ejecución.

    Cualquier cambio realizado en dicha entidad se detectará y propagará a la base de datos (durante el tiempo de descarga de la sesión).

  • Independiente : una vez que el contexto de persistencia actualmente en ejecución se cierra, todas las entidades administradas previamente se desconectan. Los cambios sucesivos ya no serán rastreados y no se realizará una sincronización automática de la base de datos.

  • Eliminado : aunque JPA exige que solo se permita eliminar entidades administradas, Hibernate también puede eliminar entidades separadas (pero solo a través de una removellamada al método).

Transiciones de estado de entidad

Para mover una entidad de un estado a otro, puede utilizar los persist, removeo mergemétodos.

Estados de la entidad JPA

Solucionando el problema

El problema que está describiendo en su pregunta:

object references an unsaved transient instance - save the transient instance before flushing

es causado por asociar una entidad en el estado de Nuevo a una entidad que está en el estado de Administrado .

Esto puede suceder cuando está asociando una entidad secundaria a una colección de uno a varios en la entidad principal, y la colección no realiza cascadelas transiciones de estado de la entidad.

Entonces, como expliqué en este artículo , puede solucionar esto agregando cascada a la asociación de entidades que desencadenó esta falla, de la siguiente manera:

La @OneToOneasociación

@OneToOne(
    mappedBy = "post",
    orphanRemoval = true,
    cascade = CascadeType.ALL)
private PostDetails details;

Observe el CascadeType.ALLvalor que agregamos para el cascadeatributo.

La @OneToManyasociación

@OneToMany(
    mappedBy = "post", 
    orphanRemoval = true,
    cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();

Nuevamente, el CascadeType.ALLes adecuado para las asociaciones bidireccionales@OneToMany .

Ahora, para que la cascada funcione correctamente de forma bidireccional, también debe asegurarse de que las asociaciones padre e hijo estén sincronizadas.

Consulte este artículo para obtener más detalles sobre cuál es la mejor manera de lograr este objetivo.

La @ManyToManyasociación

@ManyToMany(
    mappedBy = "authors",
    cascade = {
        CascadeType.PERSIST, 
        CascadeType.MERGE
    }
)
private List<Book> books = new ArrayList<>();

En una @ManyToManyasociación, no puede usar CascadeType.ALLo orphanRemovalya que esto propagará la transición del estado de eliminar entidad de una entidad primaria a otra entidad principal.

Por lo tanto, para @ManyToManylas asociaciones, por lo general, la cascada de módulos CascadeType.PERSISTo de CascadeType.MERGEoperaciones. Alternativamente, puede expandir eso a DETACHo REFRESH.

Para obtener más detalles sobre la mejor manera de asignar una @ManyToManyasociación, consulte también este artículo .

Vlad Mihalcea
fuente
¡Explicación completa y consistente! ¡Buen trabajo!
Frío
Puede encontrar cientos de explicaciones detalladas en mi tutorial de Hibernate .
Vlad Mihalcea
30

O, si desea usar "poderes" mínimos (por ejemplo, si no desea una eliminación en cascada) para lograr lo que desea, use

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;

...

@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
Haris Osmanagić
fuente
11
Esta debería ser la respuesta aceptada. CascadeType.ALL no es demasiado amplio
lilalinux
55
A partir de Hibernate 5.2.8, parece que no hay forma de lograr el mismo efecto con las anotaciones JPA. Por ejemplo, @ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})(todos menos ELIMINAR) no actualiza en cascada como lo hace Hibernate CascadeType.SAVE_UPDATE.
jcsahnwaldt dice GoFundMonica
25

En mi caso fue causado por no tener CascadeTypedel @ManyToOnelado de la relación bidireccional. Para ser más precisos, estaba CascadeType.ALLde @OneToManylado y no lo tenía puesto @ManyToOne. Agregando CascadeType.ALLa @ManyToOneresuelto el problema. Lado uno a muchos :

@OneToMany(cascade = CascadeType.ALL, mappedBy="globalConfig", orphanRemoval = true)
private Set<GlobalConfigScope>gcScopeSet;

Lado de muchos a uno (causó el problema)

@ManyToOne
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;

Muchos a uno (arreglado al agregar CascadeType.PERSIST)

@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
allap
fuente
Si lo hago de esta manera y guardo la entidad principal, lo que sucede es que hay dos inserciones en mi tabla principal que son dos filas. Creo que es porque tenemos una cascada en las entidades primarias y secundarias.
theprogrammer
18

Esto ocurrió cuando persistí en una entidad en la que el registro existente en la base de datos tenía un valor NULL para el campo anotado con @Version (para un bloqueo optimista). La actualización del valor NULL a 0 en la base de datos corrigió esto.

dukethrash
fuente
Esta debería ser una nueva pregunta y debería agregarse como un error, al menos la excepción engañosa. Esto resultó ser la causa de mi problema.
BML
Esto soluciona mi problema
Jad Chahine
11

Esta no es la única razón del error. Lo encontré hace un momento por un error tipográfico en mi codificación, que creo que estableció un valor de una entidad que ya estaba guardada.

X x2 = new X();
x.setXid(memberid); // Error happened here - x was a previous global entity I created earlier
Y.setX(x2);

Descubrí el error al encontrar exactamente qué variable causó el error (en este caso String xid). Utilicé un catchalrededor de todo el bloque de código que salvó a la entidad e imprimió los rastros.

{
   code block that performed the operation
} catch (Exception e) {
   e.printStackTrace(); // put a break-point here and inspect the 'e'
   return ERROR;
}
nanospeck
fuente
1
Problema similar al mío. Después de todo, cuando volví a cargar la entidad localmente, configuré la propiedad y luego la guardé, funcionó bien.
CsBalazsHungary
8

No lo uses Cascade.Allhasta que realmente tengas que hacerlo. Roley Permissiontienen manyToManyrelación bidireccional . Entonces el siguiente código funcionaría bien

    Permission p = new Permission();
    p.setName("help");
    Permission p2 = new Permission();
    p2.setName("self_info");
    p = (Permission)crudRepository.save(p); // returned p has id filled in.
    p2 = (Permission)crudRepository.save(p2); // so does p2.
    Role role = new Role();
    role.setAvailable(true);
    role.setDescription("a test role");
    role.setRole("admin");
    List<Permission> pList = new ArrayList<Permission>();
    pList.add(p);
    pList.add(p2);
    role.setPermissions(pList);
    crudRepository.save(role);

mientras que si el objeto es solo "nuevo", arrojaría el mismo error.

Tiina
fuente
1
es 100% correcto, Cascade. All es la solución perezosa y solo debe aplicarse cuando sea necesario. primero, si la entidad ya existe, verifique si está cargada en el administrador de la entidad actual, si no la carga.
Renato Mendes
7

Si su colección es anulable, simplemente intente: object.SetYouColection(null);

Joabe Lucena
fuente
Este fue totalmente mi problema. Nunca hubiera adivinado que tenía que configurarlo manualmente como nulo.
Deadron
Este también fue mi problema. No estaba usando una colección, así que no lo intenté al principio, pero configuré mi objeto como nulo y ahora funciona.
Forraje
5

Para agregar mis 2 centavos, recibí este mismo problema cuando envío accidentalmente nullcomo ID. El siguiente código muestra mi escenario (y OP no mencionó ningún escenario específico) .

Employee emp = new Employee();
emp.setDept(new Dept(deptId)); // --> when deptId PKID is null, same error will be thrown
// calls to other setters...
em.persist(emp);

Aquí estoy configurando el ID del departamento existente a una nueva instancia de empleado sin obtener primero la entidad del departamento, ya que no quiero que se active otra consulta de selección.

En algunos escenarios, el deptIdPKID proviene nulldel método de llamada y obtengo el mismo error.

Por lo tanto, observe los nullvalores para PK ID

manikanta
fuente
¿Qué pasa si es anulable?
vipin cp
Tengo un problema similar Obtengo la excepción cuando mi deptID es 0. Cualquier otro valor mayor que 0 funciona. Es gracioso que tengo un departamento con id = 0.
Gustavo
5

Este problema me ocurrió cuando creé una nueva entidad y una entidad asociada en un método marcado como @Transactional, y luego realicé una consulta antes de guardar. Ex

@Transactional
public someService() {
    Entity someEntity = new Entity();
    AssocaiatedEntity associatedEntity = new AssocaitedEntity();
    someEntity.setAssociatedEntity(associatedEntity);
    associatedEntity.setEntity(someEntity);

    // Performing any query was causing hibernate to attempt to persist the new entity. It would then throw an exception
    someDao.getSomething();

    entityDao.create(someEntity);
}

Para solucionarlo, realicé la consulta antes de crear la nueva entidad.

mad_fox
fuente
4

además de todas las otras buenas respuestas, esto podría suceder si usas mergepara persistir un objeto y accidentalmente olvidas usar la referencia fusionada del objeto en la clase principal. considera el siguiente ejemplo

merge(A);
B.setA(A);
persist(B);

En este caso, se fusiona Apero se olvida usar el objeto fusionado de A. Para resolver el problema, debe volver a escribir el código de esta manera.

A=merge(A);//difference is here
B.setA(A);
persist(B);
Hossein Nasr
fuente
3

me sale este error cuando uso

getSession().save(object)

pero funciona sin problemas cuando uso

getSession().saveOrUpdate(object) 
acpuma
fuente
3

También me enfrenté a la misma situación. Al establecer la siguiente anotación sobre la propiedad, se resolvió la excepción solicitada.

La excepción que enfrenté.

Exception in thread "main" java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.model.Car_OneToMany

Para superar, la anotación que utilicé.

    @OneToMany(cascade = {CascadeType.ALL})
    @Column(name = "ListOfCarsDrivenByDriver")
    private List<Car_OneToMany> listOfCarsBeingDriven = new ArrayList<Car_OneToMany>();

Lo que hizo que Hibernate lanzara la excepción:

Esta excepción se produce en su consola porque el objeto secundario que adjunto al objeto primario no está presente en la base de datos en ese momento.

Al proporcionar @OneToMany(cascade = {CascadeType.ALL}), le dice a Hibernate que los guarde en la base de datos mientras guarda el objeto padre.

Dulith De Costa
fuente
2

En aras de la exhaustividad: A

org.hibernate.TransientPropertyValueException 

con mensaje

object references an unsaved transient instance - save the transient instance before flushing

También ocurrirá cuando intente persistir / fusionar una entidad con una referencia a otra entidad que se separa .

Ralf
fuente
1

Otra posible razón: en mi caso, estaba tratando de salvar al niño antes de salvar al padre, en una entidad nueva.

El código era algo así en un modelo User.java:

this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();

El método setNewPassword () crea un registro PasswordHistory y lo agrega a la colección de historial en Usuario. Como la instrucción create () aún no se había ejecutado para el padre, estaba intentando guardar en una colección de una entidad que aún no se había creado. Todo lo que tuve que hacer para solucionarlo fue mover la llamada setNewPassword () después de la llamada para crear ().

this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);
Jeremy Goodell
fuente
1

Hay otra posibilidad que puede causar este error en hibernación. Puede establecer una referencia no guardada de su objeto Aa una entidad adjunta By desea conservar el objeto C. Incluso en este caso, obtendrá el error antes mencionado.

Hossein Nasr
fuente
1

Si está utilizando Spring Data JPA, la @Transactionalanotación adicional a la implementación de su servicio resolvería el problema.

Usama
fuente
1

Creo que es porque has intentado persistir un objeto que tiene una referencia a otro objeto que aún no persiste, y por eso intenta en el "lado DB" poner una referencia a una fila que no existe


fuente
0

Una forma simple de resolver este problema es salvar a ambas entidades. primero guarde la entidad secundaria y luego guarde la entidad principal. Porque la entidad principal depende de la entidad secundaria para el valor de la clave externa.

A continuación, un simple examen de la relación uno a uno

insert into Department (name, numOfemp, Depno) values (?, ?, ?)
Hibernate: insert into Employee (SSN, dep_Depno, firstName, lastName, middleName, empno) values (?, ?, ?, ?, ?, ?)

Session session=sf.openSession();
        session.beginTransaction();
        session.save(dep);
        session.save(emp);
shashigura
fuente
1
Es lo contrario: la entidad secundaria tiene el valor FK y depende de la matriz, por lo que debe guardar la matriz primero. En el bloque de código lo tienes correcto.
Sábado
0

Una posible causa del error es la inexistencia del establecimiento del valor de la entidad matriz; por ejemplo, para una relación departamento-empleados, debe escribir esto para corregir el error:

Department dept = (Department)session.load(Department.class, dept_code); // dept_code is from the jsp form which you get in the controller with @RequestParam String department
employee.setDepartment(dept);
feromix
fuente
0

Hay tantas posibilidades de este error que algunas otras posibilidades también están en agregar página o editar página. En mi caso, estaba tratando de guardar un objeto AdvanceSalary. El problema es que en la edición AdvanceSalary employee.employee_id es nulo. Porque en la edición no estaba configurado el employee.employee_id. He creado un campo oculto y lo configuré. Mi código funciona absolutamente bien.

    @Entity(name = "ic_advance_salary")
    @Table(name = "ic_advance_salary")
    public class AdvanceSalary extends BaseDO{

        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "id")
        private Integer id;

        @ManyToOne(fetch = FetchType.EAGER)
        @JoinColumn(name = "employee_id", nullable = false)
        private Employee employee;

        @Column(name = "employee_id", insertable=false, updatable=false)
        @NotNull(message="Please enter employee Id")
        private Long employee_id;

        @Column(name = "advance_date")
        @DateTimeFormat(pattern = "dd-MMM-yyyy")
        @NotNull(message="Please enter advance date")
        private Date advance_date;

        @Column(name = "amount")
        @NotNull(message="Please enter Paid Amount")
        private Double amount;

        @Column(name = "cheque_date")
        @DateTimeFormat(pattern = "dd-MMM-yyyy")
        private Date cheque_date;

        @Column(name = "cheque_no")
        private String cheque_no;

        @Column(name = "remarks")
        private String remarks;

        public AdvanceSalary() {
        }

        public AdvanceSalary(Integer advance_salary_id) {
            this.id = advance_salary_id;
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public Employee getEmployee() {
            return employee;
        }

        public void setEmployee(Employee employee) {
            this.employee = employee;
        }


        public Long getEmployee_id() {
            return employee_id;
        }

        public void setEmployee_id(Long employee_id) {
            this.employee_id = employee_id;
        }

    }
Ravindra
fuente
0

Me enfrenté a esta excepción cuando no persistí en el objeto padre pero estaba guardando al hijo. Para resolver el problema, en la misma sesión insistí en los objetos secundarios y principales y usé CascadeType.ALL en el principal.

Tadele Ayelegn
fuente
0

Caso 1: Recibía esta excepción cuando intentaba crear un padre y guardaba esa referencia primaria en su hijo y luego en otra consulta DELETE / UPDATE (JPQL). Así que simplemente elimino () la entidad recién creada después de crear el padre y después de crear el niño usando la misma referencia principal. Funcionó para mí

Caso 2:

Clase de padres

public class Reference implements Serializable {

    @Id
    @Column(precision=20, scale=0)
    private BigInteger id;

    @Temporal(TemporalType.TIMESTAMP)
    private Date modifiedOn;

    @OneToOne(mappedBy="reference")
    private ReferenceAdditionalDetails refAddDetails;
    . 
    .
    .
}

Clase infantil:

public class ReferenceAdditionalDetails implements Serializable{

    private static final long serialVersionUID = 1L;

    @Id
    @OneToOne
    @JoinColumn(name="reference",referencedColumnName="id")
    private Reference reference;

    private String preferedSector1;
    private String preferedSector2;
    .
    .

}

En el caso anterior, donde padre (Referencia) e hijo (ReferenceAdditionalDetails) tienen una relación OneToOne y cuando intenta crear una entidad de Referencia y luego su hijo (ReferenceAdditionalDetails), le dará la misma excepción. Entonces, para evitar la excepción, debe establecer un valor nulo para la clase secundaria y luego crear el padre (Código de muestra)

.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.
Harsh Vardhan Singh Solanki
fuente
0

Mi problema estaba relacionado con @BeforeEachJUnit. E incluso si guardé las entidades relacionadas (en mi caso@ManyToOne ), obtuve el mismo error.

El problema está relacionado de alguna manera con la secuencia que tengo en mi padre. Si asigno el valor a ese atributo, el problema está resuelto.

Ex. Si tengo la Pregunta de entidad que puede tener algunas categorías (una o más) y la Pregunta de entidad tiene una secuencia:

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
@Id
private Long id;

Tengo que asignar el valor question.setId(1L);

NikNik
fuente
0

Simplemente haga Constructor de su mapeo en su clase base. Al igual que si desea una relación uno a uno en la entidad A, la entidad B. si está tomando A como clase base, entonces A debe tener un constructor con B como argumento.

Bilal Ahmad
fuente