Podczas zapisywania obiektu za pomocą Hibernacji pojawia się następujący błąd
object references an unsaved transient instance - save the transient instance before flushing
Podczas zapisywania obiektu za pomocą Hibernacji pojawia się następujący błąd
object references an unsaved transient instance - save the transient instance before flushing
Odpowiedzi:
Powinieneś dołączyć cascade="all"
(jeśli używasz xml) lub cascade=CascadeType.ALL
(jeśli używasz adnotacji) do swojego mapowania kolekcji.
Dzieje się tak, ponieważ masz kolekcję w swojej encji, a ta kolekcja zawiera jeden lub więcej elementów, które nie są obecne w bazie danych. Określając powyższe opcje, każesz hibernacji zapisać je w bazie danych podczas zapisywania ich elementu nadrzędnego.
Wierzę, że to może być tylko powtórzenie odpowiedzi, ale dla wyjaśnienia, mam to na @OneToOne
mapie, a także @OneToMany
. W obu przypadkach był to fakt, że Child
obiekt, który dodawałem, Parent
nie był jeszcze zapisany w bazie danych. Więc kiedy dodałem Child
do Parent
, a następnie zapisałem Parent
, Hibernacja podrzuciłaby "object references an unsaved transient instance - save the transient instance before flushing"
wiadomość podczas zapisywania Rodzica.
Dodanie w cascade = {CascadeType.ALL}
sprawie Parent's
odniesienia do Child
rozwiązania problemu w obu przypadkach. To uratowało Child
i Parent
.
Przepraszamy za powtarzające się odpowiedzi, chciałem tylko wyjaśnić ludziom.
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
return performanceLog;
}
new MyEntity
(bez synchronizacji z bazą danych - opróżnianie), zamiast pobierania jego zsynchronizowanej instancji z bazy danych. Wykonywanie zapytań Hibernacja przy użyciu tego wystąpienia informuje, że to, czego oczekujesz w bazie danych, różni się od tego, co masz w pamięci aplikacji. W takim przypadku - po prostu zsynchronizuj / pobierz instancję z DB i użyj jej. Wówczas CascadeType.ALL nie jest potrzebny.
Dzieje się tak podczas zapisywania obiektu, gdy Hibernacja uważa, że musi on zapisać obiekt skojarzony z tym, który zapisujesz.
Miałem ten problem i nie chciałem zapisywać zmian w przywoływanym obiekcie, więc chciałem, aby typ kaskady był BRAK.
Sztuczka polega na upewnieniu się, że identyfikator i WERSJA w przywoływanym obiekcie są ustawione, aby Hibernacja nie uważała, że przywoływany obiekt jest nowym obiektem, który wymaga zapisania. To zadziałało dla mnie.
Przejrzyj wszystkie relacje w zapisywanej klasie, aby opracować powiązane obiekty (i powiązane obiekty powiązanych obiektów) i upewnij się, że ID i WERSJA są ustawione we wszystkich obiektach drzewa obiektów.
Jak wyjaśniłem w tym artykule podczas korzystania z JPA i Hibernacji, jednostka może znajdować się w jednym z następujących 4 stanów:
Nowość - Nowo utworzony obiekt, który nigdy nie był skojarzony z sesją hibernacji (inaczej Kontekst Persistence) i nie jest odwzorowany na żaden wiersz tabeli bazy danych, jest uważany za będący w stanie Nowy lub Przejściowy.
Aby się utrwalić, musimy albo jawnie wywołać tę persist
metodę, albo skorzystać z mechanizmu trwałości przechodniego.
Trwały - trwały byt został powiązany z wierszem tabeli bazy danych i jest zarządzany przez aktualnie działający kontekst trwałości.
Wszelkie zmiany dokonane w takiej jednostce zostaną wykryte i propagowane do bazy danych (podczas czasu opróżniania sesji).
Odłączony - Po zamknięciu aktualnie działającego kontekstu trwałości odłączane są wszystkie wcześniej zarządzane jednostki. Kolejne zmiany nie będą już śledzone i nie nastąpi automatyczna synchronizacja bazy danych.
Usunięte - chociaż JPA wymaga, aby usuwane były tylko zarządzane jednostki, Hibernacja może również usuwać odłączone jednostki (ale tylko poprzez remove
wywołanie metody).
Aby przenieść jednostkę z jednego stanu do drugiego, można użyć persist
, remove
ani merge
metod.
Problem, który opisujesz w swoim pytaniu:
object references an unsaved transient instance - save the transient instance before flushing
jest spowodowany przez powiązanie encji w stanie Nowy z encją w stanie Zarządzanym .
Może się to zdarzyć, gdy kojarzy się jednostkę podrzędną z kolekcją jeden do wielu w jednostce nadrzędnej, a kolekcja nie cascade
stanu encji.
Tak więc, jak wyjaśniłem w tym artykule , możesz to naprawić, dodając kaskadę do powiązania encji, która spowodowała ten błąd, w następujący sposób:
@OneToOne
stowarzyszenie@OneToOne(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private PostDetails details;
Zwróć uwagę na
CascadeType.ALL
wartość, którą dodaliśmy dlacascade
atrybutu.
@OneToMany
stowarzyszenie@OneToMany(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
Ponownie CascadeType.ALL
nadaje się do skojarzeń dwukierunkowych@OneToMany
.
Teraz, aby kaskada działała poprawnie w trybie dwukierunkowym, musisz także upewnić się, że skojarzenia nadrzędne i podrzędne są zsynchronizowane.
Sprawdź ten artykuł, aby uzyskać więcej informacji na temat najlepszego sposobu osiągnięcia tego celu.
@ManyToMany
stowarzyszenie@ManyToMany(
mappedBy = "authors",
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}
)
private List<Book> books = new ArrayList<>();
W @ManyToMany
związku nie można używać CascadeType.ALL
luborphanRemoval
ponieważ spowoduje to propagację przejścia stanu usuwania encji z jednego rodzica na inny podmiot nadrzędny.
Dlatego w przypadku @ManyToMany
skojarzeń zwykle kaskadowo wykonuje się operacje CascadeType.PERSIST
lub CascadeType.MERGE
. Alternatywnie możesz rozwinąć to do DETACH
lub REFRESH
.
Aby uzyskać więcej informacji na temat najlepszego sposobu mapowania
@ManyToMany
powiązania, sprawdź również ten artykuł .
Lub, jeśli chcesz użyć minimalnych „mocy” (np. Jeśli nie chcesz kasować kasowania), aby osiągnąć to, czego chcesz, użyj
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
...
@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
@ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})
(wszystkie oprócz USUŃ) nie kaskadują aktualizacji, jak Hibernacja CascadeType.SAVE_UPDATE
.
W moim przypadku było to spowodowane nie mając CascadeType
na @ManyToOne
stronie relacji dwukierunkowej. Mówiąc ściślej, miałem CascadeType.ALL
po @OneToMany
stronie i nie miałem go na sobie @ManyToOne
. Dodawanie w CascadeType.ALL
celu @ManyToOne
rozwiązania problemu.
Strona jeden do wielu :
@OneToMany(cascade = CascadeType.ALL, mappedBy="globalConfig", orphanRemoval = true)
private Set<GlobalConfigScope>gcScopeSet;
Strona wiele do jednego (spowodowała problem)
@ManyToOne
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Wiele do jednego (naprawione przez dodanie CascadeType.PERSIST
)
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Stało się to dla mnie, gdy trwałem bytu, w którym istniejący rekord w bazie danych miał wartość NULL dla pola z adnotacją @Version (dla optymistycznego blokowania). Poprawiono wartość NULL do 0 w bazie danych.
To nie jedyny powód błędu. Spotkałem go właśnie teraz z powodu błędu literowego w moim kodzie, który, jak sądzę, ustawił wartość bytu, który został już zapisany.
X x2 = new X();
x.setXid(memberid); // Error happened here - x was a previous global entity I created earlier
Y.setX(x2);
Zauważyłem błąd, znajdując dokładnie, która zmienna spowodowała błąd (w tym przypadku String xid
). Użyłem catch
całego bloku kodu, który uratował byt i wydrukował ślady.
{
code block that performed the operation
} catch (Exception e) {
e.printStackTrace(); // put a break-point here and inspect the 'e'
return ERROR;
}
Nie używaj, Cascade.All
dopóki naprawdę nie musisz. Role
i Permission
mają manyToMany
relacje dwukierunkowe . Następnie następujący kod będzie działał poprawnie
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);
podczas gdy jeśli obiekt jest po prostu „nowy”, wygenerowałby ten sam błąd.
Jeśli twoja kolekcja ma wartość zerową, po prostu spróbuj: object.SetYouColection(null);
Aby dodać moje 2 centy, dostałem ten sam problem, gdy przypadkowo wysyłam null
jako identyfikator. Poniższy kod przedstawia mój scenariusz (a OP nie wspomniał o żadnym konkretnym scenariuszu) .
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);
Tutaj ustawiam istniejący identyfikator działu na nową instancję pracownika, nie pobierając najpierw jednostki działu, ponieważ nie chcę uruchamiać innego wybranego zapytania.
W niektórych scenariuszach deptId
PKID pochodzi null
od metody wywołania i otrzymuję ten sam błąd.
Uważaj więc na null
wartości dla PK ID
Ten problem przydarzył mi się, gdy utworzyłem nowy obiekt i powiązany obiekt w metodzie oznaczonej jako @Transactional
, a następnie wykonałem zapytanie przed zapisaniem. Dawny
@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);
}
Aby to naprawić, wykonałem zapytanie przed utworzeniem nowego obiektu.
oprócz wszystkich innych dobrych odpowiedzi, może się tak zdarzyć, jeśli użyjesz merge
do utrwalenia obiektu i przypadkowo zapomnisz użyć scalonego odwołania do obiektu w klasie nadrzędnej. rozważ następujący przykład
merge(A);
B.setA(A);
persist(B);
W takim przypadku scalasz, A
ale zapominasz użyć scalonego obiektu A
. aby rozwiązać problem, musisz przepisać kod w ten sposób.
A=merge(A);//difference is here
B.setA(A);
persist(B);
Też spotkałem się z tą samą sytuacją. Ustawienie poniższej adnotacji nad właściwością sprawiło, że rozwiązano zgłoszony wyjątek.
Wyjątek, z którym się spotkałem.
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
Aby przezwyciężyć, użyłem adnotacji.
@OneToMany(cascade = {CascadeType.ALL})
@Column(name = "ListOfCarsDrivenByDriver")
private List<Car_OneToMany> listOfCarsBeingDriven = new ArrayList<Car_OneToMany>();
Co sprawiło, że Hibernacja rzuciła wyjątek:
Ten wyjątek jest zgłaszany do konsoli, ponieważ obiekt podrzędny, który dołączam do obiektu nadrzędnego, nie jest w tym momencie obecny w bazie danych.
Podając @OneToMany(cascade = {CascadeType.ALL})
, informuje Hibernację, aby zapisał je w bazie danych podczas zapisywania obiektu nadrzędnego.
Dla kompletności: A
org.hibernate.TransientPropertyValueException
z wiadomością
object references an unsaved transient instance - save the transient instance before flushing
pojawi się również, gdy spróbujesz utrwalić / scalić byt z odniesieniem do innego bytu, który przypadkowo zostanie odłączony .
Jeszcze jeden możliwy powód: w moim przypadku próbowałem uratować dziecko przed uratowaniem rodzica, na zupełnie nowej jednostce.
Kod wyglądał mniej więcej tak w modelu User.java:
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();
Metoda setNewPassword () tworzy rekord PasswordHistory i dodaje go do kolekcji historii w User. Ponieważ instrukcja create () nie została jeszcze wykonana dla elementu nadrzędnego, próbowała zapisać w kolekcji encji, która nie została jeszcze utworzona. Wszystko, co musiałem zrobić, aby to naprawić, to przenieść wywołanie setNewPassword () po wywołaniu create ().
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);
Istnieje inna możliwość, która może spowodować ten błąd w trybie hibernacji. Możesz ustawić niezapisane odniesienie swojego obiektu A
do dołączonej encji B
i chcesz utrwalić obiekt C
. Nawet w tym przypadku otrzymasz wyżej wymieniony błąd.
Myślę, że dzieje się tak, ponieważ próbujesz utrwalić obiekt, który ma odwołanie do innego obiektu, który nie jest jeszcze trwały, i dlatego po stronie DB próbuje umieścić odniesienie do wiersza, który nie istnieje
Prostym sposobem rozwiązania tego problemu jest uratowanie obu podmiotów. najpierw zapisz element potomny, a następnie zapisz element macierzysty. Ponieważ jednostka nadrzędna jest zależna od jednostki podrzędnej dla wartości klucza obcego.
Poniżej prosty egzamin relacji jeden do jednego
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);
Jedną z możliwych przyczyn błędu jest nieistnienie ustawienia wartości jednostki dominującej; na przykład dla relacji dział-pracownik musisz to napisać, aby naprawić błąd:
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);
Jest tak wiele możliwości tego błędu, że inne możliwości znajdują się również na stronie dodawania lub edycji strony. W moim przypadku próbowałem zapisać obiekt AdvanceSalary. Problem polega na tym, że w edycji AdvanceSalary pracownik.employee_id ma wartość NULL, ponieważ podczas edycji nie ustawiłem pracownika.employee_id. Zrobiłem ukryte pole i ustawiłem je. mój kod działa absolutnie dobrze.
@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;
}
}
Spotykałem się z tym wyjątkiem, gdy nie utrwalałem obiektu nadrzędnego, ale zapisywałem dziecko. Aby rozwiązać problem, w tej samej sesji utrwaliłem zarówno obiekty podrzędne, jak i nadrzędne i użyłem CascadeType.ALL na rodzicu.
Przypadek 1: Otrzymałem ten wyjątek, gdy próbowałem utworzyć element nadrzędny i zapisać to odwołanie elementu nadrzędnego do jego elementu podrzędnego, a następnie inne zapytanie DELETE / UPDATE (JPQL). Więc po prostu opróżniam () nowo utworzony byt po utworzeniu elementu nadrzędnego i po utworzeniu elementu podrzędnego przy użyciu tego samego odwołania nadrzędnego. To zadziałało dla mnie.
Przypadek 2:
Klasa rodzicielska
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;
.
.
.
}
Klasa dla dzieci:
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;
.
.
}
W powyższym przypadku, gdy rodzic (Reference) i child (ReferenceAdditionalDetails) mają relację OneToOne, a gdy spróbujesz utworzyć obiekt Reference, a następnie jego obiekt potomny (ReferenceAdditionalDetails), otrzymasz ten sam wyjątek. Aby uniknąć wyjątku, musisz ustawić wartość null dla klasy potomnej, a następnie utworzyć element nadrzędny. (Przykładowy kod)
.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.
Mój problem dotyczył @BeforeEach
JUnit. I nawet jeśli zapisałem powiązane podmioty (w moim przypadku@ManyToOne
), dostałem ten sam błąd.
Problem jest w jakiś sposób związany z sekwencją, którą mam u rodzica. Jeśli przypiszę wartość do tego atrybutu, problem zostanie rozwiązany.
Dawny. Jeśli mam pytanie encji, które może mieć pewne kategorie (jedną lub więcej), a pytanie encji ma sekwencję:
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
@Id
private Long id;
Muszę przypisać wartość question.setId(1L);
Po prostu uczyń Konstruktorem swojego mapowania w swojej klasie podstawowej. Na przykład, jeśli chcesz relacji jeden-do-jednego w Podmiocie A, Podmiotu B., jeśli bierzesz A jako klasę podstawową, to A musi mieć Konstruktor jako argument B.