Java Spring JPA无法在一个线程中正确处理多个事务
我有以下代码片段。调用Java Spring JPA无法在一个线程中正确处理多个事务,java,hibernate,transactions,spring-data-jpa,Java,Hibernate,Transactions,Spring Data Jpa,我有以下代码片段。调用BatchSettleService.batchSettleWork(Array.asList([1,2,3]))之后。我发现账户余额只减少了1分贝。调试结果是每次accountRepository.findByIdForUpdate(2)返回原始帐户时,不会对过去的循环进行任何更改。我已经尝试了Isolation.SERIALIZABLElevel,但结果是一样的。我使用的数据库是MySQL 5.7.20 InnoDb引擎。JPA实现是Hibernate。我希望账户余额减
BatchSettleService.batchSettleWork(Array.asList([1,2,3]))之后
。我发现账户余额只减少了1分贝。调试结果是每次accountRepository.findByIdForUpdate(2)
返回原始帐户时,不会对过去的循环进行任何更改。我已经尝试了Isolation.SERIALIZABLE
level,但结果是一样的。我使用的数据库是MySQL 5.7.20 InnoDb引擎。JPA实现是Hibernate。我希望账户余额减少3。我对交易的理解是否有问题?提前谢谢你
@Service
public class BatchSettleService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private WorkSettleService workSettleService;
public List<WorkSettleResponse> batchSettleWork(List<Long> workIds) {
List<WorkSettleResponse> results= new ArrayList<>();
for(Long workId:workIds) {
try {
results.add(workSettleService.settleWork(new WorkSettleRequest(workId)));
} catch (WrappedException e) {
results.add(new WorkSettleResponse(workId,e.getErrCode(),e.getMessage()));
logger.error("Settle work failed for {}",workId,e);
}
}
return results;
}
}
public class WorkSettleService{
@Autowired
private AccountRepository accountRepository;
@Transactional(rollbackFor= {WorkSettleException.class,RuntimeException.class},propagation=Propagation.REQUIRES_NEW)
public WorkSettleResponse settleWork(WorkSettleRequest req){
Account account = accountRepository.findByIdForUpdate(2);
Integer balance = account .getBalance();
accountRepository.updateBalanceById(account.getId(),balance-1)
}
}
public interface AccountRepository extends Repository<Account, Integer> {
public Account findById(Integer id);
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select ac from Account a where a.id = ?1")
public Account findByIdForUpdate(Integer id);
@Modifying
@Query("update Account a set a.balance = ?2 where a.id = ?1")
public int updateBalanceById(Integer id,Integer balance);
}
@服务
公共类BatchSettleService{
私有记录器Logger=LoggerFactory.getLogger(getClass());
@自动连线
私人工作设置服务工作设置服务;
公共列表批处理结算工作(列表工作ID){
列表结果=新建ArrayList();
for(长工作ID:workIds){
试一试{
结果.add(workSettleService.settleWork(newworksettlerequest(workId)));
}捕获(包裹){
添加(新的工作设置响应(workId,e.getErrCode(),e.getMessage());
logger.error(“为{}解决工作失败”,工作ID,e);
}
}
返回结果;
}
}
公共类WorkSettleService{
@自动连线
私人帐户存储库;
@事务性(rollbackFor={WorkSettleException.class,RuntimeException.class},propagation=propagation.REQUIRES\u NEW)
公共工作设置响应结算工作(工作设置请求){
Account Account=accountRepository.findByIdForUpdate(2);
整数余额=account.getBalance();
accountRepository.updateBalanceById(account.getId(),balance-1)
}
}
公共接口AccountRepository扩展存储库{
公共帐户findById(整数id);
@锁(锁模式类型。悲观写入)
@查询(“从帐户a中选择ac,其中a.id=?1”)
公共帐户findByIdForUpdate(整数id);
@修改
@查询(“更新帐户a集合a.balance=?2,其中a.id=?1”)
public int updateBalanceById(整数id,整数余额);
}
这个案子已经解决了。
使用account.setBalance(balance-1)
而不是accountRepository.updateBalanceById(account.getId(),balance-1)
。
我不知道为什么
@Modifying
注释在@Query
从EM缓存获取数据时不更新EM缓存,这是Spring JPA实现的一点限制。@Gab是的,我想防止并发读取。但是我不清楚悲观的阅读和悲观的写作之间的区别,所以我选择了严格的。感谢您的评论悲观读防止并发写(共享锁)和悲观写防止并发读(独占锁)。您编写它的方式确实需要一个独占锁,@Query(“从帐户a中选择ac,其中a.id=?1”)中没有输入您的期望值correct@Gab是的,我现在觉得差别很明显。我幸运地做出了正确的选择,哈哈。你能帮我研究一下这个案子吗?