我正在使用 JPA 的乐观锁和 spring data jpa 并使用 spring retry for ConcurrencyFailureException
,但是发生了一些我无法理解的事情。
我的测试代码如下:
@Repository
public interface AccountRepo extends JpaRepository<Account, String>, JpaSpecificationExecutor<Account> {
@Lock(LockModeType.OPTIMISTIC)
@Query(value = "select a from Account a where a.id = :id")
Account findOne(@Param("id") String id);
}
--------------------------------------------------------------------
@Service
public class AccountService {
@Qualifier("accountRepo")
@Autowired
private AccountRepo accountRepo;
@Transactional(value = "transactionManager", isolation = Isolation.READ_COMMITTED)
public Account saveOne(Account account){
return accountRepo.save(account);
}
public Account findOne(String id){
return accountRepo.findOne(id);
}
}
----------------------------------------------------------------------
@Transactional(value = "transactionManager", isolation = Isolation.READ_COMMITTED)
@Retryable(value = {ConcurrencyFailureException.class}, backoff = @Backoff(delay = 3000))
public void testOptimistic(){
Account account = accountService.findOne("abee19e08d3d4ee79d9f831c3ed78344");
account.setHistoryIncome(new BigDecimal("3300"));
account.setTodayTotalIncome(new BigDecimal("3300"));
account.setTodayOnlineIncome(new BigDecimal("3300"));
account.setAvaliableAmount(new BigDecimal("3300"));
accountService.saveOne(account);
AccountOperRecord accountOperRecord = new AccountOperRecord();
accountOperRecord.setId(BuildNoUtil.buildAccountOperRecordId());
accountOperRecord.setTrxType(TrxTypeEnum.EXPENSE.name());
accountOperRecord.setAvaliableAmount(account.getAvaliableAmount());
accountOperRecord.setOperAmount(new BigDecimal("100"));
accountOperRecord.setOrderId("4028b8815c813d64015c813e17f50000");
accountOperRecord.setOutTradeNo("2015061121001004400068549373");
accountOperRecord.setAccountId("abee19e08d3d4ee79d9f831c3ed78344");
accountOperRecordService.saveOne(accountOperRecord);
}
----------------------------------------------------------------------
@Controller
public class TestController extends BaseController {
@RequestMapping(value = "testOptimistic")
@ResponseBody
public JSONObject testOptimisticLock(){
JSONObject result = new JSONObject();
conjunctionService.testOptimistic();
result.put("code", "success");
return result;
}
}
我在 处添加了一个断点Account account = accountService.findOne("abee19e08d3d4ee79d9f831c3ed78344");
,然后我在 mysql 中运行一个事务来更改version
帐户。
之后,testOptimistic
方法抛出ConcurrencyFailureException
,方法重试,在我看来,下一次它会成功但又失败了,最后第三次成功了,我只是无法理解。
Hibernate SQL 的日志如下所示
Hibernate: select account0_.id as id1_0_, account0_.create_time as create_t2_0_, account0_.creater as creater3_0_, account0_.edit_time as edit_tim4_0_, account0_.editor as editor5_0_, account0_.remark as remark6_0_, account0_.address as address7_0_, account0_.avaliable_amount as avaliabl8_0_, account0_.email as email9_0_, account0_.history_expense as history10_0_, account0_.history_income as history11_0_, account0_.merchant_name as merchan12_0_, account0_.name as name13_0_, account0_.phone as phone14_0_, account0_.settled_amount as settled15_0_, account0_.settling_amount as settlin16_0_, account0_.splited_amount as splited17_0_, account0_.today_offline_income as today_o18_0_, account0_.today_online_income as today_o19_0_, account0_.today_total_income as today_t20_0_, account0_.version as version21_0_ from account account0_ where account0_.id=?
Hibernate: select accountope0_.id as id1_1_0_, accountope0_.account_id as account_2_1_0_, accountope0_.avaliable_amount as avaliabl3_1_0_, accountope0_.oper_amount as oper_amo4_1_0_, accountope0_.order_id as order_id5_1_0_, accountope0_.out_trade_no as out_trad6_1_0_, accountope0_.remit_id as remit_id7_1_0_, accountope0_.settle_id as settle_i8_1_0_, accountope0_.split_batch_id as split_ba9_1_0_, accountope0_.trx_type as trx_typ10_1_0_ from account_oper_record accountope0_ where accountope0_.id=?
Hibernate: insert into account_oper_record (account_id, avaliable_amount, oper_amount, order_id, out_trade_no, remit_id, settle_id, split_batch_id, trx_type, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: update account set avaliable_amount=?, history_income=?, today_online_income=?, today_total_income=?, version=? where id=? and version=?
Hibernate: select account0_.id as id1_0_, account0_.create_time as create_t2_0_, account0_.creater as creater3_0_, account0_.edit_time as edit_tim4_0_, account0_.editor as editor5_0_, account0_.remark as remark6_0_, account0_.address as address7_0_, account0_.avaliable_amount as avaliabl8_0_, account0_.email as email9_0_, account0_.history_expense as history10_0_, account0_.history_income as history11_0_, account0_.merchant_name as merchan12_0_, account0_.name as name13_0_, account0_.phone as phone14_0_, account0_.settled_amount as settled15_0_, account0_.settling_amount as settlin16_0_, account0_.splited_amount as splited17_0_, account0_.today_offline_income as today_o18_0_, account0_.today_online_income as today_o19_0_, account0_.today_total_income as today_t20_0_, account0_.version as version21_0_ from account account0_ where account0_.id=?
Hibernate: select accountope0_.id as id1_1_0_, accountope0_.account_id as account_2_1_0_, accountope0_.avaliable_amount as avaliabl3_1_0_, accountope0_.oper_amount as oper_amo4_1_0_, accountope0_.order_id as order_id5_1_0_, accountope0_.out_trade_no as out_trad6_1_0_, accountope0_.remit_id as remit_id7_1_0_, accountope0_.settle_id as settle_i8_1_0_, accountope0_.split_batch_id as split_ba9_1_0_, accountope0_.trx_type as trx_typ10_1_0_ from account_oper_record accountope0_ where accountope0_.id=?
Hibernate: insert into account_oper_record (account_id, avaliable_amount, oper_amount, order_id, out_trade_no, remit_id, settle_id, split_batch_id, trx_type, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: update account set avaliable_amount=?, history_income=?, today_online_income=?, today_total_income=?, version=? where id=? and version=?
Hibernate: select version from account where id =?
Hibernate: select account0_.id as id1_0_, account0_.create_time as create_t2_0_, account0_.creater as creater3_0_, account0_.edit_time as edit_tim4_0_, account0_.editor as editor5_0_, account0_.remark as remark6_0_, account0_.address as address7_0_, account0_.avaliable_amount as avaliabl8_0_, account0_.email as email9_0_, account0_.history_expense as history10_0_, account0_.history_income as history11_0_, account0_.merchant_name as merchan12_0_, account0_.name as name13_0_, account0_.phone as phone14_0_, account0_.settled_amount as settled15_0_, account0_.settling_amount as settlin16_0_, account0_.splited_amount as splited17_0_, account0_.today_offline_income as today_o18_0_, account0_.today_online_income as today_o19_0_, account0_.today_total_income as today_t20_0_, account0_.version as version21_0_ from account account0_ where account0_.id=?
Hibernate: select accountope0_.id as id1_1_0_, accountope0_.account_id as account_2_1_0_, accountope0_.avaliable_amount as avaliabl3_1_0_, accountope0_.oper_amount as oper_amo4_1_0_, accountope0_.order_id as order_id5_1_0_, accountope0_.out_trade_no as out_trad6_1_0_, accountope0_.remit_id as remit_id7_1_0_, accountope0_.settle_id as settle_i8_1_0_, accountope0_.split_batch_id as split_ba9_1_0_, accountope0_.trx_type as trx_typ10_1_0_ from account_oper_record accountope0_ where accountope0_.id=?
Hibernate: insert into account_oper_record (account_id, avaliable_amount, oper_amount, order_id, out_trade_no, remit_id, settle_id, split_batch_id, trx_type, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: update account set avaliable_amount=?, history_income=?, today_online_income=?, today_total_income=?, version=? where id=? and version=?
Hibernate: select version from account where id =?
Hibernate: select version from account where id =?
您可以看到select version from account where id =?
第三次执行两次,任何人都可以解释一下吗?!
非常感谢!