【问题标题】:How to make Hibernate @Lock annotation work for Oracle DB?如何使 Hibernate @Lock 注释适用于 Oracle DB?
【发布时间】:2017-02-28 03:15:14
【问题描述】:

我偶然发现了在 Oracle DB 中锁定行的问题。锁的目的是防止多个事务从 DB 中读取数据,因为这些数据会影响新数据的生成,并且在事务方面会发生变化。

为了生成锁,我在 SpringData 的 find 方法上添加了 @Lock 注释,该方法检索参与事务的数据。

@Lock(LockModeType.PESSIMISTIC_WRITE)
User findUserById(@Param("id") String operatorId);

执行此代码后,我会收到日志消息

org.hibernate.loader.Loader - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes

此外,它没有影响和原因

org.springframework.dao.DataIntegrityViolationException: could not execute batch; SQL [insert into ...]

使用实体管理器重写锁时可以解决问题

entityManager.lock(userByIdWithLockOnReadWrite, LockModeType.PESSIMISTIC_WRITE);    

或

entityManager.unwrap(Session.class).lock(userByIdWithLockOnReadWrite, LockMode.PESSIMISTIC_WRITE);

MariaDB (MySQL) 上未出现此问题。

也许使用注解有一些特殊的规则?

【问题讨论】:

    标签: java spring oracle hibernate spring-data


    【解决方案1】:

    你说过:

    锁的目的是防止多于一个事务 从数据库中读取数据,因为这些数据会影响生成 新数据并根据事务进行更改。

    Oracle 使用MVCC (Multiversion Concurrency Control),因此阅读器不会阻塞写入器,写入器不会阻塞读取器。即使您使用 Oracle 获得了行级锁,并且您修改了该行而不提交,其他事务仍然可以读取最后提交的值。

    与此日志消息相关:

    org.hibernate.loader.Loader - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes
    

    后续锁定机制是由于 Oracle 在执行 Oracle 11g 分页时无法应用锁定,使用 DISTINCT 或 UNION ALL。

    如果您使用的是 Oracle 12i,那么您可以将 Hibernate 方言更新为 Oracle12cDialect,并且分页和锁定将正常工作,因为 Oracle 12 使用 SQL 标准分页并且不再需要派生表查询。

    这不会发生在 MariaDB 或任何其他数据库中。这只是 Oracle pre-12 的限制。

    如果您使用的是 Hibernate 5.2.1,我们添加了一个新提示 HINT_FOLLOW_ON_LOCKING,它会禁用此机制。

    因此,您的 Spring Data 查询变为:

    @QueryHints(value = { @QueryHint(name = "hibernate.query.followOnLocking", value = "false")}, forCounting = false)
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    User findUserById(@Param("id") String operatorId);
    

    您也可以手动应用它:

    User user = entityManager.createQuery(
        "select u from User u where id = :id", User.class)
    .setParameter("id", id);
    .unwrap( Query.class )
    .setLockOptions(
        new LockOptions( LockMode.PESSIMISTIC_WRITE )
            .setFollowOnLocking( false ) )
    .getSingleResult();
    

    【讨论】:

    • 非常感谢您的回答。它对后续锁定很清楚。但据我了解,只要我有 Oracle12cDialect 注释就可以工作。问题发生时我就是这样。我的休眠是 5.0.2.Final。
    • 使用 Hibernate 5.0,您可以获得每个查询的后续锁定。你使用什么方言并不重要。只有 Hibernate 5.2.1 添加了对智能跟随锁定的支持。
    【解决方案2】:

    是否有任何选项可以让 Transaction(TxB) 等待一段时间(不抛出 Lock Acquisition Exception)以等待另一个 Transaction(TxA) 释放锁。

    【讨论】:

      猜你喜欢
      • 2011-04-07
      • 1970-01-01
      • 1970-01-01
      • 2019-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-07
      相关资源
      最近更新 更多