Объект ссылается на несохраненный временный экземпляр - сохраните временный экземпляр перед сбросом
Я получаю следующую ошибку при сохранении объекта с помощью Hibernate
object references an unsaved transient instance - save the transient instance before flushing
33 ответа
Вы должны включить cascade="all"
(если используется XML) или cascade=CascadeType.ALL
(если используются аннотации) в вашей коллекции сопоставлений.
Это происходит потому, что у вас есть коллекция в вашей сущности, и у этой коллекции есть один или несколько элементов, которых нет в базе данных. Указав вышеуказанные параметры, вы указываете hibernate сохранять их в базу данных при сохранении их родителя.
Я считаю, что это может быть просто повторный ответ, но просто чтобы уточнить, я получил это на @OneToOne
отображение, а также @OneToMany
, В обоих случаях это был тот факт, что Child
объект, который я добавлял к Parent
еще не был сохранен в базе данных. Поэтому, когда я добавил Child
к Parent
затем сохранил Parent
Hibernate бросил бы "object references an unsaved transient instance - save the transient instance before flushing"
сообщение при сохранении Родителя.
Добавление в cascade = {CascadeType.ALL}
на Parent's
ссылка на Child
решил проблему в обоих случаях. Это спасло Child
и Parent
,
Извините за любые повторные ответы, просто хотел бы уточнить для людей.
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
return performanceLog;
}
Вступление
Как я объяснял в этой статье, при использовании JPA и Hibernate объект может находиться в одном из следующих 4 состояний:
Новый - вновь созданный объект, который никогда не был связан с сеансом гибернации (также известный как контекст сохранения) и не сопоставлен ни с одной строкой таблицы базы данных, считается находящимся в состоянии New или Transient.
Чтобы стать устойчивым, нам нужно либо явно вызвать
persist
метод или использовать механизм переходной персистентности.Постоянный - постоянный объект был связан со строкой таблицы базы данных и управляется текущим текущим контекстом сохранения.
Любое изменение, внесенное в такую сущность, будет обнаружено и распространено в базу данных (во время сброса сеанса).
Отсоединение - после закрытия текущего работающего контекста постоянства все ранее управляемые объекты отсоединяются. Последовательные изменения больше не будут отслеживаться, и автоматическая синхронизация базы данных не произойдет.
Удалено - хотя JPA требует, чтобы было разрешено удаление только управляемых объектов, Hibernate также может удалять отсоединенные объекты (но только через
remove
вызов метода).
Переходы между состояниями объекта
Чтобы переместить объект из одного состояния в другое, вы можете использовать persist
, remove
или merge
методы.
Устранение проблемы
Проблема, которую вы описываете в своем вопросе:
object references an unsaved transient instance - save the transient instance before flushing
вызывается связыванием объекта в состоянии New с объектом, находящимся в состоянии Managed.
Это может произойти, когда вы связываете дочернюю сущность с коллекцией "один ко многим" в родительской сущности, а эта коллекция не cascade
переходы состояния объекта.
Итак, как я объяснял в этой статье, вы можете исправить это, добавив каскад к ассоциации сущностей, которая вызвала этот сбой, следующим образом:
В @OneToOne
ассоциация
@OneToOne(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private PostDetails details;
Обратите внимание на
CascadeType.ALL
ценность, которую мы добавили дляcascade
атрибут.
В @OneToMany
ассоциация
@OneToMany(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
Опять же, CascadeType.ALL
подходит для двунаправленного@OneToMany
ассоциации.
Теперь, чтобы каскад работал правильно в двунаправленном режиме, вам также необходимо убедиться, что родительская и дочерняя ассоциации синхронизированы.
Прочтите эту статью, чтобы узнать, как лучше всего достичь этой цели.
В @ManyToMany
ассоциация
@ManyToMany(
mappedBy = "authors",
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}
)
private List<Book> books = new ArrayList<>();
В @ManyToMany
ассоциация, вы не можете использовать CascadeType.ALL
или orphanRemoval
так как это будет распространять переход состояния удаленного объекта от одного родительского объекта к другому родительскому объекту.
Следовательно, для @ManyToMany
ассоциаций, вы обычно каскадируете CascadeType.PERSIST
или CascadeType.MERGE
операции. В качестве альтернативы вы можете расширить это доDETACH
или REFRESH
.
Для получения дополнительных сведений о лучшем способе отображения
@ManyToMany
ассоциации, ознакомьтесь также с этой статьей.
Это происходит при сохранении объекта, когда Hibernate считает, что ему нужно сохранить объект, связанный с тем, который вы сохраняете.
У меня была эта проблема, и я не хотел сохранять изменения в ссылочном объекте, поэтому я хотел, чтобы каскадный тип был NONE.
Хитрость заключается в том, чтобы убедиться, что ID и VERSION в указанном объекте установлены так, что Hibernate не думает, что указанный объект является новым объектом, который необходимо сохранить. Это сработало для меня.
Просмотрите все отношения в классе, который вы сохраняете, чтобы обработать связанные объекты (и связанные объекты связанных объектов) и убедитесь, что ID и VERSION установлены во всех объектах дерева объектов.
В моем случае это было вызвано отсутствием CascadeType
на @ManyToOne
сторона двунаправленных отношений. Чтобы быть более точным, я имел CascadeType.ALL
на @OneToMany
сторона и не было его на @ManyToOne
, Добавление CascadeType.ALL
в @ManyToOne
решил проблему. Сторонаодин-ко-многим:
@OneToMany(cascade = CascadeType.ALL, mappedBy="globalConfig", orphanRemoval = true)
private Set<GlobalConfigScope>gcScopeSet;
Много-к-одному (вызвало проблему)
@ManyToOne
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Много-к-одному (исправлено путем добавления CascadeType.PERSIST
)
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Или, если вы хотите использовать минимальные "полномочия" (например, если вы не хотите каскадного удаления), чтобы достичь того, что вы хотите, используйте
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
...
@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
Это произошло для меня при сохранении сущности, в которой существующая запись в базе данных имела значение NULL для поля, помеченного @Version (для оптимистической блокировки). Обновление значения NULL до 0 в базе данных исправило это.
Не использовать Cascade.All
пока ты действительно не должен. Role
а также Permission
двунаправленный manyToMany
связь. Тогда следующий код будет работать нормально
Permission p = new Permission();
p.setName("help");
Permission p2 = new Permission();
p2.setName("self_info");
p = (Permission)crudRepository.save(p); // returned p has id filled in.
p2 = (Permission)crudRepository.save(p2); // so does p2.
Role role = new Role();
role.setAvailable(true);
role.setDescription("a test role");
role.setRole("admin");
List<Permission> pList = new ArrayList<Permission>();
pList.add(p);
pList.add(p2);
role.setPermissions(pList);
crudRepository.save(role);
в то время как если объект является просто "новым", он выдаст ту же ошибку.
Это не единственная причина ошибки. Я столкнулся с этим только что из-за ошибки опечатки в моем коде, которая, как я полагаю, установила значение сущности, которая уже была сохранена.
X x2 = new X();
x.setXid(memberid); // Error happened here - x was a previous global entity I created earlier
Y.setX(x2);
Я обнаружил ошибку, найдя точно, какая переменная вызвала ошибку (в этом случае String xid
). Я использовал catch
вокруг всего блока кода, который сохранил сущность и напечатал следы.
{
code block that performed the operation
} catch (Exception e) {
e.printStackTrace(); // put a break-point here and inspect the 'e'
return ERROR;
}
Кроме всех других хороших ответов, это может произойти, если вы используете merge
сохранить объект и случайно забыть использовать объединенную ссылку объекта в родительском классе. рассмотрим следующий пример
merge(A);
B.setA(A);
persist(B);
В этом случае вы сливаете A
но забудьте использовать объединенный объект A
, Чтобы решить проблему, вы должны переписать код следующим образом.
A=merge(A);//difference is here
B.setA(A);
persist(B);
Это также может произойти, когда у вас есть отношение OneToMany, и вы пытаетесь добавить дочерний объект в список в родительском объекте, а затем извлекаете этот список через родительский объект (перед сохранением этого родительского объекта) без сохранения самого дочернего объекта , например:
Child childEntity = new Child();
parentEntity.addChild(childEntity);
parentEntity.getChildren(); // I needed the retrieval for logging, but one may need it for other reasons.
parentRepository.save(parentEntity);
Ошибка была выдана, когда я сохранил родительский объект. Если я удалил извлечение в предыдущей строке, то ошибка не возникла, но, конечно, это не решение.
Решение было спасение childEntity и добавив , что сохраненный дочерний объект для родительского объекта, как это:
Child childEntity = new Child();
Child savedChildEntity = childRepository.save(childEntity);
parentEntity.addChild(savedChildEntity);
parentEntity.getChildren();
parentRepository.save(parentEntity);
Эта проблема произошла со мной, когда я создал новую сущность и связанную сущность в методе, помеченном как @Transactional
, затем выполнил запрос перед сохранением. бывший
@Transactional
public someService() {
Entity someEntity = new Entity();
AssocaiatedEntity associatedEntity = new AssocaitedEntity();
someEntity.setAssociatedEntity(associatedEntity);
associatedEntity.setEntity(someEntity);
// Performing any query was causing hibernate to attempt to persist the new entity. It would then throw an exception
someDao.getSomething();
entityDao.create(someEntity);
}
Чтобы исправить, я выполнил запрос перед созданием нового объекта.
Если ваша коллекция пуста, просто попробуйте: object.SetYouColection(null);
Чтобы добавить мои 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
Если вы используете Spring Data JPA, то дополнение @Transactional
аннотация к реализации вашего сервиса решит проблему.
Я только что получил эту ошибку, потому что я назначал объект без прокси другому объекту перед сохранением.
Я должен был связать экземпляр прокси-сущности.
См. пояснение ниже:
Child saved = childRepository.save(child);
// INCORRECT
parent.setChild(child); // <-- 'child' is NOT managed (not proxied)
// CORRECT
parent.setChild(saved); // <-- 'saved' is managed (proxied)
Я тоже столкнулся с такой же ситуацией. Установив следующую аннотацию над свойством, оно решает вопрос об исключении.
Исключение, с которым я столкнулся.
Exception in thread "main" java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.model.Car_OneToMany
Чтобы преодолеть, аннотации я использовал.
@OneToMany(cascade = {CascadeType.ALL})
@Column(name = "ListOfCarsDrivenByDriver")
private List<Car_OneToMany> listOfCarsBeingDriven = new ArrayList<Car_OneToMany>();
Что заставило Hibernate бросить исключение:
Это исключение выдается на вашей консоли, потому что дочерний объект, который я присоединяю к родительскому объекту, в данный момент отсутствует в базе данных.
Предоставляя @OneToMany(cascade = {CascadeType.ALL})
он сообщает Hibernate сохранить их в базу данных при сохранении родительского объекта.
Ради полноты: A
org.hibernate.TransientPropertyValueException
с сообщением
object references an unsaved transient instance - save the transient instance before flushing
также произойдет, когда вы попытаетесь сохранить / объединить сущность со ссылкой на другую сущность, которая оказывается отсоединенной.
Я получаю эту ошибку при использовании
getSession().save(object)
но это работает без проблем, когда я использую
getSession().saveOrUpdate(object)
Я думаю, потому что вы пытаетесь сохранить объект, который имеет ссылку на другой объект, который еще не сохранен, и поэтому он пытается в "стороне БД" поместить ссылку на несуществующую строку
В моем случае проблема была совсем другой. У меня есть два класса, скажем, c1 и c2. Между C1 и C2 есть зависимость OneToMany. Теперь, если я сохраняю C1 в БД, он выдает ошибку выше.
Решение этой проблемы заключалось в том, чтобы получить первый идентификатор C2 из запроса потребителя и найти C2 через вызов репозитория. Затем сохранить c2 в объект C1. Теперь, если я сохраняю C1, он работает нормально.
Случай 1: я получал это исключение, когда пытался создать родителя и сохранить эту родительскую ссылку на его потомка, а затем какой-то другой запрос DELETE/UPDATE (JPQL). Поэтому я просто сбрасываю () вновь созданную сущность после создания родителя и после создания дочернего элемента, используя ту же родительскую ссылку. Это сработало для меня.
Случай 2:
Родительский класс
public class Reference implements Serializable {
@Id
@Column(precision=20, scale=0)
private BigInteger id;
@Temporal(TemporalType.TIMESTAMP)
private Date modifiedOn;
@OneToOne(mappedBy="reference")
private ReferenceAdditionalDetails refAddDetails;
.
.
.
}
Детский класс:
public class ReferenceAdditionalDetails implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@OneToOne
@JoinColumn(name="reference",referencedColumnName="id")
private Reference reference;
private String preferedSector1;
private String preferedSector2;
.
.
}
В приведенном выше случае, когда родительский объект (Reference) и дочерний элемент (ReferenceAdditionalDetails) имеют отношение OneToOne, а также при попытке создания ссылочного объекта, а затем его дочернего элемента (ReferenceAdditionalDetails), вы получите одно и то же исключение. Поэтому, чтобы избежать исключения, вы должны установить значение null для дочернего класса, а затем создать родительский.(Пример кода)
.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.
Существует много возможностей этой ошибки, некоторые другие возможности также доступны на странице добавления или редактирования. В моем случае я пытался сохранить объект AdvanceSalary. Проблема заключается в том, что при редактировании AdvanceSalary employee.employee_id имеет значение null, поскольку при редактировании не был задан employee.employee_id. Я сделал скрытое поле и установил его. мой код работает абсолютно нормально.
@Entity(name = "ic_advance_salary")
@Table(name = "ic_advance_salary")
public class AdvanceSalary extends BaseDO{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
@Column(name = "employee_id", insertable=false, updatable=false)
@NotNull(message="Please enter employee Id")
private Long employee_id;
@Column(name = "advance_date")
@DateTimeFormat(pattern = "dd-MMM-yyyy")
@NotNull(message="Please enter advance date")
private Date advance_date;
@Column(name = "amount")
@NotNull(message="Please enter Paid Amount")
private Double amount;
@Column(name = "cheque_date")
@DateTimeFormat(pattern = "dd-MMM-yyyy")
private Date cheque_date;
@Column(name = "cheque_no")
private String cheque_no;
@Column(name = "remarks")
private String remarks;
public AdvanceSalary() {
}
public AdvanceSalary(Integer advance_salary_id) {
this.id = advance_salary_id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public Long getEmployee_id() {
return employee_id;
}
public void setEmployee_id(Long employee_id) {
this.employee_id = employee_id;
}
}
Я столкнулся с той же ошибкой для всех HTTP-транзакций PUT после введения оптимистической блокировки (@Version)
Во время обновления объекта обязательно отправить идентификатор и версию этого объекта. Если какое-либо из полей сущности связано с другими сущностями, тогда для этого поля мы также должны предоставить значения идентификатора и версии, без этого JPA пытается сначала сохранить эту связанную сущность как новую сущность.
Пример: у нас есть две сущности -> Автомобиль(id,Car,version); Автомобиль(id, версия, марка); чтобы обновить / сохранить сущность автомобиля, убедитесь, что в поле автомобиля в сущности автомобиля указаны поля идентификатора и версии.
Еще одна возможная причина: в моем случае я пытался спасти ребенка перед тем, как сохранить родителя, на совершенно новом объекте.
Код был примерно таким в модели User.java:
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();
Метод setNewPassword() создает запись PasswordHistory и добавляет ее в коллекцию истории в User. Так как оператор create() еще не был выполнен для родителя, он пытался сохранить в коллекции сущность, которая еще не была создана. Все, что мне нужно было сделать, чтобы это исправить, это переместить вызов setNewPassword() после вызова create().
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);
Есть еще одна возможность, которая может вызвать эту ошибку в спящем режиме. Вы можете установить несохраненную ссылку на ваш объект A
прикрепленной сущности B
и хочу сохранить объект C
, Даже в этом случае вы получите вышеупомянутую ошибку.
Одной из возможных причин ошибки является отсутствие установки значения родительского объекта; например, для отношений отдела с сотрудниками вы должны написать это, чтобы исправить ошибку:
Department dept = (Department)session.load(Department.class, dept_code); // dept_code is from the jsp form which you get in the controller with @RequestParam String department
employee.setDepartment(dept);
Просто сделайте Конструктор вашего отображения в вашем базовом классе. Например, если вам нужна связь "один-к-одному" в сущности A, сущности B. если вы принимаете A в качестве базового класса, тогда у A должен быть конструктор с аргументом B.
Простой способ решения этой проблемы - сохранить оба объекта. сначала сохраните дочернюю сущность, а затем сохраните родительскую сущность. Потому что родительский объект зависит от дочернего объекта для значения внешнего ключа.
Ниже простой экзамен отношения один к одному
insert into Department (name, numOfemp, Depno) values (?, ?, ?)
Hibernate: insert into Employee (SSN, dep_Depno, firstName, lastName, middleName, empno) values (?, ?, ?, ?, ?, ?)
Session session=sf.openSession();
session.beginTransaction();
session.save(dep);
session.save(emp);
Я столкнулся с этим исключением, когда я не сохранил родительский объект, но я сохранял дочерний объект. Чтобы решить эту проблему, в одном сеансе я сохранил как дочерний, так и родительский объекты и использовал CascadeType.ALL для родительского объекта.