1

我正在使用 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 =?第三次执行两次,任何人都可以解释一下吗?!

非常感谢!

4

0 回答 0