您应该在集合映射中包含 cascade="all"
(如果使用 xml)或 cascade=CascadeType.ALL
(如果使用注释)。
发生这种情况是因为您的实体中有一个集合,并且该集合具有一个或多个数据库中不存在的项目。通过指定上述选项,您可以告诉 hibernate 在保存它们的父对象时将它们保存到数据库中。
我相信这可能只是重复的答案,但为了澄清,我在 @OneToOne
映射和 @OneToMany
上得到了这个。在这两种情况下,事实上我添加到 Parent
的 Child
对象还没有保存在数据库中。因此,当我将 Child
添加到 Parent
,然后保存 Parent
时,Hibernate 会在保存 Parent 时抛出 "object references an unsaved transient instance - save the transient instance before flushing"
消息。
在 Parent's
上添加 cascade = {CascadeType.ALL}
对 Child
的引用解决了这两种情况下的问题。这保存了 Child
和 Parent
。
抱歉有任何重复的答案,只是想为人们进一步澄清。
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
return performanceLog;
}
new MyEntity
创建了实体对象(没有将其同步到数据库 - 刷新),而不是从数据库中获取其同步实例。使用该实例进行 Hibernate 查询会通知您,您期望在数据库中的内容与您在应用程序内存中的内容不同。在这种情况下 - 只需从数据库同步/获取您的实体实例并使用它。那时不需要 CascadeType.ALL。
介绍
使用 JPA 和 Hibernate 时,实体可以处于以下 4 种状态之一:
New - 从未与 Hibernate Session(又名 Persistence Context)关联且未映射到任何数据库表行的新创建的对象被视为处于 New 或 Transient 状态。
要持久化,我们需要显式调用 persist
方法或使用传递持久性机制。
持久性 - 持久性实体已与数据库表行相关联,并且由当前运行的持久性上下文管理。
对此类实体所做的任何更改都将被检测到并传播到数据库(在会话刷新期间)。
Detached - 一旦当前运行的 Persistence Context 关闭,所有以前管理的实体都将被分离。将不再跟踪连续的更改,也不会发生自动数据库同步。
Removed - 尽管 JPA 要求只允许删除托管实体,但 Hibernate 也可以删除分离的实体(但只能通过 remove 方法调用)。
实体状态转换
要将实体从一种状态移动到另一种状态,您可以使用 persist
、remove
或 merge
方法。
https://i.stack.imgur.com/QSdMl.png
解决问题
您在问题中描述的问题:
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;
注意我们为 cascade 属性添加的 CascadeType.ALL 值。
@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
。
当 Hibernate 认为它需要保存与您正在保存的对象相关联的对象时,会在保存对象时发生这种情况。
我遇到了这个问题,不想保存对引用对象的更改,所以我希望级联类型为 NONE。
诀窍是确保设置了被引用对象中的 ID 和 VERSION,以便 Hibernate 不会认为被引用对象是需要保存的新对象。这对我有用。
查看您正在保存的类中的所有关系以计算出关联对象(以及关联对象的关联对象),并确保在对象树的所有对象中设置了 ID 和 VERSION。
或者,如果您想使用最小的“权力”(例如,如果您不想要级联删除)来实现您想要的,请使用
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
那样级联更新。
在我的情况下,这是由于双向关系的 @ManyToOne
端没有 CascadeType
引起的。更准确地说,我在 @OneToMany
一侧有 CascadeType.ALL
而在 @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;
这发生在我持久化一个实体时,其中数据库中的现有记录对于带有 @Version 注释的字段具有 NULL 值(用于乐观锁定)。将数据库中的 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);
当我在标记为 @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);
为了增加我的 2 美分,当我不小心将 null
作为 ID 发送时,我遇到了同样的问题。下面的代码描述了我的场景(并且 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);
在这里,我将现有的部门 ID 设置为新的员工实例,而实际上并没有首先获取部门实体,因为我不想触发另一个选择查询。
在某些情况下,deptId
PKID 作为 null
来自调用方法,我得到了同样的错误。
因此,请注意 PK ID 的 null
个值
我也面临同样的情况。通过在属性上方设置以下注释使其解决了提示的异常。
我面临的异常。
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 在保存父对象的同时将它们保存到数据库中。
当您具有 OneToMany 关系并且您尝试将子实体添加到父实体中的列表时,也会发生这种情况,然后通过父实体检索此列表(在保存此父实体之前),而不保存子实体本身,例如:
Child childEntity = new Child();
parentEntity.addChild(childEntity);
parentEntity.getChildren(); // I needed the retrieval for logging, but one may need it for other reasons.
parentRepository.save(parentEntity);
保存父实体时引发错误。如果我删除了上一行中的检索,则不会引发错误,但这当然不是解决方案。
解决方案是保存 childEntity 并将保存的子实体添加到父实体,如下所示:
Child childEntity = new Child();
Child savedChildEntity = childRepository.save(childEntity);
parentEntity.addChild(savedChildEntity);
parentEntity.getChildren();
parentRepository.save(parentEntity);
我使用时收到此错误
getSession().save(object)
但是当我使用时它没有问题
getSession().saveOrUpdate(object)
为了完整起见:A
org.hibernate.TransientPropertyValueException
带消息
object references an unsaved transient instance - save the transient instance before flushing
当您尝试将实体与对碰巧分离的另一个实体的引用进行持久化/合并时,也会发生这种情况。
如果您使用的是 Spring Data JPA,那么在您的服务实现中添加 @Transactional
注释将解决该问题。
另一个可能的原因:在我的情况下,我试图在一个全新的实体上拯救父母之前拯救孩子。
User.java 模型中的代码是这样的:
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();
setNewPassword() 方法创建一个 PasswordHistory 记录并将其添加到 User 中的历史记录集合中。由于尚未为父级执行 create() 语句,它试图保存到尚未创建的实体的集合中。我所要做的就是在调用 create() 之后移动 setNewPassword() 调用。
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);
在hibernate中还有另一种可能导致此错误。您可以将对象 A
的未保存引用设置为附加实体 B
,并希望保留对象 C
。即使在这种情况下,您也会收到上述错误。
此错误有很多可能性,添加页面或编辑页面上也有一些其他可能性。就我而言,我试图保存一个对象 AdvanceSalary。问题是在编辑时 AdvanceSalary employee.employee_id 为空 因为在编辑时我没有设置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;
}
}
我认为是因为您尝试持久化一个对象,该对象具有对另一个尚未持久化的对象的引用,因此它尝试在“数据库端”中引用不存在的行
案例 1:当我尝试创建父级并将该父级引用保存到其子级以及其他一些 DELETE/UPDATE 查询(JPQL)时,我遇到了这个异常。所以我只是在创建父对象和使用相同的父引用创建子对象之后刷新()新创建的实体。它对我有用。
案例二:
家长班
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;
.
.
}
在上述情况下,父(参考)和子(参考附加详细信息)具有 OneToOne 关系,并且当您尝试创建参考实体及其子(参考附加详细信息)时,它会给您同样的异常。因此,为了避免异常,您必须为子类设置 null 然后创建父类。(示例代码)
.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.
就我而言,问题完全不同。我有两个课程,比如说 c1 和 c2。 C1 和 C2 之间的依赖是 OneToMany。现在,如果我将 C1 保存在 DB 中,则会引发上述错误。
这个问题的解决方法是从消费者请求中获取第一个 C2 的 id 并通过存储库调用找到 C2。然后将 c2 保存到 C1 对象中。现在如果我保存 C1,它工作正常。
在引入乐观锁定(@Version)之后,我在所有 PUT HTTP 事务中都面临同样的错误
在更新实体时,必须发送该实体的 ID 和版本。如果任何实体字段与其他实体相关,那么对于该字段,我们还应该提供 id 和版本值,而不是 JPA 尝试首先将该相关实体作为新实体持久化
示例:我们有两个实体 --> Vehicle(id,Car,version) ;汽车(id、版本、品牌);更新/保留车辆实体确保车辆实体中的 Car 字段提供了 id 和 version 字段
解决此问题的简单方法是保存两个实体。首先保存子实体,然后保存父实体。因为父实体依赖于外键值的子实体。
下面是一对一关系的简单考试
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);
当我没有坚持父对象但我正在拯救孩子时,我遇到了这个异常。为了解决这个问题,我在同一个会话中保留了子对象和父对象,并在父对象上使用了 CascadeType.ALL。
我的问题与 JUnit 的 @BeforeEach
有关。即使我保存了相关实体(在我的情况下为 @ManyToOne
),我也得到了同样的错误。
这个问题在某种程度上与我父母的顺序有关。如果我将值分配给该属性,问题就解决了。
前任。如果我有可以有一些类别(一个或多个)的实体 Question 并且实体 Question 有一个序列:
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
@Id
private Long id;
我必须分配值 question.setId(1L);
只需在基类中创建映射的构造函数即可。就像如果您想要实体 A、实体 B 中的一对一关系。如果您将 A 作为基类,那么 A 必须有一个构造函数,并以 B 作为参数。
不定期副业成功案例分享