Je reçois l'erreur suivante lorsque j'enregistre l'objet à l'aide d'Hibernate
object references an unsaved transient instance - save the transient instance before flushing
Je reçois l'erreur suivante lorsque j'enregistre l'objet à l'aide d'Hibernate
object references an unsaved transient instance - save the transient instance before flushing
Réponses:
Vous devez inclure cascade="all"
(si vous utilisez xml) ou cascade=CascadeType.ALL
(si vous utilisez des annotations) sur le mappage de votre collection.
Cela se produit car vous avez une collection dans votre entité et cette collection a un ou plusieurs éléments qui ne sont pas présents dans la base de données. En spécifiant les options ci-dessus, vous dites à hibernate de les enregistrer dans la base de données lors de l'enregistrement de leur parent.
Je crois que cela pourrait être juste une réponse répétée, mais juste pour clarifier, j'ai obtenu ceci sur une @OneToOne
cartographie ainsi que sur un @OneToMany
. Dans les deux cas, c'était le fait que l' Child
objet que j'ajoutais Parent
n'était pas encore enregistré dans la base de données. Ainsi, lorsque j'ajoutais le Child
à Parent
, puis enregistrais le Parent
, Hibernate lancerait le "object references an unsaved transient instance - save the transient instance before flushing"
message lors de l'enregistrement du parent.
Ajout dans le cascade = {CascadeType.ALL}
sur la Parent's
référence au Child
résoudre le problème dans les deux cas. Cela a sauvé le Child
et le Parent
.
Désolé pour toutes les réponses répétées, je voulais juste clarifier davantage pour les gens.
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
return performanceLog;
}
new MyEntity
(sans le synchroniser avec la base de données - vidage), au lieu d'obtenir son instance synchronisée de la base de données. Faire des requêtes Hibernate en utilisant cette instance vous informe que ce que vous attendez d'être dans la base de données diffère de ce que vous avez dans la mémoire de votre application. Dans ce cas, synchronisez / obtenez simplement votre entité à partir de la base de données et utilisez-la. Aucun CascadeType.ALL n'est alors nécessaire.
Cela se produit lors de l'enregistrement d'un objet lorsque Hibernate pense qu'il doit enregistrer un objet associé à celui que vous enregistrez.
J'ai eu ce problème et je ne voulais pas enregistrer les modifications apportées à l'objet référencé, je voulais donc que le type de cascade soit AUCUN.
L'astuce consiste à s'assurer que l'ID et la VERSION dans l'objet référencé sont définis de sorte que Hibernate ne pense pas que l'objet référencé est un nouvel objet qui doit être enregistré. Cela a fonctionné pour moi.
Parcourez toutes les relations de la classe que vous enregistrez pour déterminer les objets associés (et les objets associés des objets associés) et assurez-vous que l'ID et la VERSION sont définis dans tous les objets de l'arborescence d'objets.
Comme je l'ai expliqué dans cet article lors de l'utilisation de JPA et Hibernate, une entité peut se trouver dans l'un des 4 états suivants:
Nouveau - Un objet nouvellement créé qui n'a jamais été associé à une session de mise en veille prolongée (aka Contexte de persistance) et qui n'est mappé à aucune ligne de table de base de données est considéré comme étant à l'état Nouveau ou Transitoire.
Pour devenir persistant, nous devons soit appeler explicitement la persist
méthode, soit utiliser le mécanisme de persistance transitive.
Persistant - Une entité persistante a été associée à une ligne de table de base de données et elle est gérée par le contexte de persistance en cours d'exécution.
Toute modification apportée à une telle entité va être détectée et propagée à la base de données (pendant le vidage de session).
Détaché - Une fois que le contexte de persistance en cours d'exécution est fermé, toutes les entités précédemment gérées se détachent. Les modifications successives ne seront plus suivies et aucune synchronisation automatique de la base de données ne se produira.
Supprimé - Bien que JPA exige que seules les entités gérées soient supprimées, Hibernate peut également supprimer les entités détachées (mais uniquement via un remove
appel de méthode).
Pour déplacer une entité d'un état à l'autre, vous pouvez utiliser les méthodes persist
, remove
ou merge
.
Le problème que vous décrivez dans votre question:
object references an unsaved transient instance - save the transient instance before flushing
est causée par l'association d'une entité à l'état Nouveau à une entité à l'état Géré .
Cela peut se produire lorsque vous associez une entité enfant à une collection un à plusieurs dans l'entité parent et que la collection ne cascade
les transitions d'état d'entité.
Ainsi, comme je l'ai expliqué dans cet article , vous pouvez résoudre ce problème en ajoutant une cascade à l'association d'entité qui a déclenché cet échec, comme suit:
@OneToOne
association@OneToOne(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private PostDetails details;
Notez la
CascadeType.ALL
valeur que nous avons ajoutée pour l'cascade
attribut.
@OneToMany
association@OneToMany(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
Encore une fois, le CascadeType.ALL
convient aux associations bidirectionnelles@OneToMany
.
Maintenant, pour que la cascade fonctionne correctement dans un sens bidirectionnel, vous devez également vous assurer que les associations parent et enfant sont synchronisées.
Consultez cet article pour plus de détails sur la meilleure façon d'atteindre cet objectif.
@ManyToMany
association@ManyToMany(
mappedBy = "authors",
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}
)
private List<Book> books = new ArrayList<>();
Dans une @ManyToMany
association, vous ne pouvez pas utiliser CascadeType.ALL
ouorphanRemoval
car cela propagera la transition d'état de suppression de l'entité d'un parent à une autre entité parent.
Par conséquent, pour les @ManyToMany
associations, vous mettez généralement en cascade les opérations CascadeType.PERSIST
or CascadeType.MERGE
. Vous pouvez également l'étendre à DETACH
ou REFRESH
.
Pour plus de détails sur la meilleure façon de mapper une
@ManyToMany
association, consultez également cet article .
Ou, si vous voulez utiliser un minimum de "pouvoirs" (par exemple si vous ne voulez pas une suppression en cascade) pour obtenir ce que vous voulez, utilisez
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
...
@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
@ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})
(tout sauf REMOVE) ne met pas en cascade les mises à jour comme le fait Hibernate CascadeType.SAVE_UPDATE
.
Dans mon cas, cela a été causé par le fait de ne pas être CascadeType
du @ManyToOne
côté de la relation bidirectionnelle. Pour être plus précis, je l'avais CascadeType.ALL
sur le @OneToMany
côté et je ne l'avais pas @ManyToOne
. Ajout CascadeType.ALL
pour @ManyToOne
résoudre le problème.
Côté un à plusieurs :
@OneToMany(cascade = CascadeType.ALL, mappedBy="globalConfig", orphanRemoval = true)
private Set<GlobalConfigScope>gcScopeSet;
Côté plusieurs-à-un (a causé le problème)
@ManyToOne
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Plusieurs à un (corrigé en ajoutant CascadeType.PERSIST
)
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Cela s'est produit pour moi lors de la persistance d'une entité dans laquelle l'enregistrement existant dans la base de données avait une valeur NULL pour le champ annoté avec @Version (pour un verrouillage optimiste). La mise à jour de la valeur NULL à 0 dans la base de données a corrigé cela.
Ce n'est pas la seule raison de l'erreur. Je l'ai rencontré tout à l'heure pour une erreur de frappe dans mon codage, qui, je crois, a défini une valeur d'une entité qui était déjà enregistrée.
X x2 = new X();
x.setXid(memberid); // Error happened here - x was a previous global entity I created earlier
Y.setX(x2);
J'ai repéré l'erreur en trouvant exactement quelle variable a causé l'erreur (dans ce cas String xid
). J'ai utilisé un catch
autour du bloc de code entier qui a sauvé l'entité et imprimé les traces.
{
code block that performed the operation
} catch (Exception e) {
e.printStackTrace(); // put a break-point here and inspect the 'e'
return ERROR;
}
N'utilisez pas Cascade.All
avant d'avoir vraiment besoin. Role
et Permission
ont une manyToMany
relation bidirectionnelle . Ensuite, le code suivant fonctionnerait 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);
tandis que si l'objet est juste un "nouveau", alors il lancerait la même erreur.
Si votre collection est nullable, essayez simplement: object.SetYouColection(null);
Pour ajouter mes 2 cents, j'ai eu ce même problème lorsque j'envoyais accidentellement null
comme ID. Le code ci-dessous décrit mon scénario (et OP n'a mentionné aucun scénario spécifique) .
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);
Ici, je mets l'ID de service existant sur une nouvelle instance d'employé sans obtenir d'abord l'entité de service, car je ne veux pas qu'une autre requête de sélection se déclenche.
Dans certains scénarios, deptId
PKID arrive commenull
de la méthode d'appel et la même erreur.
Alors, surveillez les null
valeurs pour PK ID
Ce problème m'est arrivé lorsque j'ai créé une nouvelle entité et une entité associée dans une méthode marquée comme @Transactional
, puis effectué une requête avant d'enregistrer. 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);
}
Pour corriger, j'ai effectué la requête avant de créer la nouvelle entité.
à côté de toutes les autres bonnes réponses, cela peut se produire si vous utilisez merge
pour conserver un objet et oubliez accidentellement d'utiliser la référence fusionnée de l'objet dans la classe parent. considérons l'exemple suivant
merge(A);
B.setA(A);
persist(B);
Dans ce cas, vous fusionnez A
mais oubliez d'utiliser l'objet fusionné de A
. pour résoudre le problème, vous devez réécrire le code comme ceci.
A=merge(A);//difference is here
B.setA(A);
persist(B);
J'ai également fait face à la même situation. En définissant l'annotation suivante au-dessus de la propriété, elle résout l'exception demandée.
L'exception que j'ai rencontrée.
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
Pour surmonter, l'annotation que j'ai utilisée.
@OneToMany(cascade = {CascadeType.ALL})
@Column(name = "ListOfCarsDrivenByDriver")
private List<Car_OneToMany> listOfCarsBeingDriven = new ArrayList<Car_OneToMany>();
Ce qui a poussé Hibernate à lever l'exception:
Cette exception est levée sur votre console car l'objet enfant que j'attache à l'objet parent n'est pas présent dans la base de données à ce moment.
En fournissant @OneToMany(cascade = {CascadeType.ALL})
, il indique à Hibernate de les enregistrer dans la base de données tout en enregistrant l'objet parent.
Par souci d'exhaustivité: A
org.hibernate.TransientPropertyValueException
avec message
object references an unsaved transient instance - save the transient instance before flushing
se produira également lorsque vous essayez de conserver / fusionner une entité avec une référence à une autre entité qui se trouve être détachée .
Une autre raison possible: dans mon cas, j'essayais de sauver l'enfant avant de sauver le parent, sur une toute nouvelle entité.
Le code était quelque chose comme ça dans un modèle User.java:
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();
La méthode setNewPassword () crée un enregistrement PasswordHistory et l'ajoute à la collection d'historique dans User. Étant donné que l'instruction create () n'avait pas encore été exécutée pour le parent, elle tentait de l'enregistrer dans une collection d'une entité qui n'avait pas encore été créée. Tout ce que j'avais à faire pour y remédier était de déplacer l'appel setNewPassword () après l'appel à create ().
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);
Il existe une autre possibilité qui peut provoquer cette erreur en veille prolongée. Vous pouvez définir une référence non enregistrée de votre objet A
sur une entité attachée B
et vouloir conserver un objet C
. Même dans ce cas, vous obtiendrez l'erreur susmentionnée.
Je pense que c'est parce que vous avez essayé de faire persister un objet qui a une référence à un autre objet qui n'est pas encore persistant, et donc il essaie du "côté DB" de mettre une référence à une ligne qui n'existe pas
Un moyen simple de résoudre ce problème consiste à enregistrer les deux entités. enregistrez d'abord l'entité enfant, puis enregistrez l'entité parent. Parce que l'entité parent dépend de l'entité enfant pour la valeur de clé étrangère.
Ci-dessous un simple examen d'une relation un à un
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);
Une cause possible de l'erreur est l'inexistence du réglage de la valeur de l'entité parent; Par exemple, pour une relation département-employés, vous devez écrire ceci afin de corriger l'erreur:
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);
Il y a tellement de possibilités de cette erreur, d'autres possibilités sont également sur la page d'ajout ou la page d'édition. Dans mon cas, j'essayais d'enregistrer un objet AdvanceSalary. Le problème est que lors de la modification, AdvanceSalary employee.employee_id est null car lors de la modification, je n'ai pas défini l'employé.employee_id. J'ai créé un champ caché et je l'ai défini. mon code fonctionne très 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;
}
}
J'ai fait face à cette exception lorsque je n'ai pas persisté d'objet parent mais que je sauvais l'enfant. Pour résoudre le problème, avec dans la même session, j'ai persisté les objets enfant et parent et utilisé CascadeType.ALL sur le parent.
Cas 1: J'obtenais cette exception lorsque j'essayais de créer un parent et d'enregistrer cette référence parent à son enfant, puis une autre requête DELETE / UPDATE (JPQL). Je viens donc de vider () l'entité nouvellement créée après avoir créé un parent et après avoir créé un enfant en utilisant la même référence parent. Cela a fonctionné pour moi.
Cas 2:
Classe parent
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 enfant:
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;
.
.
}
Dans le cas ci-dessus, où le parent (Reference) et l'enfant (ReferenceAdditionalDetails) ont une relation OneToOne et lorsque vous essayez de créer une entité Reference, puis son enfant (ReferenceAdditionalDetails), cela vous donnera la même exception. Donc, pour éviter l'exception, vous devez définir null pour la classe enfant, puis créer le parent. (Exemple de code)
.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.
Mon problème était lié à @BeforeEach
JUnit. Et même si j'ai enregistré les entités liées (dans mon cas @ManyToOne
), j'ai eu la même erreur.
Le problème est en quelque sorte lié à la séquence que j'ai chez mes parents. Si j'attribue la valeur à cet attribut, le problème est résolu.
Ex. Si j'ai la question d'entité qui peut avoir certaines catégories (une ou plusieurs) et la question d'entité a une séquence:
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
@Id
private Long id;
Je dois assigner la valeur question.setId(1L);
Créez simplement Constructeur de votre mapping dans votre classe de base. Comme si vous voulez une relation un à un dans l'entité A, l'entité B. si vous prenez A comme classe de base, alors A doit avoir un constructeur avec B comme argument.