"Getrennte Entität übergeben, um Fehler beizubehalten" mit JPA / EJB-Code

81

Ich versuche, diesen grundlegenden JPA / EJB-Code auszuführen:

public static void main(String[] args){
         UserBean user = new UserBean();
         user.setId(1);
         user.setUserName("name1");
         user.setPassword("passwd1");
         em.persist(user);
  }

Ich erhalte diesen Fehler:

javax.ejb.EJBException: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.JPA.Database

Irgendwelche Ideen?

Ich suche im Internet und der Grund, den ich gefunden habe, war:

Dies wurde dadurch verursacht, wie Sie die Objekte erstellt haben, dh wenn Sie die ID-Eigenschaft explizit festgelegt haben. Das Entfernen der ID-Zuweisung hat das Problem behoben.

Aber ich habe es nicht verstanden. Was muss ich ändern, damit der Code funktioniert?

zengr
quelle

Antworten:

49

ERD

Angenommen, Sie haben zwei Entitäten Albumund Photo. Das Album enthält viele Fotos, es ist also eine Eins-zu-Viele-Beziehung.

Albumklasse

@Entity
public class Album {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    Integer albumId;

    String albumName;

    @OneToMany(targetEntity=Photo.class,mappedBy="album",cascade={CascadeType.ALL},orphanRemoval=true)
    Set<Photo> photos = new HashSet<Photo>();
}

Fotoklasse

@Entity
public class Photo{
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    Integer photo_id;

    String photoName;

    @ManyToOne(targetEntity=Album.class)
    @JoinColumn(name="album_id")
    Album album;

}

Bevor Sie fortfahren oder zusammenführen, müssen Sie die Albumreferenz in jedem Foto festlegen.

        Album myAlbum = new Album();
        Photo photo1 = new Photo();
        Photo photo2 = new Photo();

        photo1.setAlbum(myAlbum);
        photo2.setAlbum(myAlbum);       

Auf diese Weise können Sie die zugehörige Entität anhängen, bevor Sie fortfahren oder zusammenführen.

zawhtut
quelle
2
Wenn Sie Generika verwenden, müssen Sie "targetEntity = Photo.class"
Rollerball
Hallo, nachdem wir das Albumobjekt auf Foto1 und Foto2 gesetzt haben ... welches Objekt müssen wir speichern, um die Liste der Fotos oder des Albums zu speichern?
Dilanka Rathnayake
129

Der Fehler tritt auf, weil die ID des Objekts festgelegt ist. Der Ruhezustand unterscheidet zwischen vorübergehenden und getrennten Objekten und persistfunktioniert nur mit vorübergehenden Objekten. Wenn der persistSchluss gezogen wird, dass das Objekt getrennt wurde (was der Fall ist, weil die ID festgelegt ist), wird der Fehler "Zum Fortbestehen übergebenes getrenntes Objekt" zurückgegeben. Weitere Details finden Sie hier und hier .

Dies gilt jedoch nur, wenn Sie den automatisch zu generierenden Primärschlüssel angegeben haben: Wenn das Feld so konfiguriert ist, dass es immer manuell festgelegt wird, funktioniert Ihr Code.

Tomislav Nakic-Alfirevic
quelle
Werde ich technisch richtig sein, wenn ich sage, ich verwende JPA und nicht Hibernate, so dass die obige Aussage nicht richtig gelten sollte? Ich bin Neuling in JPA (3 Jahre um genau zu sein: P)
Zengr
Suns JPA Javadoc ( java.sun.com/javaee/5/docs/api/javax/persistence/… ) und das von Toplink sind genau gleich und legen nahe, dass Sie technisch richtig sind. Es läuft jedoch wirklich darauf hinaus, wie sich in der Spezifikation persist () verhalten soll, und leider weiß ich nicht, was das ist.
Tomislav Nakic-Alfirevic
Diese Lösung hat bei mir funktioniert. Ich habe ein Objekt beibehalten und die ID irgendwo gespeichert. Ich möchte dann das vorhandene Objekt mit einem neuen Wert überschreiben, also habe ich das Objekt erstellt, die ID wieder kopiert und es explodierte, als ich versuchte zu speichern. Am Ende musste ich die Datenbank (findById) anfordern, die Änderungen vornehmen und dieses Objekt dann beibehalten .
cs94njw
24

