IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    JPA/Hibernate optimistic lock not working

    10k发表于 2024-03-27 00:00:00
    love 0

    描述

    在数据更新操作时,希望利用分布式锁-乐观锁机制预防并发修改,增加了对于entity的version的update以引起乐观锁版本的检查。实际的现象是修改可以正常保存,set 的version被忽略,直接在根据数据库中的数据做了递增。

    原因

    JPA/Hibernate中的实体有四种状态

    org.hibernate.event.internal.EntityState{
       PERSISTENT,//托管状态
       TRANSIENT,//瞬时状态
       DETACHED,//游离状态
       DELETED;//删除状态 
    }
    

    当除以一个事务中时, 查询出的实体将一直处于PERSISTENT状态,对这个状态下的实体修改不会检测乐观锁,手动setVersion也会被忽略。

    所以要让实体不在处于PERSISTENT状态。

    当查询与保存不在同一事务时,实体由于查询事务的结束,将变成DETACHED状态,这对JPA来说,该实体是不可信的,所以对实体保存时,JPA将通过主键再次查询数据库,进行数据比对,这也包括乐观锁的检测。 同样new一个实体时,也不会处于PERSISTENT状态, 这也可以迫使JPA进行乐观锁检测。

    解决方法

    1. 将本次操作的查询与保存放到俩个事务中;
    2. 实物保持不变,不用查询出来的实体进行set操作,new一个实体,将查出的实体与请求提交的实体合并,然后保存。(利用merge)

    JPA/Hibernate 代码实现

    org.hibernate.event.internal.DefaultMergeEventListener{
       public void onMerge(MergeEvent event, Map copiedAlready){
           //获取实体上下文,比对得到实体状态
           ...
           switch(entityState) {
               case DETACHED:
                   this.entityIsDetached(event, copyCache);
                   break;
               case TRANSIENT:
                   this.entityIsTransient(event, copyCache);
                   break;
               case PERSISTENT:
                   this.entityIsPersistent(event, copyCache);
                   break;
           }
       }
       protected void entityIsDetached(MergeEvent event, Map copyCache) {
           ...
           if (this.isVersionChanged(entity, source, persister, target)) {
               StatisticsImplementor statistics = source.getFactory().getStatistics();
               if (statistics.isStatisticsEnabled()) {
                   statistics.optimisticFailure(entityName);
               }
               
               throw new StaleObjectStateException(entityName, id);
           }
           ...
       }
       protected void entityIsPersistent(MergeEvent event, Map copyCache) {
            LOG.trace("Ignoring persistent instance");
            Object entity = event.getEntity();
            EventSource source = event.getSession();
            EntityPersister persister = source.getEntityPersister(event.getEntityName(), entity);
            ((MergeContext)copyCache).put(entity, entity, true);
            this.cascadeOnMerge(source, persister, entity, copyCache);
            this.copyValues(persister, entity, entity, source, copyCache);
            event.setResult(entity);
        }
    }
    


沪ICP备19023445号-2号
友情链接