[英]How to handle transactions with concurrent access in Spring
I have a service with one method:我有一种方法的服务:
@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);
}
}
}
And I'm running Gatling with 2 parallel calls config with testModelId = 1L
argument.而且我正在使用带有testModelId = 1L
参数的 2 个并行调用配置运行 Gatling。 As a result of these calls I'm getting error:由于这些调用,我收到错误:
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "test_model_pkey"
What I can see from logs is that two calls have entered this method at once and each printed log我从日志中看到的是两个调用一次进入了这个方法,并且每个打印的日志
"Saved with id: 1"
"Saved with id: 1"
I assumed that adding transaction annotation on this method would block one of the calls on the line testRepository.findOne(testModelId)
until other call finishes its execution, but as I can see from logs it's working in a different way.我假设在此方法上添加事务注释会阻止testRepository.findOne(testModelId)
行中的一个调用,直到其他调用完成其执行,但正如我从日志中看到的那样,它以不同的方式工作。
So my question is how transaction works in this case when concurrent access appears?所以我的问题是当并发访问出现时,在这种情况下事务如何工作? And how can I handle this case with concurrent access?以及如何通过并发访问处理这种情况?
I have a service with one method:我有一种方法的服务:
@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);
}
}
}
And I'm running Gatling with 2 parallel calls config with testModelId = 1L
argument.我正在使用带有testModelId = 1L
参数的2个并行调用config运行Gatling。 As a result of these calls I'm getting error:这些调用的结果是我得到了错误:
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "test_model_pkey"
What I can see from logs is that two calls have entered this method at once and each printed log我从日志中看到的是,有两个调用一次输入了此方法,每个打印的日志
"Saved with id: 1"
"Saved with id: 1"
I assumed that adding transaction annotation on this method would block one of the calls on the line testRepository.findOne(testModelId)
until other call finishes its execution, but as I can see from logs it's working in a different way.我假设在此方法上添加事务注释会阻塞对testRepository.findOne(testModelId)
的调用,直到其他调用完成执行为止,但是正如我从日志中看到的那样,它以不同的方式工作。
So my question is how transaction works in this case when concurrent access appears?所以我的问题是,当出现并发访问时,事务在这种情况下如何工作? And how can I handle this case with concurrent access?我如何通过并发访问来处理这种情况?
I have a service with one method:我有一种方法的服务:
@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);
}
}
}
And I'm running Gatling with 2 parallel calls config with testModelId = 1L
argument.我正在使用带有testModelId = 1L
参数的2个并行调用config运行Gatling。 As a result of these calls I'm getting error:这些调用的结果是我得到了错误:
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "test_model_pkey"
What I can see from logs is that two calls have entered this method at once and each printed log我从日志中看到的是,有两个调用一次输入了此方法,每个打印的日志
"Saved with id: 1"
"Saved with id: 1"
I assumed that adding transaction annotation on this method would block one of the calls on the line testRepository.findOne(testModelId)
until other call finishes its execution, but as I can see from logs it's working in a different way.我假设在此方法上添加事务注释会阻塞对testRepository.findOne(testModelId)
的调用,直到其他调用完成执行为止,但是正如我从日志中看到的那样,它以不同的方式工作。
So my question is how transaction works in this case when concurrent access appears?所以我的问题是,当出现并发访问时,事务在这种情况下如何工作? And how can I handle this case with concurrent access?我如何通过并发访问来处理这种情况?
You could use optimistic locking, just as shown in this Baeldung guide .您可以使用乐观锁定,正如本Baeldung 指南中所示。 A little summary of what must be done.必须做的小总结。
bigint (or anything similar)
version field to the tables that were modified in the commonly deadlocked transactions (database wise)向在常见死锁事务中修改的表添加一个bigint (or anything similar)
版本字段(数据库方面)@Version
to the JPA model's newly created version field在 JPA 模型新创建的版本字段中添加注解@Version
With a quite simple solution, most of my deadlocked issues disappear.通过一个非常简单的解决方案,我的大部分僵局问题都消失了。 Hope it helps somebody else.希望它可以帮助别人。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.