如果保存了引用的对象,应该更新引用的对象吗?

Posted

技术标签:

【中文标题】如果保存了引用的对象,应该更新引用的对象吗?【英文标题】:Referencing object should be updated if referenced object is saved? 【发布时间】:2019-07-07 22:56:25 【问题描述】:

想象以下情况:我们有两个数据库表,TenantHouse。租户使用 @ManyToOne 映射引用 House。

Tenant tenant = tenantRepository.findById(id).orElseThrow();
House house = tenant.getHouse();

house.setPrice(340_000);
house = houseRepository.save(house); // A new instance is returned by the CrudRepository::save() method

// TODO Is this necessary for further use?
tenant.setHouse(house);

// Further use...
tenant.setAge(23);
tenant = tenantRepository.save(tenant); // Otherwise it is saved with the old reference where house's ID can be null?
...

是否需要将租户更新为 House 的新引用?

编辑:为澄清起见,您可以假设实体在上述代码之前立即加载(因此处于托管状态)。并且因为这个“事务”是 Spring @RequestMapping 函数的一部分,所以事务将在它的末尾隐式提交。

编辑2:问题是我是否应该在一开始就拯救房子以避免这种情况。这是为了更好地理解对象的管理方式。

---但是你也可以告诉我,我应该先更新所有内容,然后保存,作为一种常见做法吗?子>

【问题讨论】:

A new instance is returned ... 只是部分正确。应该是The saved instance is returned ...。然后,the saved instance 依赖于 JPA 提供程序。例如,Hibernate 返回传递给save 方法的相同对象(直到当前版本),而其他提供者可能完全返回不同的对象。这就是 JPA 规范建议使用从 save 调用返回的实例的原因。如果您想与提供者无关或与 Hibernate 以外的任何东西相关联,则应在保存 house 后调用 tenant.setHouse(house) JPA 规范实际上是指persistmerge 方法。 JPA API 中没有save 很抱歉,为什么不发布理解您的问题所需的完整代码呢?在不知道这些关心现有实体还是新实体的情况下,没有人可以给出明智的答案 @BillyFrost 我说的是 Spring Data JPA 和 CrudRepository,它们是 JPA 之上的另一层。 显然 ... JPA 规范中没有提到。 【参考方案1】:

关键问题是housetenant 是否已经管理实体?

如果是(因为它们被加载到仍在运行的同一事务中)所有涉及的House 实例都是相同的,您不需要在tenant 中设置house。 但在这种情况下,您甚至都不需要调用 save 方法。

如果它们是分离的实例,是的,您需要致电tenant.setHouse(house);。 如果没有它,您将获得异常或覆盖对house 的更改,具体取决于您对关系的级联设置。

完成这一切的首选方法是:

在单个事务中:

加载实体 根据需要操作它们 提交事务

JPA 将在实际提交数据库事务之前跟踪对实体的更改并将它们刷新到数据库中。

【讨论】:

是的,一开始它们是托管实体,我还添加了对问题的说明。我对您的解决方案感到困惑的是:为什么在修改托管实体后不需要为托管实体调用save()?可能我错了,但是看Spring和Spring Boot教程的时候,我记得修改后总是需要调用save(),不是吗? 关于您提出的方法的另一条评论。你的意思是这样的:1. 加载实体 -> 2. 根据需要操作它们 -> 3. 对它们调用 save -> 4. 显式提交还是让 Spring 隐式执行? 不需要save 托管实例。 JPA 进行脏检查,即它跟踪您更改了哪些实体并自动刷新它们。见***.com/a/1785126/66686 好的,我理解 Entity 生命周期,但它与 EntityManager 生命周期密切相关 - 实体在其管理器关闭之前被管理,之后它变得分离。但是很难找到任何关于 Spring Boot 如何管理 EntityManager 生命周期的文档。我如何知道并确定实体确实受管理的? EntityManager基本上是由事务管理的。见***.com/a/25710391/66686

以上是关于如果保存了引用的对象,应该更新引用的对象吗?的主要内容,如果未能解决你的问题,请参考以下文章

对象引用未保存的瞬态实例 在刷新错误之前保存瞬态实例

TransientPropertyValueException:对象引用了未保存的瞬态实例 - 在刷新之前保存瞬态实例

java中,"对象引用"和"对象"问题

Model.find 返回相同的对象引用

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

创建新模式对象后,Mongoose 引用的字段丢失