Данные не были сохранены: объект ссылается на несохраненный экземпляр переходного процесса - сохранить временный экземпляр перед промывкой
У меня есть база данных с двумя таблицами 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;
тогда объект этого класса я означает, что сначала необходимо сохранить страну.
потому что он позволит пользователю только сохранить его в базе данных, если для этой страны есть ключ, доступный для страны этого пользователя. означает, что он позволит пользователю быть сохраненным, если и только если эта страна существует в таблице страны.
Итак, для этого вам нужно сначала сохранить эту Страну в таблицу.