数据未保存:对象引用了未保存的瞬态实例 - 在刷新之前保存瞬态实例 [重复]
Posted
技术标签:
【中文标题】数据未保存:对象引用了未保存的瞬态实例 - 在刷新之前保存瞬态实例 [重复]【英文标题】:Data was not saved: object references an unsaved transient instance - save the transient instance before flushing [duplicate] 【发布时间】:2013-06-21 10:18:26 【问题描述】:我有一个包含两个表用户和国家的数据库。我想建立许多用户可以属于一个县的关系。我使用以下模型类使用休眠来实现这一点:
@Entity (name = "user")
public class User
@Id @GeneratedValue (strategy = GenerationType.IDENTITY)
private int userId;
private String username;
private String password;
@ManyToOne ()
@JoinColumn (name = "countryId")
private Country country;
//Getter & Setter
@Entity (name = "country")
public class Country
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private int countryId;
private String countryName;
//Getter & Setter
当我尝试保存用户对象时,出现以下异常:
org.hibernate.HibernateException: Data was not saved: object references an unsaved transient instance - save the transient instance before flushing: com.kyrogaming.models.Country
我该如何解决这个问题?
【问题讨论】:
保留用户对象然后设置国家,然后调用保存或合并到用户中 【参考方案1】:在您还告诉 Hibernate 这个新保存的对象引用的所有其他对象之前,您无法将内容保存到 Hibernate。所以在这种情况下,你告诉 Hibernate 一个 User
,但没有告诉它 Country
。
您可以通过两种方式解决此类问题。
手动
在保存User
之前,请致电session.save(country)
。
CascadeType
您可以向 Hibernate 指定这种关系应该使用CascadeType 传播一些操作。在这种情况下,CascadeType.PERSIST 可以完成这项工作,CascadeType.ALL 也可以。
参考现有国家/地区
不过,根据您对@zerocool 的回复,您还有第二个问题,即当您有两个具有相同Country
的User
对象时,您并不能确定它是相同的Country
。为此,您必须从数据库中获取适当的Country
,将其设置为新用户,然后保存User
。然后,您的两个User
对象将引用相同的Country
,而不仅仅是两个碰巧具有相同名称的Country
实例。查看Criteria
API 作为获取现有实例的一种方式。
【讨论】:
如果我必须使用 Criteria API 来确保正确保存所有内容,那么 ManyToOne 注释的意义何在? 您使用 Criteria API 从持久性上下文中获取您想要引用的对象的实例:换句话说,生成和运行 SQL。您使用 ManyToOne 注释来告诉 Hibernate 如何在数据库中建模两个表之间的关系。它们是相关的概念,但不能相互替代。【参考方案2】:看起来添加到您的 Country 对象中的用户尚未出现在数据库中。您需要使用级联来确保当 Country 被持久化时,所有不在数据中但与 Country 关联的 User 也会被持久化。
下面的代码应该会有所帮助:
@ManyToOne (cascade = CascadeType.ALL)
@JoinColumn (name = "countryId")
private Country country;
【讨论】:
我将 CascadeType.ALL 添加到 @ManyToOne 注释中,这解决了问题,但现在它保存了一个新的国家/地区实例,即使该国家/地区已准备好存储在该国家/地区。我该如何解决这个新问题? 同样的原因可能有很多。现在看起来你没有设置 Country 的主键。 我使用 generateValue 注释来自动生成国家表的主键,这会导致我目前遇到的问题 你好,拜托,我也有同样的问题***.com/questions/18895585/…这个有什么解决办法吗? 通常不建议在子项中添加级联。否则,您最终将拥有多个相同的父母。【参考方案3】:为了增加我的 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
,我得到了同样的错误。
因此,请注意null
PK ID 的值
给出了相同的答案here
【讨论】:
是的,这解决了问题。 emp.setDept((emp.getDept().getDeptId != null) ? new Dept(emp.getDept().getDeptId) : null);我们从 UI 中获取,我们需要显式地将属性设置为 null 以使 hibernate 快乐。:-)【参考方案4】:我遇到了同样的问题。在我的情况下,它出现了,因为查找表“国家/地区”具有 countryId==0 和原始主键的现有记录,并且我尝试使用 countryID==0 保存用户。将国家的主键更改为整数。现在 Hibernate 可以识别新记录。
有关使用包装类作为主键的建议,请参阅this *** question
【讨论】:
【参考方案5】:应该是CascadeType.Merge,如果记录已经存在就会更新。
【讨论】:
【参考方案6】:如果你给了
@ManyToOne ()
@JoinColumn (name = "countryId")
private Country country;
然后那个类的对象我的意思是国家需要首先被保存。
因为它只允许用户保存到数据库中,前提是该用户的国家/地区有可用的密钥。表示当且仅当 Country 表中存在该国家/地区时,它将允许保存用户。
因此,您需要先将该国家/地区保存到表格中。
【讨论】:
【参考方案7】:这是因为CASCADE TYPE
如果你放
@OneToOne(cascade=CascadeType.ALL)
你可以像这样保存你的对象
user.setCountry(country);
session.save(user)
但如果你放
@OneToOne(cascade=
CascadeType.PERSIST,
CascadeType.REFRESH,
...
)
你需要像这样保存你的对象
user.setCountry(country);
session.save(country)
session.save(user)
【讨论】:
以上是关于数据未保存:对象引用了未保存的瞬态实例 - 在刷新之前保存瞬态实例 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
org.hibernate.TransientObjectException:对象引用了未保存的瞬态实例 - 在刷新之前保存瞬态实例
HIbernate - 对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例