我正在使用JPA的弹性数据jpa的乐观锁和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 =?
第三次执行两次,有人可以解释这个吗?!
非常感谢!