J'ai une base de données avec deux tables utilisateur et pays. Je veux une relation où de nombreux utilisateurs peuvent appartenir à un comté. Je l'implémente en utilisant hibernate en utilisant les classes de modèle suivantes:
@Entity (name = "user")
public class User {
@Id @GeneratedValue (strategy = GenerationType.IDENTITY)
private int userId;
private String username;
private String password;
@ManyToOne ()
@JoinColumn (name = "countryId")
private Country country;
//Getter & Setter
}
@Entity (name = "country")
public class Country {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private int countryId;
private String countryName;
//Getter & Setter
}
Lorsque j'essaie de sauvegarder l'objet utilisateur, j'obtiens l'exception suivante:
org.hibernate.HibernateException: Data was not saved: object references an unsaved transient instance - save the transient instance before flushing: com.kyrogaming.models.Country
Comment puis-je résoudre le problème?
Vous ne pouvez pas enregistrer des choses dans Hibernate avant d’avoir également informé Hibernate de tous les autres objets référencés par cet objet récemment enregistré. Donc, dans ce cas, vous parlez à Hibernate d’une User
, mais vous ne l’avez pas dit à propos de la Country
.
Vous pouvez résoudre des problèmes comme celui-ci de deux manières.
Manuellement
Appelez session.save(country)
avant de sauvegarder la User
.
CascadeType
Vous pouvez spécifier à Hibernate que cette relation doit propager certaines opérations à l'aide de CascadeType . Dans ce cas, CascadeType.PERSIST fera le travail, de même que CascadeType.ALL.
Référence aux pays existants
Si vous répondez à @zerocool, vous rencontrez un deuxième problème: lorsque vous avez deux objets User
avec le même Country
, vous ne vous assurez pas qu'il s'agit bien du mêmeCountry
. Pour ce faire, vous devez extraire la Country
appropriée de la base de données, la définir sur le nouvel utilisateur, puis enregistrer la User
. Ensuite, vos deux objets User
feront référence à la même Country
, et pas seulement à deux instances Country
ayant le même nom. Consultez l’API Criteria
comme un moyen de récupérer des instances existantes.
Il semble que les utilisateurs ajoutés à votre objet Pays ne soient pas déjà présents dans la base de données. Vous devez utiliser cascade pour vous assurer que lorsque Pays est conservé, tous les utilisateurs qui ne figurent pas dans les données mais qui sont associés à Pays le sont également.
Le code ci-dessous devrait aider:
@ManyToOne (cascade = CascadeType.ALL)
@JoinColumn (name = "countryId")
private Country country;
J'ai eu le même problème. Dans mon cas, cela est dû au fait que la table de recherche "pays" a un enregistrement existant avec countryId == 0 et une clé primaire primitive et que j'essaie de sauvegarder un utilisateur avec un countryID == 0. Remplacez la clé primaire du pays par Integer. Hibernate peut maintenant identifier de nouveaux enregistrements.
Pour la recommandation d'utiliser des classes wrapper comme clé primaire, voir cette question de stackoverflow
Pour ajouter mes 2 cents, j’ai eu le même problème lorsque j’ai accidentellement envoyé null
comme ID. Le code ci-dessous décrit mon scénario (et de toute façon, 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’identifiant de service existant sur une nouvelle instance d’employé sans obtenir d’abord l’entité de service, car je ne souhaite pas qu’une autre requête de sélection soit déclenchée.
Dans certains scénarios, deptId
PKID vient sous la forme null
à partir de la méthode d'appel et j'obtiens la même erreur.
Donc, surveillez les valeurs null
pour l'ID de la PK
_ {Même réponse donnée ici
C’est à cause de TYPE DE CASCADE
si vous mettez
@OneToOne (cascade = CascadeType.ALL)
Vous pouvez simplement sauvegarder votre objet comme ceci
user.setCountry(country);
session.save(user)
mais si vous mettez
@OneToOne(cascade={
CascadeType.PERSIST,
CascadeType.REFRESH,
...
})
Vous devez enregistrer votre objet comme ceci
user.setCountry(country);
session.save(country)
session.save(user)
Eh bien, si vous avez donné
@ManyToOne ()
@JoinColumn (name = "countryId")
private Country country;
alors objet de cette classe je veux dire que le pays doit être sauvegardé en premier.
car cela ne permettra à l'utilisateur d'être enregistré dans la base de données que s'il existe une clé disponible pour le pays de cet utilisateur. signifie que cela permettra à l'utilisateur d'être sauvegardé si et seulement si ce pays existe dans la table Pays.
Pour cela, vous devez d'abord enregistrer ce pays dans le tableau.
Il devrait s'agir de CascadeType.Merge. Dans ce cas, il sera mis à jour si l'enregistrement existe déjà.