Spring Data JPA保存指定ID新实体后version为何变为1而非0?
为什么手动设置version=0的实体保存后查询到version=1?
这是JPA乐观锁机制结合Spring Data JPA的merge操作逻辑导致的正常现象,具体原因拆解如下:
为什么触发
merge而非persist
Spring Data JPA的SimpleJpaRepository.save()方法会根据实体是否包含非空id值判断执行逻辑:当你传入SomEntity(id=1, version=0, ...)时,因为id已存在,框架会认为这是一个处于detached状态的实体,因此执行em.merge()而非em.persist()。merge操作的执行流程
执行merge时JPA会完成这些步骤:- 检查当前持久化上下文是否存在id=1的实体,不存在则查询数据库(此时数据库中无该实体)
- 创建新的持久化实例,将你传入的detached实体的属性(id=1、version=0、name=SomeName)复制到这个实例中
- 由于数据库无对应实体,
merge最终执行插入操作,但JPA乐观锁机制会自动处理@Version字段:首次持久化实体时,不管手动设置的version值是什么,都会自动递增为1(主流JPA实现如Hibernate均遵循此逻辑) - 插入完成后,数据库中存储的实体version为1,而
merge返回的是这个持久化实例——如果你没有接收并使用这个返回值,而是继续用原来的detached实体,就会看到version还是0。
findAll查询到version=1的原因repo.findAll()直接从数据库查询最新的持久化数据,因此会拿到数据库中实际存储的version=1的实体,这和merge操作后数据库的状态一致。
补充说明:如果目标是插入全新实体,建议不要手动设置id(除非使用手动分配id的策略),让JPA自动生成id值,此时save()会触发persist逻辑,新实体的version初始值会按JPA实现规则生成(通常也是1)。
内容的提问来源于stack exchange,提问作者ezjdmoq
相关产品推荐
相关产品推荐

