Как исправить ошибку Hibernate «объект ссылается на несохраненный временный экземпляр - сохраните временный экземпляр перед сбросом»

611

Я получаю следующую ошибку при сохранении объекта с помощью Hibernate

object references an unsaved transient instance - save the transient instance before flushing
Тушар Ахиррао
источник
1
В контексте контекста эта ошибка? это с или без переходной переменной?
Виджей

Ответы:

803

Вы должны включить cascade="all"(если используете xml) или cascade=CascadeType.ALL(если используете аннотации) в отображение своей коллекции.

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

Bozho
источник
7
Разве это не подразумевается? Разве вы не хотите, чтобы Hibernate спас их?
Маркус Леон
29
@ Маркус - нет, это не так. Вы можете обрабатывать их вручную.
Божо
5
Божо прав. Я столкнулся с обстоятельствами, когда у меня были коллекции, которыми я хотел управлять вручную из-за их размера или из-за бизнес-правил, которые не позволяют сохранять все объекты в коллекции одновременно.
Алекс Маршалл
26
Это происходит не только для коллекций, но и для простых однозначных сопоставлений
Себастьян Лорбер,
12
Не лучше ли начать с CascadeType.PERSIST и использовать для сохранения «persist»?
Сергей Шевчик,
249

Я считаю, что это может быть просто повторный ответ, но просто чтобы уточнить, я получил это на @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;
}
Макдональд Ноланд
источник
7
Что делать, если я не хочу каскадного сохранения отношений @OneToOne? Когда я создаю оба объекта в первый раз, как я могу сохранить их в базе данных, не вызывая исключения?
кстианский
4
Что если я хочу спасти ребенка в некоторых случаях, а не в других?
Омаручан
@xtian: Тогда вам нужно позаботиться о правильном порядке сохранения в базе данных, сохранив объекты с помощью EntityManager. В основном вы просто говорите em.persist (object1); em.persist (object2); и т. д.
kaba713
Я получил эту проблему специально, когда я использовал @Inheritance, в этом случае TABLE_PER_CLASS, я ссылался на подкласс. CascadeType.ALL это исправил.
Джим РисПоттер
Или вы создали объект сущности с new MyEntity(без синхронизации его с базой данных - сброс) вместо того, чтобы получать его синхронизированный экземпляр из базы данных. Выполнение запросов Hibernate с использованием этого экземпляра информирует вас о том, что то, что вы ожидаете в базе данных, отличается от того, что у вас есть в памяти вашего приложения. В этом случае - просто синхронизируйте / получите вашу сущность из БД и используйте ее. Тогда CascadeType.ALL не требуется.
Зон
67

Это происходит при сохранении объекта, когда Hibernate считает, что необходимо сохранить объект, связанный с тем, который вы сохраняете.

У меня была эта проблема, и я не хотел сохранять изменения в ссылочном объекте, поэтому я хотел, чтобы каскадный тип был NONE.

Хитрость заключается в том, чтобы убедиться, что ID и VERSION в указанном объекте установлены так, что Hibernate не считает, что указанный объект является новым объектом, который необходимо сохранить. Это сработало для меня.

Просмотрите все отношения в классе, который вы сохраняете, чтобы обработать связанные объекты (и связанные объекты связанных объектов) и убедитесь, что ID и VERSION установлены во всех объектах дерева объектов.

Cookalino
источник
4
Этот комментарий поставил меня на правильный путь. Я назначал новый экземпляр родительского объекта в свойство его дочернего элемента. Так что NH думал, что это разные экземпляры.
Эльвин
2
Да. Это происходит, если, например, идентификатор связанного объекта не включен (например, он был проигнорирован @JsonIgnore). Hibernate не может идентифицировать связанный объект, поэтому он хочет сохранить его.
Рори Штумпф
36

Введение

