【问题标题】:Application-level locking and hibernate optimistic locking应用程序级锁定和休眠乐观锁定
【发布时间】:2017-02-06 12:24:34
【问题描述】:

我正在使用类似 MariaDB 的休眠乐观锁定

@Version
@Column(columnDefinition="TIMESTAMP(3) DEFAULT '2016-01-01'")
private Timestamp timestamp;

最近,我有一个StaleStateException,作为一个快速破解,我尝试通过锁定应用程序来对抗它。这导致以下顺序(第二列是线程号;每个线程都有自己的连接):

A 1 createSession
B 2 createSession
C 1 read some irrelevant entity
D 2 read some irrelevant entity
E 1 try to obtain lock
F 2 try to obtain lock
G 1 LOCK GRANTED
H 1 READ THE ENTITY, timestamp=2016-09-28 14:52:32.076
I 1 do some other stuff
J 1 WRITE THE ENTITY, new timestamp=2016-09-28 14:52:32.076
K 1 COMMIT
L 1 release lock
M 2 LOCK GRANTED
N 1 READ THE ENTITY, timestamp=2016-09-28 14:52:32.076
O 2 do some other stuff
P 2 WRITE THE ENTITY, StaleStateException

问题显然在第 N 行,第二个线程看到旧的时间戳。我没有在任何地方指定隔离级别,Mysql的默认应该是REPEATABLE READ,但是AFAIK没有级别,除了 甚至 SNAPSHOT 或 SERIALIZABLE 都应该让第二个线程看到旧值,不是吗?

我可以看到应用程序级锁定是一个坏主意,我只是想了解发生了什么,然后再切换到重试或 DB 锁定。

一个可能的原因是我使用 JodaTime Instant 进行版本控制, which is broken.

【问题讨论】:

  • 如果你不“阅读一些不相关的实体”会发生什么?
  • @JohnDonn 我不知道,也无法轻易找到它,因为它是从无法轻易删除的真实代码中提取的。制作 SSCCE 肯定会有所帮助,但它相当复杂。 +++ 我猜,新的时间戳会被读取,因为createSession 是懒惰的。但这只是猜测。

标签: java mysql multithreading hibernate


【解决方案1】:

你没有说的是交易的开始和结束。

如果每个会话在步骤 A 和 B 开始事务,那么确实如此,Serializable 意味着会话 2 只会看到它提交的旧时间戳单元并开始新事务。

如果会话 2 使用 Read Committed,它将在会话 1 提交后看到新值。

在此示例中,JPA 完全按照您的要求进行操作,并使用 Version 字段来避免隐藏更新。如果您不在乎,并且无论如何都想进行更新,请不要使用版本...?

【讨论】:

    猜你喜欢
    • 2021-09-27
    • 1970-01-01
    • 2011-10-18
    • 2011-09-14
    • 2011-03-21
    • 2013-08-12
    • 2016-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多