JPA 乐观锁
JPA Optimistic locking
我在理解 OPTIMISTIC LockMode 时遇到了一些麻烦。
让我们考虑以下场景:"Thread A creates a Transaction and reads a list of all Users from Table USERS. Thread B updates a user in Table USERS. Thread B commits. Thread A commits".
假设我正在使用 OPTIMISTIC 锁定。在这种情况下,第二次提交会导致抛出 OptimisticLockException 吗?
因为根据this docu:"During commit (and flush), ObjectDB checks every database object that has to be updated or deleted, and compares the version number of that object in the database to the version number of the in-memory object being updated. The transaction fails and an OptimisticLockException is thrown if the version numbers do not match".
不应抛出异常,因为版本号仅针对那些必须更新或删除的实体进行检查。
但是
This docu 表示:"JPA Optimistic locking allows anyone to read and update an entity, however a version check is made upon commit and an exception is thrown if the version was updated in the database since the entity was read. "
根据这个描述,应该抛出异常,因为版本检查是在提交时进行的(我假设它们是指每次提交,包括读取后的提交)。
我想实现所描述的场景不应该抛出任何并发异常,如果线程 A returns 不是最新的用户列表也没问题。那么使用乐观锁定是否正确,如果不正确,我应该使用哪种 LockType?
你给的两个链接说的是同一件事。如果一个实体正在 TransactionA 中更新,并且自从 TransactionA 读取该实体后它已在 DB 中被 TransactionB 修改,那么将抛出 OptimisticLockException。
在你的例子中,你正在检索线程 A 中所有用户的列表,但你只更新了一个。仅当 same 实体在 threadb.
中被更改和提交(尝试)时,您才会得到 OptimisticLockException
在这种情况下,您会希望抛出异常,否则只有一个更新会成功——最后提交的更新将简单地覆盖较早的提交——但最后一个更新有点不确定——有时threadA 有时 threadB 和 DB 内容确实不是预期的。所以锁定可以防止这种不良行为。
如果您的应用程序事务经常与数据发生冲突,请考虑使用 https://blogs.oracle.com/carolmcdonald/entry/jpa_2_0_concurrency_and
中所述的悲观锁定
乐观锁很容易理解:
每个实体都有一个时间戳/版本号属性。
每次更新实体时,时间戳/版本号也会更新。
当你更新一个实体时,你首先读取持久层(数据库)中的实际时间戳,如果它在你加载它之后发生了变化,那么就会抛出一个 OptimisticLockException ,否则它会随着新的时间戳/版本号一起更新。
如果您没有并发更新的风险,那么您不应该使用任何锁机制,因为即使是乐观的锁机制也会对性能产生影响(您必须在更新实体之前检查时间戳)。
悲观锁定是一个可伸缩性问题,因为它一次只允许对给定资源进行一次更新访问(因此其他非只读访问被阻止),但它可以避免操作失败。如果可扩展性不是问题,如果您不能放松操作,请悲观,否则在业务级别处理并发缓解。
我在理解 OPTIMISTIC LockMode 时遇到了一些麻烦。
让我们考虑以下场景:"Thread A creates a Transaction and reads a list of all Users from Table USERS. Thread B updates a user in Table USERS. Thread B commits. Thread A commits".
假设我正在使用 OPTIMISTIC 锁定。在这种情况下,第二次提交会导致抛出 OptimisticLockException 吗?
因为根据this docu:"During commit (and flush), ObjectDB checks every database object that has to be updated or deleted, and compares the version number of that object in the database to the version number of the in-memory object being updated. The transaction fails and an OptimisticLockException is thrown if the version numbers do not match".
不应抛出异常,因为版本号仅针对那些必须更新或删除的实体进行检查。
但是
This docu 表示:"JPA Optimistic locking allows anyone to read and update an entity, however a version check is made upon commit and an exception is thrown if the version was updated in the database since the entity was read. "
根据这个描述,应该抛出异常,因为版本检查是在提交时进行的(我假设它们是指每次提交,包括读取后的提交)。
我想实现所描述的场景不应该抛出任何并发异常,如果线程 A returns 不是最新的用户列表也没问题。那么使用乐观锁定是否正确,如果不正确,我应该使用哪种 LockType?
你给的两个链接说的是同一件事。如果一个实体正在 TransactionA 中更新,并且自从 TransactionA 读取该实体后它已在 DB 中被 TransactionB 修改,那么将抛出 OptimisticLockException。
在你的例子中,你正在检索线程 A 中所有用户的列表,但你只更新了一个。仅当 same 实体在 threadb.
中被更改和提交(尝试)时,您才会得到 OptimisticLockException在这种情况下,您会希望抛出异常,否则只有一个更新会成功——最后提交的更新将简单地覆盖较早的提交——但最后一个更新有点不确定——有时threadA 有时 threadB 和 DB 内容确实不是预期的。所以锁定可以防止这种不良行为。
如果您的应用程序事务经常与数据发生冲突,请考虑使用 https://blogs.oracle.com/carolmcdonald/entry/jpa_2_0_concurrency_and
中所述的悲观锁定乐观锁很容易理解:
每个实体都有一个时间戳/版本号属性。
每次更新实体时,时间戳/版本号也会更新。 当你更新一个实体时,你首先读取持久层(数据库)中的实际时间戳,如果它在你加载它之后发生了变化,那么就会抛出一个 OptimisticLockException ,否则它会随着新的时间戳/版本号一起更新。
如果您没有并发更新的风险,那么您不应该使用任何锁机制,因为即使是乐观的锁机制也会对性能产生影响(您必须在更新实体之前检查时间戳)。
悲观锁定是一个可伸缩性问题,因为它一次只允许对给定资源进行一次更新访问(因此其他非只读访问被阻止),但它可以避免操作失败。如果可扩展性不是问题,如果您不能放松操作,请悲观,否则在业务级别处理并发缓解。