Как я объяснил в этой статье при использовании JPA и Hibernate, сущность может находиться в одном из следующих 4 состояний:

  • Новый . Недавно созданный объект, который никогда не ассоциировался с сеансом Hibernate (он же контекст постоянства) и не сопоставлен ни с одной строкой таблицы базы данных, считается находящимся в состоянии «Новый» или «Переходный».

    Чтобы стать постоянными, нам нужно либо явно вызвать persistметод, либо использовать транзитивный механизм персистентности.

  • Постоянный - постоянный объект был связан со строкой таблицы базы данных и управляется текущим контекстом постоянства.

    Любые изменения, внесенные в такой объект, будут обнаружены и распространены в базе данных (во время сброса сеанса).

  • Detached - после закрытия текущего сохраняемого контекста постоянства все ранее управляемые объекты становятся отсоединенными. Последовательные изменения больше не будут отслеживаться, и автоматическая синхронизация базы данных не произойдет.

  • Удалено - хотя JPA требует, чтобы разрешалось удалять только управляемые объекты, Hibernate также может удалять отдельные объекты (но только с помощью removeвызова метода).

Переходы состояния объекта

Чтобы переместить объект из одного состояния в другое, вы можете использовать persist, removeили mergeметоды.

Субъекты JPA

Исправление проблемы

Проблема, которую вы описываете в своем вопросе:

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 .
Влад Михалча
30

Или, если вы хотите использовать минимальные «полномочия» (например, если вы не хотите каскадного удаления), чтобы достичь того, что вы хотите, используйте

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;

...

@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
Харис Османагич
источник
11
Это должен быть принятый ответ. CascadeType.ALL слишком широк
lilalinux
5
Начиная с Hibernate 5.2.8, по-видимому, нет никакого способа добиться того же эффекта с помощью аннотаций JPA. Например, @ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})(все, кроме REMOVE) не каскадные обновления, как в Hibernate CascadeType.SAVE_UPDATE.
jcsahnwaldt говорит GoFundMonica
26

В моем случае это было вызвано не имея 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;
AllaP
источник
Если я сделаю это таким образом и сохраню родительский объект, то получится, что в моей родительской таблице будет две вставки, состоящие из двух строк. Я думаю, потому что у нас есть каскад на родительских и дочерних объектах?
theprogrammer
18

Это произошло для меня при сохранении сущности, в которой существующая запись в базе данных имела значение NULL для поля, помеченного @Version (для оптимистической блокировки). Обновление значения NULL до 0 в базе данных исправило это.

dukethrash
источник
Это должен быть новый вопрос, и он должен быть добавлен как ошибка, по крайней мере, вводящее в заблуждение исключение. Это оказалось причиной моей проблемы.
BML
Это решает мою проблему
Jad Chahine
11

Это не единственная причина ошибки. Я столкнулся с этим только что из-за ошибки опечатки в моем коде, которая, как мне кажется, установила значение сущности, которая уже была сохранена.

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;
}
nanospeck
источник
1
Схожая проблема с моей. В конце концов, когда я перезагружал объект локально, устанавливал свойство, затем сохранял, он работал нормально.
CsBalazsHungary
8

Не используйте, 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);

в то время как если объект просто «новый», он выдаст ту же ошибку.

Тийна
источник
1
на 100% правильно, Cascade.All это ленивое решение и должно применяться только при необходимости. Во-первых, если объект уже существует, проверьте, загружен ли он в текущем диспетчере объектов, если не загружен.
Ренато Мендес
7

Если ваша коллекция пуста, просто попробуйте: object.SetYouColection(null);

Жоабе Лусена
источник
Это была моя проблема. Я никогда бы не догадался, что мне пришлось вручную установить его на ноль.
Deadron
Это было также моей проблемой. Я не использовал коллекцию, поэтому сначала я не пробовал, но я установил для своего объекта значение null, и теперь оно работает.
Корм
5

Чтобы добавить мои 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);

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

В некоторых случаях deptIdPKIDnull из вызывающего метода, и я получаю ту же ошибку.

Итак, следите за nullзначениями для PK ID

