小编典典

在Spring-boot项目中更改版本时不会引发OptimisticLockException

spring-boot

型号结构:

@MappedSuperclass
public class BaseModel<K extends Comparable> implements Serializable, Comparable<Object> {

    private static final long serialVersionUID = 1L;

    @Id
    private K id;

    @Version
    private Integer version;

    // getter/setter
}

@Entity
public class MyEntity extends BaseModel<String> {
    // some fields and it's getter/setter
}

在我的数据库中记录my_entity

id:1版本:1 …

下面是我的更新方法:

void update(String id, Integer currentVersion, ....) {
    MyEntity myEntity = myRepository.findOne(id);
    myEntity.setVersion(currentVersion);
    // other assignments

    myRepository.save(myEntity);
}

下面是调用此方法时触发的查询。

update my_entity set version=?, x=?, y=?, ...
where id=? and version=?

currentVersion在上述方法中传递的不是时,我期望OptimisticLockException 1

谁能帮我为什么我没有得到OptimisticLockException?我正在为我的webmvc项目使用spring-boot。


阅读 423

收藏
2020-05-30

共1个答案

小编典典

JPA规范的11.1.54节指出:

通常,应用程序不应更新使用Version批注指定的字段或属性。

根据经验,我可以建议您尝试手动更新版本字段时,某些JPA提供程序(OpenJPA是其中之一)实际上会引发异常。

虽然不是严格回答您的问题,但是您可以按以下方式进行重构,以确保JPA提供程序之间的可移植性以及对JPA规范的严格遵守:

public void update(String id, Integer currentVersion) throws MyWrappedException {
    MyEntity myEntity = myRepository.findOne(id);

    if(currentVersion != myEntity.getVersion()){
        throw new MyWrappedException();
    }

    myRepository.save(myEntity);

   //still an issue here however: see below
}

假设您的update(...)方法正在事务中运行,但是如JPA规范第3.4.5节所述,您仍然遇到上述问题:

3.4.5
OptimisticLockException与有效的锁定模式和刷新模式设置一致时,提供程序实现可以将对数据库的写操作推迟到事务结束。在这种情况下,直到提交时间才可能进行乐观锁检查,并且可能在提交的“完成之前”阶段引发OptimisticLockException。
如果OptimisticLockException必须由应用程序捕获或处理,则应用程序应使用flush方法来强制执行数据库写操作。
这将允许应用程序捕获和处理乐观锁异常。

基本上,两个用户可以同时提交同一实体的修改。这两个线程都可以通过初始检查,但是当更新被刷新到数据库时可能会失败,这可能是在事务提交时执行的,即在您的方法完成之后。

为了可以捕获和处理OptimisticLock异常,代码应如下所示:

public void update(String id, Integer currentVersion) throws MyWrappedException {
    MyEntity myEntity = myRepository.findOne(id);

    if(currentVersion != myEntity.getVersion()){
        throw new MyWrappedException();
    }

    myRepository.save(myEntity);

    try{
       myRepository.flush()
    }
    catch(OptimisticLockingFailureException  ex){
       throw new MyWrappedException();
    }
}
2020-05-30