繁体   English   中英

Spring Data JPA - 悲观锁定不起作用

[英]Spring Data JPA - Pessimistic Locking Not Working

使用:Spring Boot 2.3.3、MySQL 5.7(目前通过 TestContainers)、JUnit 5

我有一个JpaRepository ,有一个方法设置为一个Spring MVC应用程序中@Lock(LockModeType.PESSIMISTIC_WRITE)和,而我看到的SELECT ... FOR UPDATE即将在生成的SQL,它似乎并没有做很多东西。

我将把代码放在下面,但是,如果我尝试启动多个执行相同调用的线程,每个线程都能够读取相同的初始值,并且似乎没有任何内容会阻塞/等待。 我的理解是,任何“额外”调用的方法也是@Transactional (来自 org.springframework.transaction 命名空间)都是原始事务的一部分。

我无法弄清楚我做错了什么。 任何帮助将不胜感激,即使这意味着指出我的理解/期望是有缺陷的。

存储库

public interface AccountDao extends JpaRepository<Account, Long> {
  @Lock(LockModeType.PESSIMISTIC_WRITE)
  public Optional<Account> findById(Long id);
}

服务

账户服务

@Service
public class AccountServiceImpl implements AccountService {
  @Autowired
  private FeeService feeService;

  @Override
  @Transactional // have also tried this with REQUIRES_NEW, but the same results occur
  public void doTransfer(Long senderId, Long recipientId, TransferDto dto) {
    // do some unrelated stuff

    this.feeService.processFees(recipientId);
  }
}

收费服务

@Service
public class FeeServiceImpl implements FeeService {
  @Autowired
  private AccountDao accountDao;

  @Override
  @Transactional // have also tried removing this
  public void processFees(Long recipientId) {
    // this next line is actually done through another service with a @Transactional annotation, but even without that annotation it still doesn't work
    Account systemAccount = this.accountDao.findById(recipientId);

    System.out.println("System account value: " + systemAccount.getFunds());

    systemAccount.addToFunds(5);

    System.out.println("Saving system account value: " + systemAccount.getFunds());
  }
}

测试

public class TheTest {
  // starts a @SpringBootTest with ```webEnvironment = WebEnvironment.RANDOM_PORT``` so it should start up a dedicated servlet container

  // also auto configures a WebTestClient

  @Test
  @Transactional
  public void testLocking() {
    // inserts a bunch of records to have some users and accounts to test with and does so via JPA, hence the need for @Transactional

    // code here to init an ExecutorService and a synchronized list

    // code here to create a series of threads via the ExecutorService that uses different user IDs as the sender, but the same ID for the recipient, hence the need for pessimistic locking
  }
}

如有必要,我可以放入测试代码,但是,我不确定还需要哪些其他详细信息。

结果输出(尤其是来自FeeServiceImplSystem.out.println调用)表明,所有线程都读取了相同的“系统帐户”值,因此保存的值也始终相同。

当应用程序启动时,该值为 0,所有线程都读取该 0,没有明显的锁定或等待。 我可以看到多个事务启动和提交(我在 Hibernate 的 TransactionImpl 上增加了日志记录级别),但是,这似乎无关紧要。

希望我忽略或做一些愚蠢的事情,但是,我无法弄清楚它是什么。

谢谢!

当然,这是我没想到的事情被埋葬了。

奇怪的是,我的表是使用 MyISAM 而不是 InnoDB 创建的,因为这在很长一段时间内都不是 MySQL 中表创建的默认设置。

所以,这就是我所做的:

  1. 我以为我使用的是 MySQL 8.0。 当使用未具体命名版本的 JDBC 连接字符串时,结果是 TestContainers 默认值(在我的情况下为 5.7.22)。 所以我解决了这个问题。

  2. 这仍然没有解决问题,因为 MyISAM 仍在使用中。 事实证明这是因为我的配置中有一个传统的方言设置。 将其更新为 MySQL57Dialect 之类的内容更正了这一点。

这实际上也解释了我在 JUnit 测试中看到的“奇怪”行为,因为值立即弹出到数据库中而不是回滚等。

我希望这可以帮助其他人在未来!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM