Come correggere l'ibernazione "l'oggetto fa riferimento a un'istanza transitoria non salvata - salvare l'istanza transitoria prima di svuotare"


610

Ricevo il seguente errore quando salvo l'oggetto usando Hibernate

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

1
Nel contesto desiderato è questo errore? è con o senza variabile transitoria?
vijay,

Risposte:


803

È necessario includere cascade="all"(se si utilizza xml) o cascade=CascadeType.ALL(se si utilizzano annotazioni) nel mapping della raccolta.

Ciò accade perché hai una raccolta nella tua entità e quella raccolta ha uno o più elementi che non sono presenti nel database. Specificando le opzioni sopra indicate, si dice a ibernazione di salvarle nel database quando si salva il loro genitore.


7
Questo non è implicito? Non vorresti sempre che Hibernate li salvasse?
Marcus Leon,

29
@Marcus - no, non lo è. Potrebbe essere necessario gestirli manualmente.
Bozho,

5
Bozho ha ragione. Ho riscontrato circostanze in cui avevo raccolte che volevo gestire manualmente a causa delle loro dimensioni o a causa delle regole aziendali che non consentono di salvare tutti gli oggetti in una raccolta contemporaneamente.
Alex Marshall,

26
Non succede solo per le raccolte ma anche per semplici mappature one to one
Sebastien Lorber,

12
Non sarebbe meglio iniziare con CascadeType.PERSIST e usare persist per salvare?
Sergii Shevchyk,

248

Credo che questa potrebbe essere solo una risposta ripetuta, ma solo per chiarire, ho ottenuto questo su una @OneToOnemappatura e su un @OneToMany. In entrambi i casi, era il fatto che l' Childoggetto che stavo aggiungendo Parentnon era ancora salvato nel database. Quindi quando ho aggiunto il Childal Parent, quindi salvato il Parent, Hibernate lanciava il "object references an unsaved transient instance - save the transient instance before flushing"messaggio quando salvavo il Parent.

Aggiungendo cascade = {CascadeType.ALL}il Parent'sriferimento al Childrisolto il problema in entrambi i casi. Ciò ha salvato il Childe il Parent.

Ci scusiamo per le risposte ripetute, volevo solo chiarire ulteriormente per la gente.

@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
    return performanceLog;
}

7
E se non volessi salvare a cascata i salvataggi su una relazione @OneToOne? Quando si creano entrambi gli oggetti per la prima volta, come è possibile salvare entrambi nel database senza attivare l'eccezione?
xtian,

4
E se volessi salvare il bambino per alcuni casi e non per altri?
Omaruchan,

@xtian: Bene, allora devi prenderti cura del giusto ordine di salvataggio nel database persistendo gli oggetti con un EntityManager. In basic dite semplicemente em.persist (oggetto1); em.persist (Object2); ecc.
kaba713,

Ho riscontrato questo problema specificamente quando ho usato @Inheritance, in questo caso TABLE_PER_CLASS, mi riferivo a una sottoclasse. CascadeType.ALL lo ha riparato.
Jim ReesPotter

Oppure hai creato l'oggetto entità con new MyEntity(senza sincronizzarlo con il database - flushing), invece di ottenere la sua istanza sincronizzata dal database. Fare query Hibernate usando quell'istanza ti informa che ciò che ti aspetti di essere nel database differisce da quello che hai nella memoria della tua app. In questo caso, basta sincronizzare / ottenere instabnce dall'entità da DB e usarlo. Non è necessario quindi CascadeType.ALL.
Zon,

67

Ciò accade quando si salva un oggetto quando Hibernate pensa di dover salvare un oggetto associato a quello che si sta salvando.

Ho avuto questo problema e non volevo salvare le modifiche all'oggetto referenziato, quindi volevo che il tipo a cascata fosse NESSUNO.

Il trucco è assicurarsi che l'ID e la VERSIONE nell'oggetto referenziato siano impostati in modo tale che Hibernate non pensi che l'oggetto referenziato sia un nuovo oggetto che deve essere salvato. Questo ha funzionato per me.

Esaminare tutte le relazioni nella classe che si sta salvando per elaborare gli oggetti associati (e gli oggetti associati degli oggetti associati) e assicurarsi che l'ID e la VERSIONE siano impostati in tutti gli oggetti dell'albero degli oggetti.


4
Questo commento mi ha messo sulla strada giusta. Stavo assegnando una nuova istanza del genitore a una proprietà di suo figlio. Quindi NH pensava che fossero casi diversi.
elvin,

