java Hibernate保证并发插入现有记录而不是更新时失败

osh3o9ms  于 2023-05-12  发布在  Java
关注(0)|答案(1)|浏览(85)

我有这样的设想。我有一个web/rest服务,它接收一个带有业务ID的新记录,并使用hibernateentityManager插入它。然而,存在可以同时进行这样的插入的多个过程。Hibernate save方法将插入或更新(如果存在相同主键的记录)。
因此,如果多个进程正在执行以下等效操作:
流程1:

MySimple r=new MySimple(1,'tagP1')
    mySinmpleRepository.save(r);

流程二:

MySimple r=new MySimple(1,'tagP2')
    mySinmpleRepository.save(r);

我需要实现的是一种乐观锁定的形式,如果记录已经存在,则更新失败。
不幸的是,到目前为止发生的情况是,记录得到更新,即使我尝试“插入”一个分离的实体,也没有一个保存方法失败

示例实体:

create table MySimple(
     SIMPLE_ID INTEGER NOT NULL,
    TAG VARCHAR(200),
    primary key PK_MYSIMPLE (SIMPLE_ID)
    )

实体:

@Entity
    public class MySimple {
     @Id
     @Column(name="SIMPLE_ID", nullable=false, updateable=false)
     private simpleId;
    
     @Column(name="TAG", nullable=false, updateable=true)
     private tag;
     // getters/setters/etc..
    }

    public interface MySimpleRepository extends JpaRepository<MySimple, Integer>{
    }
vu8f3i0k

vu8f3i0k1#

spring-data-jpa就是这样设计的:因为你实体有外部标识符,spring-data-jpa永远不会把它当作一个新的实体,而不是调用em.persist(),它调用em.save()(在最近的版本中是em.merge()),这反过来又会导致Hibernate决定如何处理实体:插入或更新:

@Transactional
@Override
public <S extends T> S save(S entity) {

    Assert.notNull(entity, "Entity must not be null");

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}

选项如下:
1.利用DB锁,即在进入临界区时锁定数据库中的某些内容,并在保存实体之前执行一些检查。在PostgreSQL中,可以使用咨询锁,在其他DB中,您可以创建一个具有单行的表并锁定它。
1.为您的实体定义一个“technical”@Id字段,并使用作为@NaturalId提供的“business identifier”,在这种情况下,唯一约束将有所帮助
1.在这种情况下,不要使用spring-data-jpa存储库-使用EnitytManager API。
1.利用Composite Repositories并调用em.persist()
1.提交一个CR到spring-data-jpa项目,要求提供persist方法或其他一些选项,例如boolean isNew参数
1.利用@Version - hibernate(和spring-data-jpa)认为具有null版本的实体是新的。
1.以某种方式尝试在您实体中实现org.springframework.data.domain.Persistable,以便告诉spring-data-jpa在特定场景中它应该将实体视为新的,例如:

@Entity
public class MySimple extends AbstractPersistable<Integer> {

    private Boolean forceNew;

    @Transient
    @Override
    public boolean isNew() {
        if (forceNew != null) {
            return forceNew;
        }
        return super.isNew();
    }

}

相关问题