Коли я зберігаю об'єкт за допомогою 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
(якщо ви використовуєте анотації) у своєму зіставленні колекції.
Це відбувається тому, що у вас є колекція у вашій сутності, і ця колекція містить один або кілька елементів, яких немає в базі даних. Вказавши вищенаведені параметри, ви скажете сплячому режиму зберегти їх у базі даних при збереженні батьків.
Я вважаю, що це може бути просто повторна відповідь, але просто для уточнення я отримав це як на @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;
}
new MyEntity
(не синхронізувавши його до бази даних - промивання), замість того, щоб отримувати його синхронізований примірник із бази даних. Здійснення запитів у сплячому режимі за допомогою цього примірника повідомляє про те, що те, що ви очікуєте знаходитись у базі даних, відрізняється від того, що є у вашій пам'яті програми. У цьому випадку - просто синхронізуйте / отримайте інстанцію від вашої сутності з БД та використовуйте її. Тоді CascadeType.ALL не потрібен.
Це відбувається під час збереження об’єкта, коли Hibernate вважає, що потрібно зберегти об'єкт, пов’язаний із тим, який ви зберігаєте.
У мене була ця проблема, і я не хотів зберігати зміни на посилається об'єкт, тому я хотів, щоб тип каскаду був НІКОЛИ.
Трюк полягає в тому, щоб забезпечити, щоб ідентифікатор та VERSION у посилається об'єкт встановлені таким чином, щоб Hibernate не думав, що посилається об'єкт є новим об'єктом, який потребує збереження. Це працювало для мене.
Перегляньте всі відносини класу, який ви зберігаєте, щоб опрацювати пов'язані об'єкти (та пов'язані з ними об’єкти асоційованих об’єктів) та переконайтесь, що ідентифікатор та VERSION встановлені у всіх об'єктах дерева об'єктів.
Як я пояснив у цій статті, використовуючи JPA та Hibernate, суб'єкт може перебувати в одному з наступних 4 штатів:
Новий - Новостворений об’єкт, який ніколи не асоціювався з сплячим сеансом (він же "Контекст стійкості") і не відображається в жодному рядку таблиці баз даних, вважається в новому або перехідному стані.
Щоб зберегтись, нам потрібно або явно викликати persist
метод, або скористатися механізмом транзитивної стійкості.
Постійний - Стійкий об'єкт асоціюється з рядком таблиці баз даних і ним керує поточний контекст постійності.
Будь-які зміни, внесені до такої сутності, будуть виявлені та розповсюджені до бази даних (під час часу сесії).
Від'єднано - Після закриття поточного контексту постійності всі раніше керовані об'єкти відключаються. Послідовні зміни більше не відстежуються, і автоматична синхронізація бази даних не відбуватиметься.
Видалено - Хоча JPA вимагає, щоб лише керовані об'єкти було дозволено видаляти, Hibernate також може видаляти відокремлені об'єкти (але лише за допомогою remove
виклику методу).
Щоб перемістити об'єкт з одного стану в інший, ви можете використовувати persist
, remove
або merge
методи.
Питання, яке ви описуєте у своєму запитанні:
object references an unsaved transient instance - save the transient instance before flushing
викликано асоціацією суб'єкта в штаті New з об'єктом, який знаходиться в штаті керованого .
Це може статися, коли ви асоціюєте дочірню сутність з колекцією, яка складається з кількох осіб у материнській сутності, і колекція не 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) не каскадує оновлення, як це CascadeType.SAVE_UPDATE
робить Hibernate .
У моєму випадку це було спричинене відсутністю 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
ідентифікатор. Нижче в коді зображений мій сценарій (а в ОП не було вказано жодного конкретного сценарію) .
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
значеннями для ідентифікатора ПК
Ця проблема трапилася зі мною, коли я створив нову сутність та асоційовану сутність методом, позначеним як @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>();
Що змусило сплячку викинути виняток:
Цей виняток кидається на вашу консоль, оскільки дочірній об’єкт, який я прикріплюю до батьківського об'єкта, на даний момент відсутній у базі даних.
Надаючи @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_id є недійсним, оскільки під час редагування я не був встановлений співробітник.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;
.
.
}
У вищенаведеному випадку, коли батьків (довідник) та дочірня (ReferenceAdditionalDetails), які мають відношення OneToOne, і коли ви намагаєтесь створити довідковий об'єкт, а потім його дочірню (ReferenceAdditionalDetails), це дасть вам те саме виняток. Таким чином, щоб уникнути винятку, вам потрібно встановити null для дочірнього класу, а потім створити батьківський (Sample Code)
.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.
Моя проблема була пов'язана з @BeforeEach
з JUnit. І навіть якщо я врятував пов'язані сутності (в моєму випадку@ManyToOne
), я отримав ту саму помилку.
Проблема так чи інакше пов'язана з послідовністю, яку я маю у свого батька. Якщо я призначу цьому атрибуту значення, проблема вирішується.
Вих. Якщо у мене є питання про сутність, яке може містити деякі категорії (одну або більше), а питання питання сутності має послідовність:
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
@Id
private Long id;
Я повинен призначити значення question.setId(1L);
Просто зробіть конструктор вашого відображення у базовому класі. Начебто, якщо ви хочете відношення «Один до одного» в Entity A, Entity B. Якщо ви приймаєте A як базовий клас, тоді A повинен мати Конструктор, який має B як аргумент.