2
Sì. Ciò accade se, ad esempio, l'id dell'oggetto associato non è incluso (ad esempio, è stato ignorato da @JsonIgnore). Hibernate non ha modo di identificare l'entità associata, quindi vuole salvarla.
Rori Stumpf,

36

introduzione

Come ho spiegato in questo articolo quando si utilizzano JPA e Hibernate, un'entità può trovarsi in uno dei seguenti 4 stati:

  • Nuovo : un oggetto appena creato che non è mai stato associato a una sessione di ibernazione (noto anche come contesto di persistenza) e che non è associato a nessuna riga della tabella del database è considerato nello stato Nuovo o Transitorio.

    Per perseverare, è necessario chiamare esplicitamente il persistmetodo o utilizzare il meccanismo di persistenza transitiva.

  • Persistente : un'entità persistente è stata associata a una riga della tabella del database ed è gestita dal contesto di persistenza attualmente in esecuzione.

    Qualsiasi modifica apportata a tale entità verrà rilevata e propagata al database (durante il flush-time della sessione).

  • Staccato : una volta chiuso il contesto di persistenza attualmente in esecuzione, tutte le entità gestite in precedenza vengono staccate. Le modifiche successive non verranno più monitorate e non verrà eseguita alcuna sincronizzazione automatica del database.

  • Rimosso - Sebbene l'APP richieda che solo le entità gestite possano essere rimosse, Hibernate può anche eliminare entità distaccate (ma solo tramite una removechiamata di metodo).

Transizioni dello stato delle entità

Per spostare un'entità da uno stato all'altro, è possibile utilizzare i persist, removeo mergemetodi.

Stati dell'entità JPA

Risolvere il problema

Il problema che stai descrivendo nella tua domanda:

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

è causato dall'associazione di un'entità nello stato di Nuovo a un'entità che si trova nello stato Gestito .

Ciò può accadere quando si associa un'entità figlio a una raccolta uno-a-molti nell'entità padre e la raccolta non esegue cascadele transizioni di stato dell'entità.

Quindi, come ho spiegato in questo articolo , è possibile risolvere questo problema aggiungendo in cascata l'associazione entità che ha innescato questo errore, come segue:

L' @OneToOneassociazione

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

Nota il CascadeType.ALLvalore che abbiamo aggiunto per l' cascadeattributo.

L' @OneToManyassociazione

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

Ancora una volta, CascadeType.ALLè adatto per le associazioni bidirezionali@OneToMany .

Ora, affinché la cascata funzioni correttamente in modo bidirezionale, è necessario assicurarsi che le associazioni padre e figlio siano sincronizzate.

Consulta questo articolo per maggiori dettagli su qual è il modo migliore per raggiungere questo obiettivo.

L' @ManyToManyassociazione

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

In @ManyToManyun'associazione, non è possibile utilizzare CascadeType.ALLo orphanRemovalpoiché ciò propagherà la transizione dello stato dell'entità di eliminazione da un genitore a un'altra entità genitore.

Pertanto, per le @ManyToManyassociazioni, di solito si eseguono operazioni a cascata CascadeType.PERSISTo CascadeType.MERGE. In alternativa, puoi espanderlo a DETACHo REFRESH.

Per ulteriori dettagli sul modo migliore per mappare @ManyToManyun'associazione, consulta anche questo articolo .


Spiegazione completa e coerente! Buon lavoro!
Freddo

Puoi trovare centinaia di spiegazioni così dettagliate nel mio tutorial di Hibernate .
Vlad Mihalcea,

30

Oppure, se si desidera utilizzare "poteri" minimi (ad es. Se non si desidera eliminare a cascata) per ottenere ciò che si desidera, utilizzare

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

...

@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;

11
Questa dovrebbe essere la risposta accettata. CascadeType.ALL è troppo ampio
lilalinux il

5
A partire da Hibernate 5.2.8, non sembra esserci modo di ottenere lo stesso effetto con le annotazioni JPA. Ad esempio, @ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})(tutti tranne RIMUOVI) non effettua aggiornamenti in cascata come fa Hibernate CascadeType.SAVE_UPDATE.
jcsahnwaldt dice GoFundMonica

25

Nel mio caso è stato causato dal fatto di non avere CascadeTypedalla @ManyToOneparte della relazione bidirezionale. Per essere più precisi, avevo CascadeType.ALLdalla @OneToManyparte e non ce l' avevo @ManyToOne. Aggiunta CascadeType.ALLper @ManyToOnerisolvere il problema. Lato uno-a-molti :

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

Lato multiplo (ha causato il problema)

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

