有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何使Hibernate@Lock注释适用于Oracle DB?

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

为了实现锁,我将@lock注释置于SpringData find方法之上,该方法检索参与事务的数据

@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)上

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


共 (1) 个答案

  1. # 1 楼答案

    你说过:

    The purpose of the lock is to prevent more than one transaction reading data from the DB because this data influences the generation of new data and is changed in terms of a transaction.

    Oracle使用^{}所以读卡器不会阻止编写器,编写器也不会阻止读卡器。即使您使用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在使用DISTINCTUNION ALL进行Oracle 11g分页时无法应用锁

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

    这在MariaDB或任何其他数据库中都不会发生。这只是甲骨文12年前的限制

    如果您使用的是Hibernate 5.2.1,我们添加了一个新的提示^{},该提示将禁用此机制

    因此,您的Spring数据查询变成:

    @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();