Я получаю следующую ошибку при сохранении объекта с помощью Hibernate
object references an unsaved transient instance - save the transient instance before flushing
Я получаю следующую ошибку при сохранении объекта с помощью Hibernate
object references an unsaved transient instance - save the transient instance before flushing
Ответы:
Вы должны включить 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"
сообщение при сохранении Parent.
Добавление в cascade = {CascadeType.ALL}
на Parent's
ссылку на Child
решить эту проблему , в обеих случаях. Это спасло Child
и Parent
.
Извините за любые повторные ответы, просто хотел бы уточнить для людей.
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
return performanceLog;
}
new MyEntity
(без синхронизации его с базой данных - сброс) вместо того, чтобы получать его синхронизированный экземпляр из базы данных. Выполнение запросов Hibernate с использованием этого экземпляра информирует вас о том, что то, что вы ожидаете в базе данных, отличается от того, что у вас есть в памяти вашего приложения. В этом случае - просто синхронизируйте / получите вашу сущность из БД и используйте ее. Тогда CascadeType.ALL не требуется.
Это происходит при сохранении объекта, когда Hibernate считает, что необходимо сохранить объект, связанный с тем, который вы сохраняете.
У меня была эта проблема, и я не хотел сохранять изменения в ссылочном объекте, поэтому я хотел, чтобы каскадный тип был NONE.
Хитрость заключается в том, чтобы убедиться, что ID и VERSION в указанном объекте установлены так, что Hibernate не считает, что указанный объект является новым объектом, который необходимо сохранить. Это сработало для меня.
Просмотрите все отношения в классе, который вы сохраняете, чтобы обработать связанные объекты (и связанные объекты связанных объектов) и убедитесь, что ID и VERSION установлены во всех объектах дерева объектов.
Как я объяснил в этой статье при использовании JPA и Hibernate, сущность может находиться в одном из следующих 4 состояний:
Новый . Недавно созданный объект, который никогда не ассоциировался с сеансом Hibernate (он же контекст постоянства) и не сопоставлен ни с одной строкой таблицы базы данных, считается находящимся в состоянии «Новый» или «Переходный».
Чтобы стать постоянными, нам нужно либо явно вызвать persist
метод, либо использовать транзитивный механизм персистентности.
Постоянный - постоянный объект был связан со строкой таблицы базы данных и управляется текущим контекстом постоянства.
Любые изменения, внесенные в такой объект, будут обнаружены и распространены в базе данных (во время сброса сеанса).
Detached - после закрытия текущего сохраняемого контекста постоянства все ранее управляемые объекты становятся отсоединенными. Последовательные изменения больше не будут отслеживаться, и автоматическая синхронизация базы данных не произойдет.
Удалено - хотя 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
ассоциации, ознакомьтесь также с этой статьей .
Или, если вы хотите использовать минимальные «полномочия» (например, если вы не хотите каскадного удаления), чтобы достичь того, что вы хотите, используйте
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
...
@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
@ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})
(все, кроме REMOVE) не каскадные обновления, как в Hibernate CascadeType.SAVE_UPDATE
.
В моем случае это было вызвано не имея 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;
Это произошло для меня при сохранении сущности, в которой существующая запись в базе данных имела значение NULL для поля, помеченного @Version (для оптимистической блокировки). Обновление значения NULL до 0 в базе данных исправило это.
Это не единственная причина ошибки. Я столкнулся с этим только что из-за ошибки опечатки в моем коде, которая, как мне кажется, установила значение сущности, которая уже была сохранена.
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;
}
Не используйте, 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);
в то время как если объект просто «новый», он выдаст ту же ошибку.
Если ваша коллекция пуста, просто попробуйте: 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
PKIDnull
из вызывающего метода, и я получаю ту же ошибку.
Итак, следите за null
значениями для PK ID
Эта проблема возникла у меня, когда я создал новую сущность и связанную сущность в методе, помеченном как @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);
}
Чтобы исправить, я выполнил запрос перед созданием новой сущности.
Помимо других хороших ответов, это может произойти, если вы используете merge
для сохранения объекта и случайно забыли использовать объединенную ссылку на объект в родительском классе. рассмотрим следующий пример
merge(A);
B.setA(A);
persist(B);
В этом случае вы объединяете, A
но забыли использовать объединенный объект A
. Чтобы решить проблему, вы должны переписать код следующим образом.
A=merge(A);//difference is here
B.setA(A);
persist(B);
Я тоже столкнулся с такой же ситуацией. Установив следующую аннотацию над свойством, оно решает вопрос об исключении.
Исключение, с которым я столкнулся.
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
также произойдет, когда вы попытаетесь сохранить / объединить объект со ссылкой на другой объект, который оказывается отсоединенным .
Еще одна возможная причина: в моем случае я пытался спасти ребенка перед тем, как сохранить родителя, на совершенно новом объекте.
Код был примерно таким в модели 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
. Даже в этом случае вы получите вышеупомянутую ошибку.
Я думаю, потому что вы пытаетесь сохранить объект, который имеет ссылку на другой объект, который еще не сохранен, и поэтому он пытается на стороне «БД» поместить ссылку на несуществующую строку
Простой способ решения этой проблемы - сохранить оба объекта. сначала сохраните дочернюю сущность, а затем сохраните родительскую сущность. Потому что родительский объект зависит от дочернего объекта для значения внешнего ключа.
Ниже простой экзамен отношения один к одному
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);
Одной из возможных причин ошибки является отсутствие установки значения родительского объекта; например, для отношений отдела с сотрудниками вы должны написать это, чтобы исправить ошибку:
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);
Возможностей этой ошибки очень много, есть и другие возможности на странице добавления или редактирования. В моем случае я пытался сохранить объект 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;
}
}
Я столкнулся с этим исключением, когда я не сохранил родительский объект, но я сохранял дочерний объект. Чтобы решить эту проблему, в том же сеансе я сохранил как дочерний, так и родительский объекты и использовал CascadeType.ALL для родительского объекта.
Случай 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, а также при попытке создать сущность Reference, а затем ее дочерний объект (ReferenceAdditionalDetails), вы получите одно и то же исключение. Поэтому, чтобы избежать исключения, вы должны установить значение NULL для дочернего класса, а затем создать родительский. (Пример кода)
.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.
Моя проблема была связана с @BeforeEach
JUnit. И даже если я сохранил связанные объекты (в моем случае@ManyToOne
), я получил ту же ошибку.
Проблема как-то связана с последовательностью, которую я имею в моем родителе. Если я назначу значение этому атрибуту, проблема будет решена.
Ex. Если у меня есть Вопрос сущности, который может иметь несколько категорий (одну или несколько), а Вопрос сущности имеет последовательность:
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
@Id
private Long id;
Я должен назначить значение question.setId(1L);
Просто сделайте Конструктор вашего отображения в вашем базовом классе. Например, если вы хотите, чтобы отношение «один-к-одному» в сущности А, сущности Б. было выбрано в качестве базового класса, то у А должен быть конструктор с аргументом «В».