Many-to-one (risolto aggiungendo CascadeType.PERSIST)

@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;

Se lo faccio in questo modo e salvo l'entità padre, ciò che sta accadendo è che nella mia tabella padre sono presenti due inserti di due righe. Penso che sia perché abbiamo cascata sia su entità padre che figlio?
programmatore

18

Ciò si è verificato durante il persistere di un'entità in cui il record esistente nel database aveva un valore NULL per il campo annotato con @Version (per il blocco ottimistico). L'aggiornamento del valore NULL su 0 nel database ha corretto questo.


Questa dovrebbe essere una nuova domanda e dovrebbe essere aggiunta come un bug, almeno l'eccezione fuorviante. Questo si è rivelato essere la causa del mio problema.
BML,

Questo risolve il mio problema
Jad Chahine il

11

Questo non è l'unico motivo dell'errore. L'ho riscontrato proprio ora per un errore di battitura nella mia codifica, che credo abbia impostato il valore di un'entità che era già stata salvata.

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

Ho individuato l'errore trovando esattamente quale variabile ha causato l'errore (in questo caso String xid). Ho usato un catchtutto il blocco di codice che ha salvato l'entità e stampato le tracce.

{
   code block that performed the operation
} catch (Exception e) {
   e.printStackTrace(); // put a break-point here and inspect the 'e'
   return ERROR;
}

1
Problema simile al mio. Dopotutto, quando ho ricaricato l'entità localmente, impostato la proprietà, quindi salvato, ha funzionato bene.
CsBalazs Ungheria,

8

Non usare Cascade.Allfino a quando non è necessario. Rolee Permissionhanno una manyToManyrelazione bidirezionale . Quindi il seguente codice funzionerebbe bene

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

mentre se l'oggetto è solo uno "nuovo", si genererebbe lo stesso errore.


1
è corretto al 100%, Cascade.All è la soluzione pigra e deve essere applicata solo quando richiesto. in primo luogo, se l'entità esiste già, controlla se è caricata nel gestore entità corrente, se non la carica.
Renato Mendes,

7

Se la tua raccolta è nullable prova semplicemente: object.SetYouColection(null);


Questo è stato totalmente un mio problema. Non avrei mai immaginato di doverlo impostare manualmente su null.
Deadron,

Questo è stato anche il mio problema. Non stavo usando una collezione, quindi all'inizio non l'ho provato, ma ho impostato il mio oggetto su null e ora funziona.
Foraggio

5

Per aggiungere i miei 2 centesimi, ho riscontrato lo stesso problema quando invio accidentalmente nullcome ID. Il codice seguente descrive il mio scenario (e OP non ha menzionato alcuno scenario specifico) .

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

Qui sto impostando l'ID reparto esistente su una nuova istanza di dipendente senza effettivamente prima ottenere l'entità reparto, poiché non desidero attivare un'altra query selezionata.

In alcuni scenari, deptIdPKID proviene nulldal metodo di chiamata e sto ottenendo lo stesso errore.

Quindi, cerca i nullvalori per l'ID PK


e se fosse nullable?
vipin cp,

Ho un problema simile Ottengo l'eccezione quando il mio deptID è 0. Qualsiasi altro valore maggiore di 0 funziona. Divertente è che ho un reparto con id = 0.
Gustavo,

5

Questo problema si è verificato quando ho creato una nuova entità e un'entità associata in un metodo contrassegnato come @Transactional, quindi ho eseguito una query prima di salvare. 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);
}

Per risolvere, ho eseguito la query prima di creare la nuova entità.


4

oltre a tutte le altre buone risposte, ciò può accadere se si utilizza mergeper persistere un oggetto e si dimentica accidentalmente di utilizzare il riferimento unito dell'oggetto nella classe genitore. considera il seguente esempio

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

In questo caso, si unisce Ama si dimentica di utilizzare l'oggetto unito di A. per risolvere il problema è necessario riscrivere il codice in questo modo.

A=merge(A);//difference is here
B.setA(A);
persist(B);

3

ottengo questo errore quando uso

getSession().save(object)

ma funziona senza problemi quando lo uso

getSession().saveOrUpdate(object) 

3

Ho anche affrontato la stessa situazione. Impostando la seguente annotazione sopra la proprietà, è stata risolta l'eccezione richiesta.

L'eccezione che ho affrontato.

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

Per superare, l'annotazione che ho usato.

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

Cosa ha fatto sì che Hibernate lanciasse l'eccezione:

Questa eccezione viene generata sulla tua console perché l'oggetto figlio che allego all'oggetto genitore non è presente nel database in quel momento.

