Hibernate不允许保存引用未保存的瞬态实例的对象的原因是什么?



我是Hibernate的新手,我正在尝试学习JPA和Hibernate。

我想知道Hibernate不允许保存引用未保存的瞬态实例的对象的原因是什么?我想知道为什么这有问题吗?

我问了一些人,他们中的一些人这样回答我:

如果没有,我们怎么可能把客户映射到地址呢地址记录在数据库里了吗?

您正在为客户分配特定的地址。但是Address有没有任何ID

但老实说,我不能理解他们。

(我知道会抛出一个异常,解决方案是Cascade,但我想知道数据库中问题的原因)

现在,让我们假设我们有所有这些代码:

(我使用双向一对一关系为我的例子)

public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;

@OneToOne(mappedBy = "customer")
private Address address;
}

@Entity
public class Address { 
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String street;
private String zipCode;
@OneToOne
private Customer customer;
}
public static void main(String[] args) {
EntityManager entityManager = emf.createEntityManager();
entityManager.getTransaction().begin(); // Begin Transaction
Customer c1 = new Customer("Mi", "S");
Address addrss1 = new Address("5412 S 5th", "212524");
c1.setAddress(addrss1);
addrss1.setCustomer(c1);

entityManager.persist(c1);
entityManager.getTransaction().commit(); // Commit 
entityManager.close();
}

让我们假设异常没有被抛出java和hibernate允许我们运行代码这是我们的客户表.

id    firstName    lastName    
---------------------------------
1        Mi           S
这是我们的地址表:
id    street    zipCode    customer_id
---------------------------------------------
-      -         -            -

现在,问题是什么?在这种双向一对一的关系中,一切似乎都是正确的。那问题是什么?


PS:如果可能的话,请解释并告诉我代码。我可以用代码更好地理解。谢谢你。

我想看看,例如,如果我们允许保存引用未保存的瞬态实例的对象,我们将在代码和表中面临什么问题(例如,当我们想要检索客户等时,我们是否会遇到任何问题)

因为您的地址实体有客户的主键作为外键(因为mapappedby在客户实体中),并且地址引用的客户没有id,这告诉hibernate该实体从未在数据库中持久化(字面意思是短暂的),并且hibernate需要一个持久化/托管实体来确保它存在于数据库中,以便地址对象可以与现有客户相关联。

Customer是新的,从持久化调用中可以清楚地看到您想要插入它,但是不清楚您想要对任何Customer的引用发生什么。为了清楚起见,您需要定义在任何/所有情况下JPA提供程序(Hibernate)在映射中执行的操作—这就是级联操作所引用的内容。在这种情况下,JPA将查看客户。地址OneToOne映射,没有找到任何定义;地址不是在这个EntityManager上下文中管理的,所以它不知道该怎么处理这个关系,所以它通过抛出一个错误来表明你犯了一个错误。

如果它允许它通过,则您的Customer实例引用了不存在的东西,并且它的状态与数据库中的状态不匹配。传递给持久化的内容应该是读取时返回的内容,因此它应该反映数据库中的状态。

问题不直接与你的persist调用有关,因为规范允许提供者忽略对没有级联设置的分离/新实例的引用——发生了什么是未定义的。在这种情况下,出错的地方是在flush/commit,这是当持久化单元同步到数据库时(JPA 3.0的3.2.4节),这需要提供者遍历托管实体,然后确定任何更改。添加一个新的地址prepersist会导致与post persist相同的问题,并且要求提供商在发现新的或删除的实体并回滚事务时抛出一个IllegalStateException。

为什么这是一个问题:JPA对实体身份非常重要,因为这使得这些实体可以在多个级别的缓存中缓存,并且这个实体可能会进入那些缓存。它必须知道如何处理对不存在的实体的引用,规范决定需要一个异常。即使对你的应用来说,这也应该是一个问题,因为EntityManager上下文是一个工作单元,而那个工作单元中的状态是基于错误的东西。你的客户并没有真正拥有一个地址,但是你的应用程序业务逻辑认为它已经分配了一个地址,而这个地址之后就不会存在了。

你已经知道答案了:

  • 通过在同一个EntityManager上下文中直接调用persist来纠正客户具有有效的托管地址。
  • 将映射上的级联选项设置为级联持久到您的地址
  • 不允许在同一操作中为新客户设置地址。

最新更新