Tôi nhận được lỗi sau khi lưu đối tượng bằng Hibernate
object references an unsaved transient instance - save the transient instance before flushing
Tôi nhận được lỗi sau khi lưu đối tượng bằng Hibernate
object references an unsaved transient instance - save the transient instance before flushing
Câu trả lời:
Bạn nên bao gồm cascade="all"
(nếu sử dụng xml) hoặc cascade=CascadeType.ALL
(nếu sử dụng chú thích) trên ánh xạ bộ sưu tập của bạn.
Điều này xảy ra bởi vì bạn có một bộ sưu tập trong thực thể của mình và bộ sưu tập đó có một hoặc nhiều mục không có trong cơ sở dữ liệu. Bằng cách chỉ định các tùy chọn ở trên, bạn bảo ngủ đông để lưu chúng vào cơ sở dữ liệu khi lưu cha mẹ của chúng.
Tôi tin rằng đây có thể chỉ là câu trả lời lặp lại, nhưng chỉ để làm rõ, tôi đã nhận được điều này trên @OneToOne
bản đồ cũng như a @OneToMany
. Trong cả hai trường hợp, thực tế là Child
đối tượng tôi đã thêm vào Parent
chưa được lưu trong cơ sở dữ liệu. Vì vậy, khi tôi thêm Child
vào Parent
, sau đó lưu Parent
, Hibernate sẽ quăng "object references an unsaved transient instance - save the transient instance before flushing"
nhắn khi lưu Phụ Huynh.
Thêm cascade = {CascadeType.ALL}
vào các Parent's
tham chiếu để Child
giải quyết vấn đề trong cả hai trường hợp. Điều này đã lưu Child
và Parent
.
Xin lỗi cho bất kỳ câu trả lời lặp lại, chỉ muốn làm rõ thêm cho mọi người.
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
return performanceLog;
}
new MyEntity
(mà không đồng bộ hóa nó với cơ sở dữ liệu - xóa), thay vì lấy đối tượng được đồng bộ hóa từ cơ sở dữ liệu. Thực hiện các truy vấn Hibernate bằng cách sử dụng ví dụ đó thông báo cho bạn rằng những gì bạn mong đợi có trong cơ sở dữ liệu khác với những gì bạn có trong bộ nhớ ứng dụng của mình. Trong trường hợp này - chỉ cần đồng bộ hóa / nhận thực thể của bạn từ DB và sử dụng nó. Không có CascadeType.ALL là cần thiết sau đó.
Điều này xảy ra khi lưu một đối tượng khi Hibernate nghĩ rằng nó cần lưu một đối tượng được liên kết với đối tượng bạn đang lưu.
Tôi gặp vấn đề này và không muốn lưu các thay đổi vào đối tượng được tham chiếu nên tôi muốn loại tầng là KHÔNG.
Bí quyết là đảm bảo rằng ID và VERSION trong đối tượng được tham chiếu được đặt để Hibernate không nghĩ rằng đối tượng được tham chiếu là một đối tượng mới cần lưu. Điều này làm việc cho tôi.
Xem xét tất cả các mối quan hệ trong lớp bạn đang lưu để tìm ra các đối tượng liên quan (và các đối tượng liên quan của các đối tượng được liên kết) và đảm bảo rằng ID và VERSION được đặt trong tất cả các đối tượng của cây đối tượng.
Như tôi đã giải thích trong bài viết này khi sử dụng JPA và Hibernate, một thực thể có thể ở một trong 4 trạng thái sau:
Mới - Một đối tượng mới được tạo chưa từng được liên kết với Phiên Hibernate (còn gọi là Bối cảnh liên tục) và không được ánh xạ tới bất kỳ hàng trong bảng cơ sở dữ liệu nào được coi là ở trạng thái Mới hoặc Tạm thời.
Để trở nên bền bỉ, chúng ta cần gọi persist
phương thức một cách rõ ràng hoặc sử dụng cơ chế duy trì bắc cầu.
Kiên trì - Một thực thể liên tục đã được liên kết với một hàng của bảng cơ sở dữ liệu và nó được quản lý bởi Bối cảnh liên tục hiện đang chạy.
Bất kỳ thay đổi nào được thực hiện cho một thực thể như vậy sẽ được phát hiện và lan truyền đến cơ sở dữ liệu (trong thời gian tuôn ra Phiên).
Tách rời - Khi Bối cảnh bền bỉ hiện đang chạy, tất cả các thực thể được quản lý trước đó sẽ bị tách ra. Những thay đổi kế tiếp sẽ không còn được theo dõi và sẽ không xảy ra đồng bộ hóa cơ sở dữ liệu tự động.
Đã xóa - Mặc dù JPA yêu cầu các thực thể được quản lý chỉ được phép xóa, Hibernate cũng có thể xóa các thực thể tách rời (nhưng chỉ thông qua một remove
cuộc gọi phương thức).
Để di chuyển một thực thể từ một tiểu bang khác, bạn có thể sử dụng persist
, remove
hoặc merge
phương pháp.
Vấn đề bạn đang mô tả trong câu hỏi của bạn:
object references an unsaved transient instance - save the transient instance before flushing
được gây ra bằng cách liên kết một thực thể ở trạng thái Mới với một thực thể ở trạng thái Được quản lý .
Điều này có thể xảy ra khi bạn liên kết một thực thể con với một bộ sưu tập một-nhiều trong thực thể cha và bộ sưu tập không cascade
chuyển trạng thái thực thể.
Vì vậy, như tôi đã giải thích trong bài viết này , bạn có thể khắc phục điều này bằng cách thêm tầng vào liên kết thực thể đã gây ra lỗi này, như sau:
@OneToOne
hiệp hội@OneToOne(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private PostDetails details;
Lưu ý
CascadeType.ALL
giá trị chúng tôi đã thêm chocascade
thuộc tính.
@OneToMany
hiệp hội@OneToMany(
mappedBy = "post",
orphanRemoval = true,
cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
Một lần nữa, CascadeType.ALL
phù hợp cho các hiệp hội hai chiều@OneToMany
.
Bây giờ, để tầng hoạt động chính xác theo hướng hai chiều, bạn cũng cần đảm bảo rằng các hiệp hội cha mẹ và con cái được đồng bộ hóa.
Kiểm tra bài viết này để biết thêm chi tiết về cách tốt nhất để đạt được mục tiêu này là gì.
@ManyToMany
hiệp hội@ManyToMany(
mappedBy = "authors",
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}
)
private List<Book> books = new ArrayList<>();
Trong một @ManyToMany
liên kết, bạn không thể sử dụng CascadeType.ALL
hoặc orphanRemoval
vì điều này sẽ truyền sự chuyển đổi trạng thái thực thể từ cha mẹ này sang thực thể cha mẹ khác.
Do đó, đối với các @ManyToMany
hiệp hội, bạn thường xếp tầng CascadeType.PERSIST
hoặc các CascadeType.MERGE
hoạt động. Ngoài ra, bạn có thể mở rộng đó đến DETACH
hoặc REFRESH
.
Để biết thêm chi tiết về cách tốt nhất để lập bản đồ
@ManyToMany
liên kết, hãy xem bài viết này .
Hoặc, nếu bạn muốn sử dụng "quyền hạn" tối thiểu (ví dụ: nếu bạn không muốn xóa tầng) để đạt được những gì bạn muốn, hãy sử dụng
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
...
@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
@ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})
(tất cả trừ XÓA) không xếp tầng các bản cập nhật như của Hibernate CascadeType.SAVE_UPDATE
.
Trong trường hợp của tôi nó được gây ra bởi không có CascadeType
trên @ManyToOne
mặt của mối quan hệ hai chiều. Nói chính xác hơn, tôi đã CascadeType.ALL
ở @OneToMany
bên và không có nó trên @ManyToOne
. Thêm vào CascadeType.ALL
để @ManyToOne
giải quyết vấn đề.
Một bên nhiều bên:
@OneToMany(cascade = CascadeType.ALL, mappedBy="globalConfig", orphanRemoval = true)
private Set<GlobalConfigScope>gcScopeSet;
Nhiều phía (gây ra vấn đề)
@ManyToOne
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Nhiều-một (cố định bằng cách thêm CascadeType.PERSIST
)
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;
Điều này xảy ra với tôi khi duy trì một thực thể trong đó bản ghi hiện có trong cơ sở dữ liệu có giá trị NULL cho trường được chú thích bằng @Version (để khóa tối ưu). Cập nhật giá trị NULL thành 0 trong cơ sở dữ liệu đã sửa lỗi này.
Đây không phải là lý do duy nhất cho lỗi. Tôi đã gặp nó ngay bây giờ vì một lỗi đánh máy trong mã hóa của tôi, mà tôi tin rằng, đặt giá trị của một thực thể đã được lưu.
X x2 = new X();
x.setXid(memberid); // Error happened here - x was a previous global entity I created earlier
Y.setX(x2);
Tôi phát hiện ra lỗi bằng cách tìm chính xác biến nào gây ra lỗi (trong trường hợp này String xid
). Tôi đã sử dụng catch
xung quanh toàn bộ khối mã đã lưu thực thể và in dấu vết.
{
code block that performed the operation
} catch (Exception e) {
e.printStackTrace(); // put a break-point here and inspect the 'e'
return ERROR;
}
Đừng sử dụng Cascade.All
cho đến khi bạn thực sự phải làm. Role
và Permission
có manyToMany
quan hệ hai chiều . Sau đó, đoạn mã sau sẽ hoạt động tốt
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);
trong khi nếu đối tượng chỉ là một "mới", thì nó sẽ đưa ra cùng một lỗi.
Nếu bộ sưu tập của bạn là nullable, hãy thử: object.SetYouColection(null);
Để thêm 2 xu của tôi, tôi đã gặp vấn đề tương tự khi tôi vô tình gửi null
dưới dạng ID. Mã bên dưới mô tả kịch bản của tôi (và OP không đề cập đến bất kỳ kịch bản cụ thể nào) .
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);
Ở đây tôi đang đặt id bộ phận hiện tại thành một cá thể nhân viên mới mà không thực sự có được thực thể bộ phận trước, vì tôi không muốn truy vấn chọn khác để kích hoạt.
Trong một số trường hợp, deptId
PKID xuất phát null
từ phương thức gọi và tôi gặp lỗi tương tự.
Vì vậy, hãy theo dõi các null
giá trị cho PK ID
Vấn đề này xảy ra với tôi khi tôi tạo một thực thể mới và một thực thể liên quan trong một phương thức được đánh dấu là @Transactional
, sau đó thực hiện một truy vấn trước khi lưu. Ví dụ
@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);
}
Để khắc phục, tôi đã thực hiện truy vấn trước khi tạo thực thể mới.
bên cạnh tất cả các câu trả lời hay khác, điều này có thể xảy ra nếu bạn sử dụng merge
để duy trì một đối tượng và vô tình quên sử dụng tham chiếu hợp nhất của đối tượng trong lớp cha. xem xét ví dụ sau
merge(A);
B.setA(A);
persist(B);
Trong trường hợp này, bạn hợp nhất A
nhưng quên sử dụng đối tượng hợp nhất của A
. để giải quyết vấn đề bạn phải viết lại mã như thế này.
A=merge(A);//difference is here
B.setA(A);
persist(B);
Tôi cũng phải đối mặt với tình huống tương tự. Bằng cách thiết lập chú thích sau trên thuộc tính làm cho nó giải quyết ngoại lệ được nhắc.
Ngoại lệ tôi phải đối mặt.
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
Để khắc phục, chú thích tôi đã sử dụng.
@OneToMany(cascade = {CascadeType.ALL})
@Column(name = "ListOfCarsDrivenByDriver")
private List<Car_OneToMany> listOfCarsBeingDriven = new ArrayList<Car_OneToMany>();
Điều gì đã khiến Hibernate ném ngoại lệ:
Ngoại lệ này được ném vào bàn điều khiển của bạn vì đối tượng con tôi đính kèm với đối tượng cha không có trong cơ sở dữ liệu tại thời điểm đó.
Bằng cách cung cấp @OneToMany(cascade = {CascadeType.ALL})
, nó báo cho Hibernate lưu chúng vào cơ sở dữ liệu trong khi lưu đối tượng cha.
Vì lợi ích của sự hoàn chỉnh: A
org.hibernate.TransientPropertyValueException
với tin nhắn
object references an unsaved transient instance - save the transient instance before flushing
cũng sẽ xảy ra khi bạn cố gắng duy trì / hợp nhất một thực thể với một tham chiếu đến một thực thể khác có thể bị tách ra .
Một lý do có thể khác: trong trường hợp của tôi, tôi đã cố gắng cứu đứa trẻ trước khi cứu cha mẹ, trên một thực thể hoàn toàn mới.
Mã này giống như thế này trong mô hình User.java:
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();
Phương thức setNewPassword () tạo bản ghi PasswordHistory và thêm nó vào bộ sưu tập lịch sử trong Người dùng. Vì câu lệnh tạo () chưa được thực thi cho cha mẹ, nên nó đã cố lưu vào bộ sưu tập của một thực thể chưa được tạo. Tất cả những gì tôi phải làm để khắc phục nó là di chuyển cuộc gọi setNewPassword () sau cuộc gọi để tạo ().
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);
Có một khả năng khác có thể gây ra lỗi này trong chế độ ngủ đông. Bạn có thể đặt tham chiếu chưa được lưu của đối tượng của mình A
thành một thực thể đính kèm B
và muốn duy trì đối tượng C
. Ngay cả trong trường hợp này, bạn sẽ nhận được lỗi đã nói ở trên.
Tôi nghĩ là bởi vì bạn đã cố gắng duy trì một đối tượng có tham chiếu đến một đối tượng khác chưa tồn tại và vì vậy nó cố gắng ở "bên DB" để đặt tham chiếu đến một hàng không tồn tại
Cách đơn giản để giải quyết vấn đề này là lưu cả hai thực thể. đầu tiên lưu thực thể con và sau đó lưu thực thể cha. Bởi vì thực thể cha mẹ phụ thuộc vào thực thể con cho giá trị khóa ngoài.
Dưới đây kiểm tra đơn giản mối quan hệ 1-1
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);
Một nguyên nhân có thể gây ra lỗi là sự không tồn tại của cài đặt giá trị của thực thể mẹ; ví dụ: đối với mối quan hệ nhân viên phòng ban, bạn phải viết điều này để sửa lỗi:
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);
Có rất nhiều khả năng của lỗi này, một số khả năng khác cũng có trên trang thêm hoặc trang chỉnh sửa. Trong trường hợp của tôi, tôi đã cố gắng cứu một đối tượng AdvanceSalary. Vấn đề là trong chỉnh sửa nhân viên AdvanceSalary.employee_id là null Bởi vì khi chỉnh sửa tôi không đặt nhân viên.employee_id. Tôi đã thực hiện một trường ẩn và thiết lập nó. mã của tôi hoạt động hoàn toàn tốt.
@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;
}
}
Tôi đã phải đối mặt với ngoại lệ này khi tôi không kiên trì đối tượng cha mẹ nhưng tôi đang cứu đứa trẻ. Để giải quyết vấn đề, trong cùng một phiên, tôi đã duy trì cả đối tượng con và đối tượng cha mẹ và sử dụng CascadeType.ALL trên cha mẹ.
Trường hợp 1: Tôi đã nhận được ngoại lệ này khi tôi đang cố gắng tạo cha mẹ và lưu tham chiếu cha mẹ đó cho con của nó và sau đó một số truy vấn XÓA / CẬP NHẬT khác (JPQL). Vì vậy, tôi chỉ tuôn ra () thực thể mới được tạo sau khi tạo cha mẹ và sau khi tạo con bằng cách sử dụng cùng tham chiếu cha. Nó làm việc cho tôi.
Trường hợp 2:
Lớp phụ huynh
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;
.
.
.
}
Lớp trẻ em:
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;
.
.
}
Trong trường hợp trên, khi cha mẹ (Tham chiếu) và con (ReferenceAdditableDetails) có mối quan hệ OneToOne và khi bạn cố gắng tạo thực thể Tham chiếu và sau đó là con của nó (ReferenceAdditableDetails), nó sẽ cung cấp cho bạn ngoại lệ tương tự. Vì vậy, để tránh ngoại lệ, bạn phải đặt null cho lớp con và sau đó tạo cha mẹ. (Mã mẫu)
.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.
Vấn đề của tôi liên quan đến @BeforeEach
JUnit. Và ngay cả khi tôi đã lưu các thực thể liên quan (trong trường hợp của tôi @ManyToOne
), tôi vẫn gặp lỗi tương tự.
Vấn đề là bằng cách nào đó liên quan đến trình tự mà tôi có trong cha mẹ tôi. Nếu tôi gán giá trị cho thuộc tính đó, vấn đề sẽ được giải quyết.
Ví dụ. Nếu tôi có Câu hỏi thực thể có thể có một số loại (một hoặc nhiều) và Câu hỏi thực thể có một chuỗi:
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
@Id
private Long id;
Tôi phải gán giá trị question.setId(1L);
Chỉ cần tạo Con Contor của ánh xạ trong lớp cơ sở của bạn. Giống như nếu bạn muốn quan hệ Một-Một trong Thực thể A, Thực thể B. nếu bạn đang lấy A làm lớp cơ sở, thì A phải có Trình xây dựng có B làm đối số.