Fornendo @OneToMany(cascade = {CascadeType.ALL}), indica a Hibernate di salvarli nel database mentre si salva l'oggetto principale.


2

Per completezza: A

org.hibernate.TransientPropertyValueException 

con messaggio

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

si verificherà anche quando si tenta di persistere / unire un'entità con un riferimento a un'altra entità che risulta staccata .


1

Un'altra possibile ragione: nel mio caso, stavo tentando di salvare il bambino prima di salvare il genitore, su un'entità nuova di zecca.

Il codice era qualcosa del genere in un modello User.java:

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

Il metodo setNewPassword () crea un record PasswordHistory e lo aggiunge alla raccolta della cronologia in Utente. Poiché l'istruzione create () non era stata ancora eseguita per il genitore, stava cercando di salvare in una raccolta di un'entità che non era ancora stata creata. Tutto quello che dovevo fare per risolvere il problema era spostare la chiamata setNewPassword () dopo la chiamata a create ().

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

1

Esiste un'altra possibilità che può causare questo errore in ibernazione. È possibile impostare un riferimento non salvato dell'oggetto Asu un'entità collegata Be si desidera mantenere l'oggetto C. Anche in questo caso, otterrai il suddetto errore.


1

Se si utilizza JPA Spring Data, l'aggiunta di @Transactionalannotazioni all'implementazione del servizio risolverà il problema.


1

Penso che sia perché hai provato a persistere un oggetto che ha un riferimento a un altro oggetto che non è ancora persistente, e quindi prova nel "lato DB" per inserire un riferimento a una riga che non esiste


0

Un modo semplice per risolvere questo problema è salvare entrambe le entità. prima salvare l'entità figlio e quindi salvare l'entità padre. Perché l'entità padre dipende dall'entità figlio per il valore della chiave esterna.

Di seguito semplice esame di una relazione 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);

1
È l'opposto: l'entità figlio contiene il valore FK e dipende dal genitore, quindi devi prima salvarlo! Nel blocco di codice hai ragione.
settembre

0

Una possibile causa dell'errore è l'inesistenza dell'impostazione del valore dell'entità capogruppo; ad esempio per una relazione reparto-dipendenti devi scrivere questo per correggere l'errore:

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

0

Ci sono così tante possibilità di questo errore alcune altre possibilità sono anche su Aggiungi pagina o Modifica pagina. Nel mio caso stavo cercando di salvare un oggetto AdvanceSalary. Il problema è che nella modifica AdvanceSalary employee.employee_id è null Perché in fase di modifica non sono stato impostato employee.employee_id. Ho creato un campo nascosto e lo ho impostato. il mio codice funziona perfettamente.

    @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;
        }

    }

0

Ho riscontrato questa eccezione quando non ho persistito l'oggetto principale ma stavo salvando il bambino. Per risolvere il problema, con nella stessa sessione ho persistito sia gli oggetti figlio che padre e ho usato CascadeType.ALL sul padre.


0

Caso 1: stavo ottenendo questa eccezione quando stavo provando a creare un genitore e salvando quel riferimento del genitore al suo figlio e poi qualche altra query DELETE / UPDATE (JPQL). Quindi ho appena scaricato () l'entità appena creata dopo aver creato parent e dopo aver creato child usando lo stesso riferimento parent. Ha funzionato per me.

Caso 2:

Classe genitore

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;
    . 
    .
    .
}

Classe bambino:

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;
    .
    .

}

Nel caso precedente in cui parent (Reference) e child (ReferenceAdditionalDetails) hanno una relazione OneToOne e quando si tenta di creare un'entità di riferimento e quindi il relativo child (ReferenceAdditionalDetails), si otterrà la stessa eccezione. Quindi, per evitare l'eccezione, devi impostare null per la classe figlio e quindi creare il genitore. (Codice di esempio)

.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.

0

Il mio problema era legato a @BeforeEachJUnit. E anche se ho salvato le entità correlate (nel mio caso @ManyToOne), ho avuto lo stesso errore.

Il problema è in qualche modo legato alla sequenza che ho nel mio genitore. Se assegno il valore a quell'attributo, il problema è risolto.

Ex. Se ho l'entità Domanda che può avere alcune categorie (una o più) e l'entità Domanda ha una sequenza:

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

Devo assegnare il valore question.setId(1L);


0

Fai solo Costruttore della tua mappatura nella tua classe base. Ad esempio, se vuoi una relazione One-to-One in Entità A, Entità B. se stai prendendo A come classe base, allora A deve avere un Costruttore con B come argomento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.