entfernen

user.setId(1);

weil es automatisch in der Datenbank generiert wird, und fahren Sie mit dem Befehl persist fort.

Ammar Bozorgvar
quelle
12

Ich habe die Antwort bekommen, die ich verwendet habe:

em.persist(user);

Ich habe Merge anstelle von Persist verwendet:

em.merge(user);

Aber keine Ahnung, warum persist nicht funktioniert hat. :(

zengr
quelle
11
Das ist nicht die Lösung!
Ammar Bozorgvar
1
Ich weiß, aber das hat bei mir funktioniert. Gibt es hier eine andere Antwort, die für Sie funktioniert hat? Wenn ja, dann werde ich es gerne auswählen.
Zengr
4
Es hat funktioniert, weil Ihr Objekt von der Sitzung im Ruhezustand getrennt wurde oder das Objekt vorübergehend ist, der Ruhezustand es jedoch als getrennt ansieht, weil Sie den Primärschlüssel deklariert haben. Wenn Sie das Objekt mithilfe der Zusammenführung erneut an die Sitzung anhängen, können Sie Objekte in Ihrer Datenbank aktualisieren ;; siehe: docs.jboss.org/hibernate/core/3.3/reference/en/html/…
Ben
9

Wenn Sie die id = GenerationType.AUTOStrategie in Ihrer Entität generieren .

Ersetzt user.setId (1)durch user.setId (null)und das Problem ist gelöst.

Sergio Ordóñez
quelle
5

Ich weiß, dass es zu spät ist und dass jeder die Antwort bekommen hat. Aber noch ein bisschen mehr: Wenn GenerateType festgelegt ist, wird erwartet, dass persist () für ein Objekt eine ID generiert.

Wenn der Benutzer bereits einen Wert für die ID festgelegt hat, wird dieser im Ruhezustand als gespeicherter Datensatz behandelt und daher als getrennt behandelt.

Wenn die ID null ist, wird in dieser Situation eine Nullzeigerausnahme ausgelöst, wenn der Typ AUTO oder IDENTITY usw. ist, es sei denn, die ID wird aus einer Tabelle oder einer Sequenz usw. generiert.

Design: Dies geschieht, wenn die Tabelle eine Bean-Eigenschaft als Primärschlüssel hat. GenerateType darf nur festgelegt werden, wenn eine ID automatisch generiert wird. Wenn Sie dies entfernen, sollte die Einfügung mit der vom Benutzer angegebenen ID funktionieren. (Es ist ein schlechtes Design, eine Eigenschaft dem Primärschlüsselfeld zuzuordnen.)

Haripriya
quelle
5

Hier wird nur .persist () den Datensatz einfügen. Wenn wir .merge () verwenden , wird überprüft, ob ein Datensatz mit der aktuellen ID vorhanden ist. Wenn er vorhanden ist, wird er aktualisiert, andernfalls wird ein neuer Datensatz eingefügt .

PSR
quelle
Es hat mich eine Menge Zeit gekostet, bis ich deine Antwort bekam. Vielen Dank!
Thach Van
3

Wenn Sie in Ihrer Datenbank die ID als Primärschlüssel und automatische Inkrementierung festlegen, ist diese Codezeile falsch:

user.setId(1);

Versuchen Sie es damit:

public static void main(String[] args){
         UserBean user = new UserBean();
         user.setUserName("name1");
         user.setPassword("passwd1");
         em.persist(user);
  }
Nemus
quelle
Ich habe das versucht und ich weiß, dass ich diesen Fehler detached entity passed to persist
bekomme
2

Ich hatte dieses Problem und es wurde durch den Cache der zweiten Ebene verursacht:

  1. Ich habe eine Entität im Ruhezustand beibehalten
  2. Dann habe ich die Zeile gelöscht, die aus einem separaten Prozess erstellt wurde, der nicht mit dem Cache der zweiten Ebene interagierte
  3. Ich habe eine andere Entität mit demselben Bezeichner beibehalten (meine Bezeichnerwerte werden nicht automatisch generiert).

Da der Cache nicht ungültig war, ging der Ruhezustand davon aus, dass es sich um eine getrennte Instanz derselben Entität handelte.

Hertzsprung
quelle