2013-04-18 29 views
3

在我当前的项目中,我使用Hibernate的Spring Data JPA,但认为这是一个更普遍的问题,也应该覆盖“普通”的JPA。JPA:OptimisticLockException和级联

我不确定在使用@Version时应如何处理OptimisticLockException

由于我的应用程序的工作原理,某些关系有CascadeType.PERSISTCascadeType.REFRESH,其他关系也有CascadeType.MERGE

  1. 在哪里处理OptimisticLockException

至于我可以告诉处理这个服务层上不会与CascadeType.MERGE尤其是工作,因为随后的违规实体可以是一个需要由其他处理服务(我有一个实体类的服务)。

问题是我正在创建一个框架,因此服务上面没有图层,所以我可以“委托”给我的框架的用户,但似乎“弱和懒惰”。

  1. 确定违规的实体和字段如果发生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; 
    } 
} 

注意的意见。在CascadeType.MERGE的情况下,这不会像这样工作,逻辑将不得不更复杂。我为每种实体类型提供1项服务,以便服务人员必须参考所有其他服务等等。

5

乐观锁定的关键在于能够告诉最终用户:嘿,您试图保存这一重要信息,但其他人将它保存在您的背后,所以您最好刷新信息,决定是否仍想保存它并可能输入一些新值,然后重试。

就像使用SVN一样,如果您尝试提交一个文件并且其他人提交了新版本,SVN会强制您更新您的工作副本并解决潜在的冲突。

所以我会和JPA做的一样:它可以让调用者通过抛出异常来决定做什么。这个异常应该在表示层中处理。

+0

应该吗? HTTP 409表明响应应该包含关于出错的信息:冲突最有可能发生以响应PUT请求。例如,如果正在使用版本控制并且包含PUT的实体更改为与先前(第三方)请求发生冲突的资源发生冲突,则服务器可能会使用409响应来指示它无法完成请求。在这种情况下,响应实体可能会以响应Content-Type定义的格式包含两个版本之间的差异列表 –