我有一个Spring启动应用程序和postgreSQL数据库。我想做一个隔离SERIALIZABLE或REPEATABLE_READ的事务。目标是在事务运行时更新一个名为产品的特定实体,给定一个id和担保,其他人不能读取/更新/删除该实体。
我开发了下面的代码来测试@Transactional(隔离=隔离. SERIALIZABLE)。我在findById(SELECT)和保存(UPDATE)(均由JpaRepository定义)之间放置了一个睡眠线程,以同时运行两个事务,更新相同的产品,间隔5秒。
@Autowired
private IQueryProductRepository queryProductRepository;
@Autowired
private ICommandProductRepository commandProductRepository;
@Override
public ResponseEntity updateProduct(String id) {
return ResponseEntity.ok(update(id, "name changed"));
}
@Transactional(rollbackFor = Exception.class, isolation = Isolation.SERIALIZABLE)
public Product update(String id, String name){
Optional<Product> optional = queryProductRepository.findById(id);
Product product = optional.get();
System.err.println(LocalDateTime.now() + " - SLEEP");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println(LocalDateTime.now() + " - WAKE");
product.setName(name);
product = commandProductRepository.save(product);
return product;
}
**Output:**
2021-07-15T20:44:33.584670570 - SLEEP
2021-07-15T20:44:41.377932607 - SLEEP
2021-07-15T20:44:53.585632949 - WAKE
2021-07-15T20:45:01.378063371 - WAKE
预期第一个事务锁定产品实体,第二个事务只能在第一个事务结束时更新。但结果并非如此,当我执行第一个事务时,打印“SLEEP”并进入睡眠线程,当我执行第二个事务时,第一个事务后5秒,“SLEEP”立即打印,这意味着第二个事务访问了实体,而第一个事务尚未结束。
我不明白为什么会发生这种情况,因为我把隔离=Isolation. SERIALIZABLE?
我的应用程序属性文件:
spring.datasource.url=jdbc:postgresql://localhost:5432/cc
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.show-sql=true
这不是SERIALIZABLE隔离级别的工作方式。您所描述的或多或少是一个单用户系统。SERIALIZABLE隔离级别背后的思想是它仍然允许并发,但确保并发事务不会相互干扰。如果多个事务的重叠不是SERIALIZABLE,即不提供防止幻读的保证,则一个事务可能会失败。这只是一个外行的解释,但如果您想要单个用户执行,您将需要对共享对象进行某种悲观锁。