Spring Data Rest如何基于父实体@Version实现子实体乐观锁逻辑
你不需要从零自定义@RestController,Spring Data Rest提供了多个轻量扩展点,完全可以在保留原有默认能力(HATEOAS、ALPS、分页、关联资源处理等)的前提下实现子实体修改触发父实体版本递增的需求。
具体实现方案
1. 监听子实体生命周期事件,强制递增父实体版本
Spring Data Rest提供了@RepositoryEventHandler注解,可以监听实体的增删改事件,在操作子实体前对父实体加OPTIMISTIC_FORCE_INCREMENT乐观锁,只要子实体有修改,父实体版本号会自动递增,版本冲突时会直接抛出ObjectOptimisticLockingFailureException,Spring Data Rest会自动转换为409 Conflict响应。
事件监听类代码示例:
import org.springframework.data.rest.core.annotation.HandleBeforeCreate; import org.springframework.data.rest.core.annotation.HandleBeforeDelete; import org.springframework.data.rest.core.annotation.HandleBeforeSave; import org.springframework.data.rest.core.annotation.RepositoryEventHandler; import org.springframework.stereotype.Component; import javax.persistence.EntityManager; import javax.persistence.LockModeType; import javax.persistence.PersistenceContext; @Component @RepositoryEventHandler public class ChildEntityLifecycleHandler { @PersistenceContext private EntityManager entityManager; // 新增子实体时触发 @HandleBeforeCreate public void beforeCreateChild(Child child) { lockParent(child.getParent()); } // 修改子实体时触发 @HandleBeforeSave public void beforeSaveChild(Child child) { lockParent(child.getParent()); } // 删除子实体时触发 @HandleBeforeDelete public void beforeDeleteChild(Child child) { lockParent(child.getParent()); } private void lockParent(Parent parent) { // 对父实体加乐观强制增量锁,修改后自动递增版本,版本不匹配直接抛出锁异常 entityManager.lock(parent, LockModeType.OPTIMISTIC_FORCE_INCREMENT); } }
2. 适配子实体请求的ETag校验逻辑
因为子实体本身没有@Version字段,默认的ETag生成和校验逻辑不适用,需要做两处适配:
- 子实体响应的ETag替换为父实体的版本号:可以通过
RepresentationModelProcessor自定义返回模型的响应头,或者直接替换ShallowEtagHeaderFilter的ETag生成逻辑,确保客户端拿到的ETag是父实体的版本值。 - 子实体修改请求的版本校验:通过
HandlerInterceptor或者@ControllerAdvice拦截子实体的PUT/PATCH/DELETE请求,拿到请求头If-Match携带的版本值,和关联父实体的当前版本对比,不匹配直接返回409响应即可。
3. 父实体自身修改的兼容
父实体本身自带@Version注解,原有默认的ETag校验和版本递增逻辑不需要做任何修改,会正常生效。
以上改造完成后,原有@RepositoryRestResource暴露的所有接口能力完全保留,不需要额外开发HATEOAS、ALPS相关逻辑。
内容的提问来源于stack exchange,提问作者Meini
相关产品推荐
相关产品推荐

