aha*_*aha 8 java hibernate jpa spring-data-jpa
有Foo一个@Version列的实体.如果我想删除它,我希望Spring Data JPA和/或Hibernate检查@Version列的当前值是否与数据库中的值匹配.如果没有,则应拒绝删除.这与预期的分离实体一起工作:
@Transactional
public void delete(Foo foo) {
fooRepository.delete(foo); // throws ObjectOptimisticLockingFailureException
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我首先从存储库加载实体,然后使用不同的版本在同一事务中删除它,则无论@Version列的值如何,删除都会通过:
@Transactional
public void delete(int fooId, long version) {
Foo foo = fooRepository.findOne(fooId);
foo.setVersion(version);
fooRepository.delete(foo); // passes regardless of value of version
}
Run Code Online (Sandbox Code Playgroud)
当我查看Hibernate调试输出时,执行版本比较(delete from foo where id=? and version=?)但不是我期望的效果.
我错过了什么?
根据JPA规范,第3.4.2节:
实体可以访问其版本字段或属性的状态,或者导出应用程序用于访问版本的方法,但不得修改版本值.除了4.10节中提到的例外,只允许持久性提供程序设置或更新对象中version属性的值.
版本属性的目的是保护我们免受在当前持久化上下文中加载对象后可能发生的并发更新,并且Hibernate通过忽略您手动设置的任何值来实现它,而是使用从数据库获取的值.对象已加载.要验证这一点,还要启用绑定变量值的打印,您会注意到使用了数据库中的值.
例如,在使用DTO时实际使用的标准解决方案是在从DTO更新实体状态时手动执行检查:
if (entity.getVersion() != dto.getVersion()) {
throw new OptimisticLockException("...");
}
Run Code Online (Sandbox Code Playgroud)
当然,您可以通过从为所有可版本化实体或某些util方法提供此检查的基类扩展来使其更通用.例如,一些作者直接在版本设置器中执行此操作:
public void setVersion(long version) {
if (this.version != version) {
throw new OptimisticLockException("...");
}
}
Run Code Online (Sandbox Code Playgroud)
Hibernate会自动为分离的实体执行此检查,如以下实现中所示DefaultMergeEventListener:
else if (isVersionChanged(entity, source, persister, target)) {
if (source.getFactory().getStatistics().isStatisticsEnabled()) {
source.getFactory().getStatisticsImplementor()
.optimisticFailure(entityName);
}
throw new StaleObjectStateException(entityName, id);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2092 次 |
| 最近记录: |