1

I am using the JPA's optimistic lock with spring data jpa and using spring retry for ConcurrencyFailureException, but something occurs that i can't understand.

my test code just like below:

@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;
    }
}

i added a breakpoint at Account account = accountService.findOne("abee19e08d3d4ee79d9f831c3ed78344");, then i run a transaction in mysql to change the version of Account.

After that, the testOptimistic method throws the ConcurrencyFailureException and the method retried, in my opinion, it will success the next time but it failed again, finally it success at the third time, i just can not understand.

the logs of Hibernate SQL showed below

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 =?

you can see that select version from account where id =? execute two times at the third time, anyone can explain this?!

Thank you very much!

harshavmb
  • 3,404
  • 3
  • 21
  • 55
xuxukang
  • 43
  • 5
  • Check wheter it is OptimisticLockingFailureException or PessimisticLockingFailureException. My best bet is that both are thrown that would explain 2 retries (1 for pessimistic, and that fails due to optimistic - last retry succeses because of optimistic) – Antoniossss Jun 08 '17 at 06:15
  • i changed the `ConcurrencyFailureException` to `OptimisticLockingFailureException`, it still retry two times and success at the last time. – xuxukang Jun 08 '17 at 10:15
  • Catch exceptions and log them - remember to rethrow after logging. – Antoniossss Jun 08 '17 at 12:30

0 Answers0