“Від’єднаний об’єкт передано на постійну помилку” з кодом JPA / EJB


81

Я намагаюся запустити цей базовий код JPA / EJB:

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

Я отримую цю помилку:

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

Будь-які ідеї?

Я шукаю в Інтернеті, і причиною, яку я знайшов, було:

Це було викликано тим, як ви створили об'єкти, тобто якщо ви явно встановили властивість ID. Видалення призначення ідентифікатора це виправило.

Але я не зрозумів, що мені доведеться змінити, щоб код працював?

Відповіді:


50

ERD

Скажімо, у вас є дві сутності Albumі Photo. Альбом містить багато фотографій, тому це стосунки один до багатьох.

Клас альбому

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

Фото клас

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

    String photoName;

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

}

Перш ніж зберегти або об’єднати, потрібно встановити посилання на альбом на кожній фотографії.

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

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

Ось як приєднати пов’язану сутність перед тим, як зберегти чи об’єднати.


2
Якщо ви використовуєте дженерики, немає необхідності використовувати "targetEntity = Photo.class"
Rollerball

привіт, після встановлення об’єкта альбому на photo1 і photo2 ... який об’єкт ми маємо зберегти у списку фотографій або альбому?
Dilanka Rathnayake

130

Помилка виникає, оскільки встановлено ідентифікатор об’єкта. Hibernate розрізняє перехідні та відокремлені об’єкти і persistпрацює лише з перехідними об’єктами. Якщо persistдійде висновку, що об’єкт від’єднаний (що буде, оскільки встановлено ідентифікатор), він поверне помилку "від’єднаний об’єкт, переданий на збереження". Детальніше ви можете знайти тут і тут .

Однак це стосується лише того випадку, якщо ви вказали первинний ключ, який буде автоматично згенерований: якщо поле налаштовано завжди встановлювати вручну, тоді ваш код працює.


Чи буду я технічно правий, коли кажу, що я використовую JPA, а не сплячий режим, так що вищезазначене твердження не повинно застосовуватися правильно? Я новачок у JPA (3 ays, якщо бути точним: P)
zengr

JDP Javadoc від Sun ( java.sun.com/javaee/5/docs/api/javax/persistence/… ) та Toplink абсолютно однакові, і припускають, що ви технічно праві. Однак це справді зводиться до того, що в специфікації сказано, що persist () повинен поводитись і, на жаль, я не знаю, що це таке.
Томіслав Накіч-Альфіревіч

Це рішення спрацювало для мене. Я зберігав об’єкт, зберігаючи десь збережений ідентифікатор. Потім я хочу замінити цей існуючий об’єкт новим значенням, тому я створив об’єкт, скопіював ідентифікатор назад, і він підірвався, коли я намагався зберегти. Врешті-решт, мені довелося запитувати БД (findById), вносити зміни, а потім зберігати цей об’єкт.
cs94njw


12

Я отримав відповідь, я використовував:

em.persist(user);

Я використовував злиття замість persist:

em.merge(user);

Але не маю ідеї, чому наполегливість не спрацювала. :(


11
це не рішення!
Аммар Бозоргвар,

1
я знаю, але це в мене спрацювало. будь-яка інша відповідь тут, яка вам підійшла? якщо так, то я буду із задоволенням його вибрати.
zengr

4
Це спрацювало, оскільки ваш об'єкт був від'єднаний від сеансу сплячого режиму або об'єкт перехідний, але режим глибокого сну сприймає його як від'єднаний, тому що у вас оголошено первинний ключ. За допомогою злиття ви знову приєднуєте об'єкт до сеансу, що дозволяє оновити об'єкти у вашій базі даних ; див .: docs.jboss.org/hibernate/core/3.3/reference/en/html/…
Бен,

11

якщо ви використовуєте для створення id = GenerationType.AUTO стратегії у своїй сутності.

Замінює user.setId (1)шляхом user.setId (null), і проблема вирішена.


5

Я знаю, що це занадто пізно, і кожна людина отримала відповідь. Але трохи більше, щоб додати до цього: коли встановлено GenerateType, persist () на об'єкті, як очікується, отримає ідентифікатор.

Якщо для користувача вже встановлено значення Id, режим глибокого сну розглядає його як збережений запис, і тому він розглядається як відокремлений.

якщо ідентифікатор нульовий - у цій ситуації виникає виняток нульового покажчика, коли тип АВТОМАТИЧНИЙ або ІДЕНТИЧНИЙ тощо, якщо тільки ідентифікатор не генерується з таблиці або послідовності тощо.

дизайн: це трапляється, коли таблиця має властивість bean як первинний ключ. GenerateType потрібно встановлювати лише тоді, коли ідентифікатор автоматично генерується. видаліть це, і вставка повинна працювати з вказаним користувачем ідентифікатором. (це погана конструкція, якщо властивість відображається у полі первинного ключа)


5

Тут лише .persist () вставить запис. Якщо ми використовуємо .merge (), він перевірить, чи існує якийсь запис із поточним ідентифікатором. Якщо він існує, він оновиться, інакше вставить новий запис.


Це коштувало мені купу часу, поки я не отримав вашу відповідь. Дуже дякую!
Thach Van,

3

Якщо ви встановили ідентифікатор у вашій базі даних як первинний ключ та автоінкремент, тоді цей рядок коду є неправильним:

user.setId(1);

Спробуйте з цим:

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

Я спробував це і знаю, що отримав цю помилку:detached entity passed to persist
s1ddok

2

У мене була ця проблема, і вона була спричинена кешем другого рівня:

  1. Я наполягав на організації, використовуючи сплячий режим
  2. Потім я видалив рядок, створений з окремого процесу, який не взаємодіяв з кешем другого рівня
  3. Я зберігав іншу сутність з тим самим ідентифікатором (мої значення ідентифікатора не генеруються автоматично)

Отже, оскільки кеш не був визнаний недійсним, режим глибокого сну припустив, що він мав справу з відокремленим екземпляром тієї самої сутності.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.