Ricevo il seguente errore quando salvo l'oggetto usando Hibernate
object references an unsaved transient instance - save the transient instance before flushing
Ricevo il seguente errore quando salvo l'oggetto usando Hibernate
object references an unsaved transient instance - save the transient instance before flushing
Risposte:
È 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.
Credo che questa potrebbe essere solo una risposta ripetuta, ma solo per chiarire, ho ottenuto questo su una @OneToOne
mappatura e su un @OneToMany
. In entrambi i casi, era il fatto che l' Child
oggetto che stavo aggiungendo Parent
non era ancora salvato nel database. Quindi quando ho aggiunto il Child
al 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's
riferimento al Child
risolto il problema in entrambi i casi. Ciò ha salvato il Child
e 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;
}
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.
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.
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 persist
metodo 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 remove
chiamata di metodo).
Per spostare un'entità da uno stato all'altro, è possibile utilizzare i persist
, remove
o merge
metodi.
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 cascade
le 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:
@OneToOne
associazione@OneToOne(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private PostDetails details;
Nota il
CascadeType.ALL
valore che abbiamo aggiunto per l'cascade
attributo.
@OneToMany
associazione@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.
@ManyToMany
associazione@ManyToMany(
mappedBy = "authors",
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}
)
private List<Book> books = new ArrayList<>();
In @ManyToMany
un'associazione, non è possibile utilizzare CascadeType.ALL
o orphanRemoval
poiché ciò propagherà la transizione dello stato dell'entità di eliminazione da un genitore a un'altra entità genitore.
Pertanto, per le @ManyToMany
associazioni, di solito si eseguono operazioni a cascata CascadeType.PERSIST
o CascadeType.MERGE
. In alternativa, puoi espanderlo a DETACH
o REFRESH
.
Per ulteriori dettagli sul modo migliore per mappare
@ManyToMany
un'associazione, consulta anche questo articolo .
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;
@ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})
(tutti tranne RIMUOVI) non effettua aggiornamenti in cascata come fa Hibernate CascadeType.SAVE_UPDATE
.
Nel mio caso è stato causato dal fatto di non avere CascadeType
dalla @ManyToOne
parte della relazione bidirezionale. Per essere più precisi, avevo CascadeType.ALL
dalla @OneToMany
parte e non ce l' avevo @ManyToOne
. Aggiunta CascadeType.ALL
per @ManyToOne
risolvere 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;
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.
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 catch
tutto 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;
}
Non usare Cascade.All
fino a quando non è necessario. Role
e Permission
hanno una manyToMany
relazione 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.
Se la tua raccolta è nullable prova semplicemente: object.SetYouColection(null);
Per aggiungere i miei 2 centesimi, ho riscontrato lo stesso problema quando invio accidentalmente null
come 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, deptId
PKID proviene null
dal metodo di chiamata e sto ottenendo lo stesso errore.
Quindi, cerca i null
valori per l'ID PK
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à.
oltre a tutte le altre buone risposte, ciò può accadere se si utilizza merge
per 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 A
ma 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);
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.
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 .
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);
Esiste un'altra possibilità che può causare questo errore in ibernazione. È possibile impostare un riferimento non salvato dell'oggetto A
su un'entità collegata B
e si desidera mantenere l'oggetto C
. Anche in questo caso, otterrai il suddetto errore.
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
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);
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);
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;
}
}
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.
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();
.
.
Il mio problema era legato a @BeforeEach
JUnit. 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);
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.