Java Spring@Transactional何时锁定数据库表行

Java Spring@Transactional何时锁定数据库表行,java,multithreading,hibernate,spring-data-jpa,Java,Multithreading,Hibernate,Spring Data Jpa,我使用的是Spring数据JPA,我有一个用Spring的@Transactional注释的方法。在该方法中,我从数据库中获取实体。有些实体用于只读目的,而有些则在该事务中更新。让我们举个例子 @Transactional public void method1(Long id) { var entityA = repositoryA.findById(id); var entityB = repositoryB.findByOtherId(id); entityA.s

我使用的是Spring数据JPA,我有一个用Spring的@Transactional注释的方法。在该方法中,我从数据库中获取实体。有些实体用于只读目的,而有些则在该事务中更新。让我们举个例子

@Transactional
public void method1(Long id) {
    var entityA = repositoryA.findById(id);
    var entityB = repositoryB.findByOtherId(id);

    entityA.setProperty1(entityB.getProperty() + 1);
}

@Transactional
public void method2(Long id) {
    var entityA = repositoryA.findById(id);
    var entityB = repositoryB.findByOtherId(id);

    entityA.setProperty2(entityB.getProperty() + 2);
}
在上面的例子中,假设
entityA
entityB
分别对应于
tableA
tableB
property1
property2
tableA
的两列。 此外,我使用的是SQL Server,因此默认隔离级别为
READ\u COMMITTED

我有以下问题:

  • 执行line
    var entityA=repositoryA.findById(id)后如何确定应获取哪种类型的锁?在上面的示例中,我们正在更新
    表A
    中的数据,并读取
    表B
    中的数据,因此在这里是否获得了不同的锁

  • 假设
    method1
    method2
    都与相同的
    id
    同时调用。获取锁的线程将首先阻塞第二个线程,还是两者并行执行?我知道隔离级别是
    READ\u COMMITTED
    ,但DB不知道我是否对第一个线程获取的行进行了任何更改

  • 如果我想为相同的
    id
    并行执行这两个方法,应该设置什么隔离级别?同样,在这两种方法中,我都在更新同一行的不同列,所以当两个事务都提交时,是否会产生与串行执行这些事务相同的结果?另一方面,当Hibernate在提交阶段刷新更改时,对于update语句,它会更新实体的所有字段,所以在这种情况下会丢失更新吗

  • 如果我在事务注释中指定
    readOnly=true
    ,它会对锁的获取产生任何影响吗


  • 下面是关于作为JPA提供者的Hibernate

    #1代码中没有显式锁。因此,DB将根据事务隔离级别执行锁

    #2当事务提交时,由entityA表示的行将被DB锁定。如果method1获胜,它将阻止method2访问基础行,反之亦然。同样,DB在这里管理锁(因此这里没有线程级同步)

    #3使用READ_提交的隔离,您肯定会遇到丢失的更新。为了避免更新丢失,您至少需要使用hibernate提供的悲观锁模式以编程方式使用可重复读取隔离级别或控制锁。如果您希望坚持READ_COMMITTED(99.99%的用例首选),并且仍然希望阻止丢失的更新,请使用@Version实现乐观锁


    #4 readonly=true关闭Hibernate的一级缓存脏检查。仅此而已。

    “@Transactional”本身在任何隔离级别上都不会启用任何锁定。要实现锁定行为,您应该在查询中使用“@Lock”注释或使用“for update”。这里的详细解释:
    “@Transactional”本身在任何隔离级别上都不会启用任何锁定
    ,这似乎与“是”相矛盾。你是正确的,我应该更准确。根据隔离级别,可以获取读锁或共享锁,但如果没有其他操作,则不会创建写锁或独占锁。