«Отдельный объект передан для сохранения ошибки» с кодом 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. Удаление присвоения ID исправило это.

Но я не понял, что мне нужно будет изменить, чтобы код заработал?

Zengr
источник

Ответы:

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
привет, после установки объекта альбома на фото1 и фото2 ... какой объект мы должны сохранить список фотографий или альбом?
Диланка Ратнаяке
130

Ошибка возникает из-за того, что установлен ID объекта. Hibernate различает переходные и отсоединенные объекты и persistработает только с переходными объектами. Если persistзавершится, что объект отсоединен (что произойдет, поскольку идентификатор установлен), он вернет ошибку «Отсоединенный объект передан для сохранения». Вы можете найти более подробную информацию здесь и здесь .

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

Томислав Накич-Альфиревич
источник
Буду ли я технически прав, когда скажу, что я использую JPA, а не Hibernate, поэтому приведенное выше утверждение не должно применяться правильно? Я новичок в JPA (3 ays, чтобы быть точным: P)
zengr
Документы Sun JPA Javadoc ( java.sun.com/javaee/5/docs/api/javax/persistence/… ) и Toplink полностью идентичны и предполагают, что вы технически правы. Однако на самом деле все сводится к тому, как в спецификации сказано, что persist () должна вести себя, и, к сожалению, я не знаю, что это такое.
Tomislav Nakic-Alfirevic
Это решение сработало для меня. Я сохранял объект, сохраняя где-то идентификатор. Затем я хочу перезаписать этот существующий объект новым значением, поэтому я создал объект, скопировал идентификатор обратно, и он взорвался, когда я попытался сохранить. В конце концов, мне пришлось повторно запросить БД (findById), внести изменения и затем сохранить этот объект.
cs94njw
24

удалять

user.setId(1);

потому что он автоматически создается в БД и продолжает команду persist.

Аммар Бозоргвар
источник
12

Получил ответ, использовал:

em.persist(user);

Я использовал слияние вместо сохранения:

em.merge(user);

Но не знаю, почему настаивать не сработало. :(

Zengr
источник
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 () для объекта получит сгенерированный идентификатор.

Если для идентификатора уже установлено значение идентификатора пользователем, спящий режим рассматривает его как сохраненную запись и поэтому считается отсоединенным.

если идентификатор равен нулю - в этой ситуации возникает исключение нулевого указателя, если типом является AUTO или IDENTITY и т. д., если идентификатор не сгенерирован из таблицы или последовательности и т. д.

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

хариприя
источник
5

Здесь .persist () будет вставлять только запись. Если мы используем .merge (), он проверит, существует ли какая-либо запись с текущим идентификатором, если она существует, она обновится, иначе она вставит новую запись.

PSR
источник
Я потратил кучу времени, прежде чем я получил твой ответ. Большое спасибо!
Thach Van
3

Если вы установите id в своей базе данных как первичный ключ и автоинкремент, то эта строка кода неверна:

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. Я сохранил другую сущность с тем же идентификатором (значения моих идентификаторов не создаются автоматически)

Следовательно, поскольку кеш не был признан недействительным, спящий режим предполагал, что имеет дело с отдельным экземпляром той же сущности.

Герцшпрунг
источник