没有JPA如何实现持久锁?

How to implement persistence lock without JPA?

我正在开发一个 Java Spring-Boot API 项目,数据库层不会为大多数实体使用 JPA(由于选择的数据库设计,我无法在 JPA 中实现:[database model] and for which I have raised another question a few days ago: [question])。

我担心实体的并发编辑,想知道我应该如何实现某种锁来避免并发更改,但又不依赖 JPA。

人们在 JPA 和 Hibernate 之前使用的一些常见方法是什么?

对于悲观锁定,JPA 最终归结为使用 JDBC 发出 select * from foo where id = 1 for update(在 MySQL 情况下)锁定 ID 1 的行。在锁定此行的事务完成之前,没有人可以 select 此行(另请参阅 this 其中 select for update 可能会锁定整个 table 在某些情况下)。所以你可以确保只有一个人可以更改这一行。

对于乐观锁,JPA最终归结为首先为每条记录引入一个版本号。每当一条记录被 selected 时,它也会 select 这个版本号(假设 selected 版本是 10)。每当进行更新时,它总是会将该版本增加一个,并使用以下 AND 条件检查该版本是否仍未被其他人更改:

update foo set bar = xxxx , version = version + 1 where id = 1 and version = 10;

所以,如果没有记录可以更新,这意味着另一个人在你更新之前已经更改了这条记录(即那个人已经将版本更新到 11,这使得你的 AND 条件失败)。您可以简单地向用户显示一些错误消息并告诉他们重新加载/重试等,具体取决于您的业务需求。

我的意思是您可以使用 JDBC 简单地模拟上述方法,因为它们只是最后的一些简单 SQL 语句....

为简单起见,请使用 serializable 事务锁定。它比类似版本的锁定更容易,并且解决了大部分问题。

START TRANSACTION;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- do stuff
COMMIT;

Spring Framework 通过提供 @Transactional 注解让您轻松实现,您可以在其中指定隔离级别和传播策略。我发现它非常有用且易于使用。

EDIT: MySQL Aurora (AWS RDS) 不支持序列化事务隔离级别,所以谨慎使用。

如果还不够,参考@Ken Chan的回答。根据你的问题,你担心的是 concurrent edit of the entities,所以这应该可以解决问题。