JPA:OptimisticLockException 和级联
Posted
技术标签:
【中文标题】JPA:OptimisticLockException 和级联【英文标题】:JPA: OptimisticLockException and Cascading 【发布时间】:2013-04-11 14:21:30 【问题描述】:在我当前的项目中,我将 Spring Data JPA 与 Hibernate 结合使用,但认为这是一个更普遍的问题,也应该涵盖“普通”JPA。
我不确定在使用@Version
时应该如何处理OptimisticLockException
。
由于我的应用程序的工作方式,一些关系有CascadeType.PERSIST
和CascadeType.REFRESH
,其他还有CascadeType.MERGE
。
-
在哪里处理
OptimisticLockException
据我所知,在服务层处理此问题尤其适用于CascadeType.MERGE
,因为这样有问题的实体可能需要由另一个服务处理(我每个实体类都有一个服务)。
问题是我正在创建一个框架,因此服务之上没有层,所以我可以将它“委托”给我的框架的用户,但这似乎“弱而懒惰”。
-
确定违规实体和更改的字段
如果发生 OptimisticLockException,我如何获取导致问题的实体以及更改了哪些字段?
是的,我可以调用getEntity()
,但我如何将其转换为正确的类型,尤其是在使用 CascadeType.MERGE 的情况下?实体可能有多种类型,因此想到带有instanceof
的 if/switch,但这看起来很丑陋。
一旦我有了正确的类型,我就需要获取版本之间的所有差异,不包括某些字段,例如版本本身或 lastModifiedDate。
在我的脑海里还有 HTTP 409,它指出在发生冲突的情况下响应应该包含冲突的字段。
是否有适用于所有这些的“最佳实践模式”?
【问题讨论】:
【参考方案1】:困扰我的是 JPA (Hibernate) 和 Spring 提供的异常实际上并没有返回失败对象的当前版本。因此,如果用户需要决定做什么,他显然需要查看更新后的最新版本。只是延迟了他的电话的一个错误,对我来说似乎是延迟了。我的意思是您已经在事务中处于数据库级别,因此直接获取新的当前值没有成本......
我创建了一个新异常,其中包含对未能更新实体的最新版本的引用:
public class EntityVersionConflictException
@Getter
private final Object currentVersion;
public EntityVersionConflictException(
ObjectOptimisticLockingFailureException lockEx,
Object currentVersion)
super(lockEx);
this.currentVersion = currentVersion;
public Object getConflictingVersion()
return ((OptimisticLockException)getCause().getCause()).getEntity();
public Class getEntityClass()
return getCause().getPersistentClass();
@Override
public ObjectOptimisticLockingFailureException getCause()
return (ObjectOptimisticLockingFailureException)super.getCause();
以及相应的服务方法
try
return getRepository().save(entity);
catch (ObjectOptimisticLockingFailureException lockEx)
// should only happen when updating existing entity (eg. merging)
// and because entites do not use CascadeType.MERGE
// the entity causing the issue will always be the of class
// entity.getClass()
// NOTE: for some reason lockEx.getPersistentClass() returns null!!!
// hence comparing by class name...
if (lockEx.getPersistentClassName().equals(entityClass.getName()))
T currentVersion = getById(entity.getId());
throw new EntityVersionConflictException(lockEx, currentVersion);
else
throw lockEx;
注意 cmets。在 CascadeType.MERGE 的情况下,这不会像这样工作,逻辑必须要复杂得多。我每个实体类型有 1 个服务,因此服务必须持有对所有其他服务的引用等等。
【讨论】:
【参考方案2】:乐观锁定的全部意义在于能够告诉最终用户:嘿,您试图保存这条重要信息,但有人在背后保存了它,所以您最好刷新信息,决定如果您仍想保存它并可能输入一些新值,然后重试。
就像 SVN 一样,如果您尝试提交一个文件而其他人之前提交了一个新版本,SVN 会强制您更新您的工作副本并解决潜在的冲突。
所以我会像 JPA 一样做:它让调用者通过抛出异常来决定要做什么。这个异常应该在表现层处理。
【讨论】:
应该吗? HTTP 409 表示响应应该包含有关出错的信息:冲突最有可能在响应 PUT 请求时发生。例如,如果正在使用版本控制并且被 PUT 的实体包括对资源的更改,这些更改与早期(第三方)请求所做的更改相冲突,则服务器可能会使用 409 响应来指示它无法完成请求.在这种情况下,响应实体可能会以响应 Content-Type 定义的格式包含两个版本之间的差异列表以上是关于JPA:OptimisticLockException 和级联的主要内容,如果未能解决你的问题,请参考以下文章