webentwicklung-frage-antwort-db.com.de

Daten wurden nicht gespeichert: Objekt verweist auf eine nicht gespeicherte transiente Instanz - Speichern Sie die transiente Instanz vor dem Leeren

Ich habe eine Datenbank mit zwei Tabellen User und Country. Ich möchte eine Beziehung herstellen, in der viele Benutzer zu einem Landkreis gehören können. Ich implementiere dies mit Hibernate unter Verwendung der folgenden Modellklassen: 

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

Wenn ich versuche, das Benutzerobjekt zu speichern, erhalte ich die folgende Ausnahme:

  org.hibernate.HibernateException: Data was not saved: object references an unsaved transient instance - save the transient instance before flushing: com.kyrogaming.models.Country

Wie kann ich das Problem beheben?

15
Mario Dennis

Sie können keine Daten für den Ruhezustand speichern, bis Sie auch über alle anderen Objekte, auf die das neu gespeicherte Objekt verweist, Hibernate mitgeteilt haben. In diesem Fall erzählen Sie Hibernate von einer User, haben es aber noch nicht von Country erzählt.

Sie können Probleme auf zwei Arten lösen. 

Manuell

Rufen Sie session.save(country) auf, bevor Sie die User speichern.

CascadeType

Sie können im Ruhezustand angeben, dass diese Beziehung einige Operationen mithilfe von CascadeType ausbreitet. In diesem Fall würde CascadeType.PERSIST ebenso wie CascadeType.ALL die Arbeit erledigen. 

Verweis auf bestehende Länder

Basierend auf Ihrer Antwort auf @zerocool haben Sie jedoch ein zweites Problem: Wenn Sie zwei User-Objekte mit derselben Country haben, stellen Sie nicht sicher, dass es sich um sameCountry handelt. Dazu müssen Sie die entsprechende Country aus der Datenbank abrufen, den neuen Benutzer festlegen und dann die User speichern. Dann beziehen sich beide User-Objekte auf die gleiche Country, nicht nur auf zwei Country-Instanzen, die zufällig denselben Namen haben. Überprüfen Sie die Criteria-API als eine Möglichkeit, vorhandene Instanzen abzurufen.

24
sharakan

Sieht so aus, als ob Benutzer, die in Ihrem Country-Objekt hinzugefügt wurden, noch nicht in der Datenbank vorhanden sind. Sie müssen kaskadieren, um sicherzustellen, dass bei einem permanenten Land alle Benutzer, die nicht in den Daten vorhanden sind, aber dem Land zugeordnet sind, auch persistent bleiben.

Der folgende Code sollte helfen:

@ManyToOne (cascade = CascadeType.ALL)
   @JoinColumn (name = "countryId")
   private Country country;
12
zerocool

Ich hatte das gleiche Problem. In meinem Fall ist dies der Fall, weil die Suchtabelle "country" einen vorhandenen Datensatz mit countryId == 0 und einen primitiven Primärschlüssel enthält und ich versuche, einen Benutzer mit einer countryID == 0 zu speichern. Ändern Sie den Primärschlüssel des Landes in Ganzzahl. Der Ruhezustand kann jetzt neue Datensätze identifizieren. 

Für die Empfehlung, Wrapper-Klassen als Primärschlüssel zu verwenden, siehe diese Stackoverflow-Frage

3
Frank Rünagel

Um meine 2 Cent hinzuzufügen, habe ich dasselbe Problem, wenn ich versehentlich null als ID sende. Der folgende Code zeigt mein Szenario (und OP hat kein bestimmtes Szenario erwähnt)}.

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

Hier setze ich die vorhandene Abteilungs-ID auf eine neue Mitarbeiterinstanz, ohne zuerst die Abteilungsentität zu erhalten, da ich nicht möchte, dass eine andere Auswahlabfrage ausgelöst wird.

In einigen Szenarien kommt deptId PKID als null von der aufrufenden Methode und ich bekomme den gleichen Fehler. 

Achten Sie also auf null-Werte für PK ID.

Gleiche Antwort hier

2
manikanta

Es ist wegen CASCADE TYPE

wenn Sie setzen  

@OneToOne (cascade = CascadeType.ALL)

Sie können Ihr Objekt einfach so speichern

user.setCountry(country);
session.save(user)

aber wenn Sie setzen

 @OneToOne(cascade={    
            CascadeType.PERSIST,
            CascadeType.REFRESH,
            ...
})

Sie müssen Ihr Objekt so speichern

user.setCountry(country);
session.save(country)
session.save(user)
0
Pen Lymeng

Na wenn du gegeben hast 

@ManyToOne ()
   @JoinColumn (name = "countryId")
   private Country country;

dann Objekt dieser Klasse, ich meine, Land muss zuerst gesichert werden. 

der Benutzer kann nur dann in der Datenbank gespeichert werden, wenn für dieses Land ein Schlüssel für diesen Benutzer verfügbar ist. bedeutet, dass Benutzer nur dann gespeichert werden können, wenn dieses Land in der Tabelle Land vorhanden ist.

Dafür müssen Sie dieses Land zuerst in der Tabelle speichern. 

0
Krishna

Es sollte CascadeType.Merge sein. In diesem Fall wird es aktualisiert, wenn der Datensatz bereits vorhanden ist.

0
user3571418