Данные не были сохранены: объект ссылается на несохраненный экземпляр переходного процесса - сохранить временный экземпляр перед промывкой

У меня есть база данных с двумя таблицами User и Country. Я хочу отношения, когда многие пользователи могут принадлежать одному графству. Я реализую это, используя спящий режим, используя следующие классы моделей:

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

Когда я пытаюсь сохранить объект пользователя, я получаю следующее исключение:

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

Как я могу исправить проблему?

Ответ 1

Вы не можете сохранить вещи в Hibernate, пока не сообщите Hibernate обо всех других объектах, на которые ссылается этот недавно сохраненный объект. Итак, в этом случае вы сообщаете Hibernate о User, но не сказали об этом Country.

Вы можете решить такие проблемы двумя способами.

Вручную

Вызовите session.save(country), прежде чем сохранять User.

CascadeType

Вы можете указать Hibernate, что это отношение должно распространять некоторые операции с помощью CascadeType. В этом случае CascadeType.PERSIST выполнит эту работу, как и CascadeType.ALL.

Ссылка на существующие страны

Однако, исходя из вашего ответа на @zerocool, у вас есть вторая проблема, которая заключается в том, что, когда у вас есть два объекта User с тем же Country, вы не уверены, что это тот же Country. Для этого вам нужно получить соответствующий Country из базы данных, установить его у нового пользователя, а затем сохранить User. Затем оба ваших объекта User будут ссылаться на те же Country, а не на два экземпляра Country, которые имеют одно и то же имя. Просмотрите Criteria API как один из способов извлечения существующих экземпляров.

Ответ 2

Похоже, что пользователи, добавленные в свой объект Country, еще не присутствуют в БД. Вы должны использовать каскад, чтобы убедиться, что, когда Страна сохраняется, все Пользователь, который не существует в данных, но связанных с Страной, также сохраняются.

Ниже приведен код:

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

Ответ 3

У меня была та же проблема. В моем случае это возникает, потому что таблица поиска "страна" имеет существующую запись с countryId == 0 и примитивным первичным ключом, и я пытаюсь сохранить пользователя с идентификатором страны == 0. Измените первичный ключ страны на Integer. Теперь Hibernate может идентифицировать новые записи.

Для рекомендации использования классов-оболочек в качестве первичного ключа см. этот вопрос о стековом потоке

Ответ 4

Чтобы добавить мои 2 цента, у меня возникла такая же проблема, когда я случайно отправил null в качестве идентификатора. Ниже кода изображен мой сценарий (и в любом случае OP не упоминает какой-либо конкретный сценарий).

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

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

В некоторых сценариях deptId PKID приходит как null из метода вызова, и я получаю ту же ошибку.

Итак, обратите внимание на значения null для PK ID

Тот же ответ, данный здесь

Ответ 5

Он должен быть CascadeType.Merge, в этом случае он будет обновляться, если запись уже существует.

Ответ 6

Хорошо, если вы дали

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

тогда объект этого класса я означает, что сначала необходимо сохранить страну.

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

Итак, для этого вам нужно сначала сохранить эту Страну в таблицу.