„Odłączona jednostka przekazana w celu utrwalenia błędu” z kodem JPA / EJB

81

Próbuję uruchomić ten podstawowy kod JPA / EJB:

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

Otrzymuję ten błąd:

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

Jakieś pomysły?

Wyszukuję w internecie i znalazłem powód:

Było to spowodowane sposobem tworzenia obiektów, tj. Jawnym ustawieniem właściwości ID. Usunięcie przypisania ID naprawiło to.

Ale nie rozumiem, co będę musiał zmodyfikować, aby kod działał?

zengr
źródło

Odpowiedzi:

50

ERD

Powiedzmy, że masz dwie jednostki Albumi Photo. Album zawiera wiele zdjęć, więc jest to relacja jeden do wielu.

Klasa albumu

@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>();
}

Klasa fotograficzna

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

    String photoName;

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

}

Przed utrwaleniem lub scaleniem należy ustawić odniesienie do albumu na każdym zdjęciu.

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

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

W ten sposób należy dołączyć powiązaną jednostkę przed utrwaleniem lub scaleniem.

zawhtut
źródło
2
Jeśli używasz leków ogólnych, nie ma potrzeby używania „targetEntity = Photo.class”
Rollerball
cześć, po ustawieniu obiektu albumu na foto1 i foto2 ... jaki obiekt mamy zapisać listę zdjęć lub albumu?
Dilanka Rathnayake
130

Błąd występuje, ponieważ ustawiono identyfikator obiektu. Hibernate rozróżnia obiekty przejściowe i odłączone i persistdziała tylko z obiektami przejściowymi. Jeśli persistdojdzie do wniosku, że obiekt jest odłączony (co zrobi, ponieważ identyfikator jest ustawiony), zwróci błąd „odłączony obiekt przekazany do utrwalenia”. Więcej szczegółów znajdziesz tutaj i tutaj .

Ma to jednak zastosowanie tylko wtedy , gdy określono, że klucz podstawowy ma być generowany automatycznie: jeśli pole jest skonfigurowane tak, aby zawsze było ustawiane ręcznie, kod działa.

Tomislav Nakic-Alfirevic
źródło
Czy technicznie będę miał rację, gdy powiem, że używam JPA, a nie Hibernacji, więc powyższe stwierdzenie nie powinno mieć zastosowania? Jestem nowicjuszem w JPA (dokładnie 3
ays
JPA Javadoc firmy Sun ( java.sun.com/javaee/5/docs/api/javax/persistence/ ) i Toplink są dokładnie takie same i sugerują, że masz rację techniczną. Jednak tak naprawdę sprowadza się to do tego, co specyfikacja mówi, że persist () powinno się zachowywać i niestety, nie wiem, co to jest.
Tomislav Nakic-Alfirevic
To rozwiązanie zadziałało dla mnie. Utrwalałem obiekt, przechowując gdzieś identyfikator. Następnie chcę nadpisać ten istniejący obiekt nową wartością, więc utworzyłem obiekt, skopiowałem identyfikator z powrotem i wybuchł, gdy próbowałem zapisać. W końcu musiałem ponownie sprawdzić bazę danych (findById), wprowadzić zmiany, a następnie utrwalić ten obiekt.
cs94njw
24

usunąć

user.setId(1);

ponieważ jest generowany automatycznie w bazie danych i kontynuuj z poleceniem persist.

Ammar Bozorgvar
źródło
12

Otrzymałem odpowiedź, użyłem:

em.persist(user);

Użyłem scalania zamiast trwałego:

em.merge(user);

Ale nie mam pojęcia, dlaczego upór nie zadziałał. :(

zengr
źródło
11
to nie jest rozwiązanie!
Ammar Bozorgvar
1
wiem, ale to zadziałało dla mnie. jakaś inna odpowiedź, która zadziałała dla Ciebie? jeśli tak, to z przyjemnością go wybiorę.
zengr
4
Zadziałało, ponieważ obiekt został odłączony od sesji hibernacji lub obiekt jest przejściowy, ale hibernacja postrzega go jako odłączony, ponieważ masz zadeklarowany klucz podstawowy.Za pomocą funkcji łączenia ponownie dołączasz obiekt do sesji, co umożliwia aktualizację obiektów w bazie danych ; patrz: docs.jboss.org/hibernate/core/3.3/reference/en/html/…
Ben
11

jeśli używasz do generowania id = GenerationType.AUTOstrategii w swojej jednostce.

Zastępuje user.setId (1)przez user.setId (null)i problem został rozwiązany.

Sergio Ordóñez
źródło
5

Wiem, że jest za późno i każdy z nich dostał odpowiedź. Ale trochę więcej do dodania do tego: gdy jest ustawiona opcja GenerateType, oczekuje się, że persist () na obiekcie otrzyma wygenerowany identyfikator.

Jeśli istnieje już ustawiona wartość identyfikatora przez użytkownika, hibernacja traktuje go jako zapisany rekord, a więc jest traktowany jako odłączony.

jeśli id ​​jest null - w tej sytuacji wyjątek wskaźnika zerowego jest zgłaszany, gdy typ to AUTO lub IDENTITY itp., chyba że identyfikator jest generowany z tabeli lub sekwencji itp.

design: dzieje się tak, gdy tabela ma właściwość bean jako klucz podstawowy. GenerateType należy ustawić tylko wtedy, gdy identyfikator jest generowany automatycznie. usuń to, a insert powinien działać z identyfikatorem określonym przez użytkownika. (to zły projekt mieć właściwość mapowaną na pole klucza podstawowego)

haripriya
źródło
5

Tutaj tylko .persist () wstawi rekord. Jeśli użyjemy .merge () , sprawdzi, czy istnieje jakiś rekord z bieżącym identyfikatorem, jeśli istnieje, zaktualizuje się, w przeciwnym razie wstawi nowy rekord.

PSR
źródło
Kosztowało mnie to mnóstwo czasu, zanim otrzymałem twoją odpowiedź. Dziękuję Ci bardzo!
Thach Van
3

Jeśli ustawisz id w swojej bazie danych jako klucz podstawowy i autoincrement, to ten wiersz kodu jest nieprawidłowy:

user.setId(1);

Spróbuj z tym:

public static void main(String[] args){
         UserBean user = new UserBean();
         user.setUserName("name1");
         user.setPassword("passwd1");
         em.persist(user);
  }
Nemus
źródło
Próbowałem tego i wiem, że otrzymuję ten błąd:detached entity passed to persist
s1ddok
2

Miałem ten problem i był on spowodowany pamięcią podręczną drugiego poziomu:

  1. Utrwalałem byt używając hibernacji
  2. Następnie usunąłem wiersz utworzony z oddzielnego procesu, który nie współpracował z pamięcią podręczną drugiego poziomu
  3. Utrwaliłem inny podmiot z tym samym identyfikatorem (wartości moich identyfikatorów nie są generowane automatycznie)

Dlatego, ponieważ pamięć podręczna nie została unieważniona, hibernacja zakłada, że ​​ma do czynienia z odłączoną instancją tej samej jednostki.

hertzsprung
źródło