Spring @Transactional 什么时候锁数据库表行

Jav*_*ner 6 java multithreading hibernate spring-data-jpa

我正在使用 Spring data 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);
}
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,假设entityA和分别entityB对应于tableA和,并且和是 的两列。另外,我使用的是 SQL Server,因此默认隔离级别是tableBproperty1property2tableAREAD_COMMITTED

我有以下问题:

  1. 执行完该行后,var entityA = repositoryA.findById(id);如何确定应该获取哪种类型的锁?在上面的例子中,我们在更新数据tableA和读取数据tableB,那么这里获取的是不同的锁吗?

  2. 假设两个method1method2都与同一个 同时调用id。首先获取锁的线程会阻塞第二个线程还是两者并行执行?我知道隔离级别是,READ_COMMITTED但数据库不知道我是否对第一个线程获取的行进行了任何更改。

  3. 如果我想并行执行相同的两个方法,id应该设置什么隔离级别?另外,在这两种方法中,我都更新同一行的不同列,因此当两个事务提交时,其结果是否与串行执行这些事务相同?附带说明一下,当 Hibernate 在提交阶段刷新更改时,对于更新语句,它会更新实体的所有字段,那么在这种情况下会丢失更新吗?

  4. 如果我在Transactional注解中指定的readOnly=true话,会对锁的获取有影响吗?

Kee*_*thi 3

以下是关于 Hibernate 作为 JPA 提供者的情况。

#1 代码中没有显式锁。所以,DB会根据事务隔离级别来行使锁。

#2 当事务提交时,entityA 代表的行将被数据库锁定。如果 method1 获胜,它将阻止 method2 访问底层行,反之亦然。同样,DB 在这里管理锁(因此这里不会有线程级同步)。

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

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