JPA / Hibernate:基于版本的乐观并发控制

本文是Hibernate和JPA中基于版本的乐观并发控制的简介。 这个概念已经很老了,上面已经写了很多东西,但是无论如何我都看到了它被重新发明,误解和滥用。 我在编写它只是为了传播知识,并希望引起人们对并发控制和锁定的兴趣。

用例

假设我们有一个供多个用户使用的系统,其中每个实体可以由多个用户修改。 我们希望避免两个人加载一些信息,根据他们看到的内容做出一些决定并同时更新状态的情况。 我们不希望丢失在第一个交易中首先单击“保存”的用户通过覆盖它们所做的更改。

它也可能在服务器环境中发生–多个事务可以修改共享实体,我们希望避免出现以下情况:

  1. 交易1载入资料
  2. 事务2更新该数据并提交
  3. 使用在步骤1中加载的状态(不再是当前状态),事务1执行一些计算并更新状态

在某些方面,它与不可重复的读取具有可比性。

解决方案:版本控制

因此,Hibernate和JPA实现了基于版本的并发控制的概念。 运作方式如下。

您可以使用@Version<version> (数字或时间戳)标记一个简单的属性。 这将是数据库中的特殊列。 我们的映射如下所示:

@Entity
@Table(name = 'orders')
public class Order {
	@Id
	private long id;

	@Version
	private int version;

	private String description;

	private String status;

	// ... mutators
}

当这样的实体持续存在时,version属性将设置为起始值。

每当更新时,Hibernate都会执行如下查询:

update orders
set description=?, status=?, version=?
where id=? and version=?

请注意,在最后一行, WHERE子句现在包括version 。 此值始终设置为“旧”值,因此只有在具有预期版本的情况下,它才会更新行。

假设有两个用户在版本1中加载订单,并花一些时间在GUI中查看订单。

安妮决定批准该订单并执行该操作。 数据库中的状态已更新,一切正常。 传递给update语句的版本如下:

update orders
set description=?, status=?, version=2
where id=? and version=1

如您所见,在持久化更新持久层时,版本计数器将增加到2。

在她的GUI中,Betty仍然具有旧版本(编号1)。 当她决定对订单执行更新时,该语句如下所示:

update orders
set description=?, status=?, version=2
where id=? and version=1

此时,在Anne的更新之后,数据库中该行的版本为2。因此,第二次更新影响0行(没有与WHERE子句匹配的行)。 Hibernate会检测到它,并检测到一个org.hibernate.StaleObjectStateException (包装在javax.persistence.OptimisticLockException )。

结果,第二个用户除非刷新视图,否则无法执行任何更新。 为了获得适当的用户体验,我们需要进行一些干净的异常处理,但是我将省略。

组态

这里几乎没有要自定义的内容。 @Version属性可以是数字或时间戳。 数字是人为的,但通常在内存和数据库中占用较少的字节。 时间戳较大,但始终会更新为“当前时间戳”,因此您可以实际使用它来确定实体的更新时间。

为什么?

那为什么要使用它呢?

  • 它提供了一种方便且自动化的方式来维持上述情况下的一致性。 这意味着每个动作只能执行一次,并且可以确保用户或服务器进程在制定业务决策时看到最新状态。
  • 设置只需很少的工作。
  • 由于其乐观的性质,因此速度很快。 在任何地方都没有锁定,只有一个字段添加到同一查询中。
  • 在某种程度上,即使在已提交读事务隔离级别的情况下,它也可以确保可重复读。 它将以一个异常结束,但是至少不可能创建不一致的状态。
  • 它适用于非常长的对话,包括跨越多个事务的对话。
  • 在ACID数据库上的所有可能情况和竞争条件下,它都是完全一致的。 更新必须是顺序更新,更新涉及行锁定,而“第二”更新将始终影响0行并失败。


演示版

为了演示这一点,我创建了一个非常简单的Web应用程序。 它将Spring和Hibernate连接在一起(在JPA API后面),但是它也可以在其他设置中工作:Pure Hibernate(无JPA),具有不同实现的JPA,非webapp,非Spring等。

该应用程序保留一个具有与上述类似的架构的Order ,并以Web表单显示该Order ,您可以在其中更新描述和状态。 要尝试并发控制,请在两个选项卡中打开页面,进行不同的修改并保存。 不使用@Version尝试相同的@Version

它使用嵌入式数据库,因此需要最少的设置(仅Web容器),并且只需重新启动即可从新数据库开始。

这非常简单-在@Transactional @Controller访问EntityManager并直接使用JPA映射的实体支持表单。 对于不太琐碎的项目而言,这可能不是最好的处理方法,但是至少它将所有代码集中在一个地方并且非常容易掌握。

可以在我的GitHub存储库中找到Eclipse项目的完整源代码。

参考: 我们的JCG合作伙伴 Konrad Garus的Squirrel博客上,JPA / Hibernate中基于版本的乐观并发控制

翻译自: https://www.javacodegeeks.com/2012/11/jpahibernate-version-based-optimistic-concurrency-control.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值