数据未保存:对象引用了未保存的瞬态实例 - 在刷新之前保存瞬态实例 [重复]

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 的回复,您还有第二个问题,即当您有两个具有相同CountryUser 对象时,您并不能确定它是相同的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 - 对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例

对象引用未保存的瞬态实例 - 在刷新休眠 JPA 之前保存瞬态实例 [重复]

对象引用未保存的瞬态实例:在刷新之前保存瞬态实例[重复]