我有一种方法的服务:
@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);
}
}
}
我正在使用带有testModelId = 1L
参数的2个并行调用config运行Gatling 。这些调用的结果是我得到了错误:
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "test_model_pkey"
我从日志中看到的是,有两个调用一次输入了此方法,每个打印的日志
"Saved with id: 1"
"Saved with id: 1"
我假设在此方法上添加事务注释会阻塞该行中的一个调用,testRepository.findOne(testModelId)
直到其他调用完成执行为止,但是从日志中可以看出,它以不同的方式工作。
所以我的问题是,当出现并发访问时,事务在这种情况下如何工作?我如何通过并发访问来处理这种情况?
事务意味着在事务边界内执行的对持久对象的所有修改将是:
在这种情况下,交易如何运作?
2个线程之一到达事务结束并成功提交。另一个线程到达事务的末尾,由于违反约束而无法提交,因此第二个事务以“回滚”状态终止。
为什么findOne
第二笔交易没有被冻结?
仅仅因为,尽管具有SERIALIZABLE事务级别,但是没有要锁定的行。findOne
在这两个事务中均不返回任何结果,并且没有任何结果被锁定(当然,如果在执行第二个事务之前提交了第一个事务findOne
:这是另一个故事)。
如何在您的特定情况下处理并发事务(即在插入新行时违反PK约束)?
最常见的策略是在序列的帮助下,让数据库将ID分配给新行
(作为一个实验,您可以尝试将隔离级别设置为READ_UNCOMMITED,以便第二个事务可以读取第一个事务的未提交更改。我不确定您会注意到任何区别,因为如果findOne
在第二个事务中执行的testRepository.save(ntm);
是第一个事务之前的执行仍未返回任何结果)
通常如何处理由于并发修改而导致的事务回滚?
这实际上取决于您的用例。基本上,您可以选择:
请注意,如果事务以回滚状态终止:在事务期间修改的持久对象图不会恢复为其原始状态。
请注意,使用隔离级别SERIALIZABLE可能会导致巨大的性能问题,并且通常仅用于关键和偶然的事务。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句