Ore*_*est 6 java spring spring-transactions spring-data
我有一种方法的服务:
@Service
public class DefaultTestService implements TestService {
private static final Logger LOGGER = Logger.getLogger(DefaultTestService.class);
@Autowired
private TestRepository testRepository;
@Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE)
@Override
public void incrementAndGet(Long testModelId) {
LOGGER.debug("Transaction is active: " + TransactionSynchronizationManager.isActualTransactionActive());
final TestModel tm = testRepository.findOne(testModelId);
if (tm != null) {
LOGGER.debug("Updated " + testModelId + " from value: " + tm.getValue());
tm.setValue(tm.getValue() + 1);
testRepository.save(tm);
} else {
LOGGER.debug("Saved with id: " + testModelId);
final TestModel ntm = new TestModel();
ntm.setId(testModelId);
testRepository.save(ntm);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我正在运行带有 2 个带有参数的并行调用配置的加特林testModelId = 1L。由于这些调用,我收到错误:
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "test_model_pkey"
Run Code Online (Sandbox Code Playgroud)
我从日志中可以看到的是,两个调用同时进入了这个方法,并且每个打印的日志
"Saved with id: 1"
"Saved with id: 1"
Run Code Online (Sandbox Code Playgroud)
我假设在此方法上添加事务注释会阻止线路上的一个调用,testRepository.findOne(testModelId)直到其他调用完成其执行,但正如我从日志中看到的,它以不同的方式工作。
所以我的问题是,当出现并发访问时,事务如何工作?我该如何处理并发访问的情况?
事务意味着在事务边界内执行的对持久对象的所有修改将:
在这种情况下交易如何进行?
2 个线程之一到达事务末尾并成功提交。另一个线程到达事务末尾,但由于违反约束而未能提交,因此第二个事务以“回滚”状态终止。
为什么findOne第二笔交易没有被阻止?
原因很简单,尽管事务级别为 SERIALIZABLE,但没有要锁定的行。findOne在两个事务中都没有返回结果,并且没有任何内容被锁定(当然,如果第一个事务在第二个事务执行之前提交findOne:这是另一个故事)。
如何在特定情况下处理并发事务(即插入新行时违反 PK 约束)?
最常见的策略是让数据库将 id 分配给新行 - 借助序列 -
(作为一个实验,您可以尝试将隔离级别设置为 READ_UNCOMMITED,以便第二个事务可以读取第一个事务中未提交的更改。我不确定您是否注意到任何差异,因为如果在findOne第二个事务中在第一个事务之前执行testRepository.save(ntm);,它将仍然没有返回结果)
一般情况下如何处理并发修改导致的事务回滚?
这实际上取决于您的用例。基本上你可以选择:
请注意,如果事务在回滚状态下终止:事务期间修改的持久对象图不会恢复到其原始状态。
请注意,使用隔离级别 SERIALIZABLE 可能会导致巨大的性能问题,并且通常仅用于关键和偶尔的事务。
| 归档时间: |
|
| 查看次数: |
7022 次 |
| 最近记录: |