manikanta
источник
Что делать, если его обнуляем?
Випин cp
У меня похожая проблема. Я получаю исключение, когда мой deptID равен 0. Любое другое значение больше 0 работает. Забавно, что у меня есть отдел с id = 0.
Густаво
5

Эта проблема возникла у меня, когда я создал новую сущность и связанную сущность в методе, помеченном как @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);
}

Чтобы исправить, я выполнил запрос перед созданием новой сущности.

mad_fox
источник
4

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

merge(A);
B.setA(A);
persist(B);

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

A=merge(A);//difference is here
B.setA(A);
persist(B);
Хоссейн Наср
источник
3

я получаю эту ошибку при использовании

getSession().save(object)

но это работает без проблем, когда я использую

getSession().saveOrUpdate(object) 
acpuma
источник
3

Я тоже столкнулся с такой же ситуацией. Установив следующую аннотацию над свойством, оно решает вопрос об исключении.

Исключение, с которым я столкнулся.

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 сохранить их в базу данных при сохранении родительского объекта.

Дулит Де Коста
источник
2

Ради полноты: A

org.hibernate.TransientPropertyValueException 

с сообщением

object references an unsaved transient instance - save the transient instance before flushing

также произойдет, когда вы попытаетесь сохранить / объединить объект со ссылкой на другой объект, который оказывается отсоединенным .

Ralf
источник
1

Еще одна возможная причина: в моем случае я пытался спасти ребенка перед тем, как сохранить родителя, на совершенно новом объекте.

Код был примерно таким в модели 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);
Джереми Гуделл
источник
1

Есть еще одна возможность, которая может вызвать эту ошибку в спящем режиме. Вы можете установить несохраненную ссылку вашего объекта Aна присоединенную сущность Bи хотите сохранить объект C. Даже в этом случае вы получите вышеупомянутую ошибку.

Хоссейн Наср
источник
1

Если вы используете Spring Data JPA, дополнительная @Transactionalаннотация к реализации вашего сервиса решит проблему.

Usama
источник
1

Я думаю, потому что вы пытаетесь сохранить объект, который имеет ссылку на другой объект, который еще не сохранен, и поэтому он пытается на стороне «БД» поместить ссылку на несуществующую строку


источник
0

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

Ниже простой экзамен отношения один к одному

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);
shashigura
источник
1
Наоборот, дочерняя сущность содержит значение FK и зависит от родителя, поэтому сначала нужно сохранить родителя! В блоке кода у вас все правильно.
Sõber
0

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

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);
pheromix
источник
0

Возможностей этой ошибки очень много, есть и другие возможности на странице добавления или редактирования. В моем случае я пытался сохранить объект 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;
        }

    }
Ravindra
источник
0

Я столкнулся с этим исключением, когда я не сохранил родительский объект, но я сохранял дочерний объект. Чтобы решить эту проблему, в том же сеансе я сохранил как дочерний, так и родительский объекты и использовал CascadeType.ALL для родительского объекта.

Таделе Айлень
источник
0

Случай 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();
.
.
Суровый Вардхан Сингх Соланки
источник
0

Моя проблема была связана с @BeforeEachJUnit. И даже если я сохранил связанные объекты (в моем случае@ManyToOne ), я получил ту же ошибку.

Проблема как-то связана с последовательностью, которую я имею в моем родителе. Если я назначу значение этому атрибуту, проблема будет решена.

Ex. Если у меня есть Вопрос сущности, который может иметь несколько категорий (одну или несколько), а Вопрос сущности имеет последовательность:

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
@Id
private Long id;

Я должен назначить значение question.setId(1L);

NikNik
источник
0

Просто сделайте Конструктор вашего отображения в вашем базовом классе. Например, если вы хотите, чтобы отношение «один-к-одному» в сущности А, сущности Б. было выбрано в качестве базового класса, то у А должен быть конструктор с аргументом «В».

Билал Ахмад
источник