使用JPA进行更新的最佳方法

使用JPA我应该在更新时遵循哪种方法?

方法1

obj o = new obj() o.setName('val') set other values entitymanger.merge(ibj) 

方法2

 obj o = getObjFromDb(obj) obj.setval(name) //not am not updating other attributes entitymanage.merge(obj) 

你真的需要理解merge操作的语义,所以我将重复JPA规范所说的内容:

3.2.4.1合并独立实体状态

合并操作允许将状态从分离的实体传播到由EntityManager管理的持久实体。

应用于实体X的合并操作的语义如下:

  • 如果X是分离的实体,则将X的状态复制到具有相同标识的预先存在的管理实体实例X’上,或者创建X的新管理副本X’。
  • 如果X是新的实体实例,则创建新的管理实体实例X’,并且将X的状态复制到新的管理实体实例X’中。
  • 如果X是已删除的实体实例,则合并操作将抛出IllegalArgumentException (或者事务提交将失败)。
  • 如果X是托管实体,则合并操作会忽略它,但是,如果已使用级联元素值cascade=MERGEcascade=ALL批注对这些关系进行批注,则合并操作将级联到由X关系引用的实体。
  • 对于具有级联元素值cascade=MERGEcascade=ALL X的关系引用的所有实体Y,Y被递归地合并为Y’。 对于由X引用的所有这样的Y,X’被设置为引用Y’。 (注意,如果管理X,则X与X’是同一个对象。)
  • 如果X是合并到X’的实体,并且引用另一个实体Y,其中未指定cascade=MERGEcascade=ALL ,则从X’导航相同的关联将产生对托管对象Y’的引用。与Y相同的持久性身份。

持久性提供程序不能合并尚未提取的标记为LAZY的字段:合并时必须忽略此类字段。

在合并操作期间和/或刷新或提交时,必须由持久性运行时实现检查实体使用的任何Version列。 如果没有Version列,则在合并操作期间,持久性提供程序运行时不会执行其他版本检查。

换句话说, merge操作将传递的实体的状态复制到具有相同数据库标识的托管实体(如果需要,将在持久化上下文中加载),然后返回对该托管实体的引用(并且传递的对象是没有附加到持久化上下文)。

现在回到最初的问题。 在这两种情况下,将使用传递的实体的完整状态覆盖数据库值,包括非null(和null)值。 在实践中:

  • 方法2即直接在分离的实体上工作更容易
  • 方法1听起来像DTO反模式,意味着更多的痛苦

如果可以的话,更喜欢方法2。

相关问题

  • JPA EntityManager:为什么在merge()上使用persist()?

您首先创建新对象,因此合并不会更新对象,而是将新的对象放入DB。 所以方法2看起来更好。 但是看看你对getObjFromDb关于事务的评论,我必须注意方法: mergepersistremove应该在事务范围内完成。

所以更好的解决方案伪代码是:

 boolean transactionClosed = false; entitymanage.getTransaction().begin(); try{ obj o = entitymanage.find( someKey ); o.setval(name); entitymanage.getTransaction().commit(); transactionClosed = true; } finally{ if( !transactionClosed ) entitymanage.getTransaction().rollback(); }