18

我有一个包含两个表用户和国家的数据库。我想建立许多用户可以属于一个县的关系。我使用以下模型类使用休眠来实现这一点:

@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

我该如何解决这个问题?

4

7 回答 7

26

在您还告诉 Hibernate 这个新保存的对象引用的所有其他对象之前,您不能将内容保存到 Hibernate。所以在这种情况下,你告诉 Hibernate 关于 a User,但没有告诉它关于Country.

您可以通过两种方式解决此类问题。

手动

session.save(country)在保存之前调用User

级联类型

您可以向 Hibernate 指定这种关系应该使用CascadeType传播一些操作。在这种情况下,CascadeType.PERSIST 可以完成这项工作,CascadeType.ALL 也可以。

参考现有国家

不过,根据您对@zerocool 的回复,您还有第二个问题,即当您有两个User具有相同 的对象时Country,您并不能确保它是相同 Country的。为此,您必须Country从数据库中获取适当的,在新用户上设置它,然后保存User. 然后,您的两个User对象都将引用相同的Country,而不仅仅是Country碰巧具有相同名称的两个实例。查看CriteriaAPI作为获取现有实例的一种方式。

于 2013-06-24T20:44:17.510 回答
13

看起来在您的 Country 对象中添加的用户尚未出现在数据库中。您需要使用级联来确保当 Country 被持久化时,所有不在数据中但与 Country 关联的 User 也会被持久化。

下面的代码应该有帮助:

@ManyToOne (cascade = CascadeType.ALL)
   @JoinColumn (name = "countryId")
   private Country country;
于 2013-06-24T19:29:13.510 回答
7

为了增加我的 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 设置为新的员工实例,而实际上并没有首先获取部门实体,因为我不想触发另一个选择查询。

在某些情况下,deptIdPKIDnull来自调用方法,我得到了同样的错误。

因此,请注意nullPK ID 的值

这里给出了相同的答案

于 2016-07-01T11:06:45.833 回答
3

我有同样的问题。在我的情况下,它出现了,因为查找表“国家”具有 countryId==0 和原始主键的现有记录,并且我尝试使用 countryID==0 保存用户。将国家的主键更改为整数。现在 Hibernate 可以识别新记录。

有关使用包装类作为主键的建议,请参阅这个 stackoverflow 问题

于 2013-11-24T14:22:08.083 回答
0

好吧,如果你给了

@ManyToOne ()
   @JoinColumn (name = "countryId")
   private Country country;

然后那个类的对象我的意思是国家需要首先被保存。

因为它只允许用户保存到数据库中,前提是该用户的国家/地区有可用的密钥。意味着当且仅当该国家/地区存在于 Country 表中时,它将允许保存用户。

因此,您需要先将该国家/地区保存到表格中。

于 2014-12-31T10:23:09.113 回答
0

它应该是 CascadeType.Merge,在这种情况下,如果记录已经存在,它将更新。

于 2014-07-12T17:55:17.373 回答
0

这是因为CASCADE TYPE

如果你把

@OneToOne(级联=CascadeType.ALL)

你可以像这样保存你的对象

user.setCountry(country);
session.save(user)

但如果你把

 @OneToOne(cascade={    
            CascadeType.PERSIST,
            CascadeType.REFRESH,
            ...
})

你需要像这样保存你的对象

user.setCountry(country);
session.save(country)
session.save(user)
于 2018-09-24T08:00:29.987 回答