Spring Data JPA坑點記錄

一、分頁查詢

場景:動態(tài)查詢,分頁查詢,根據(jù)傳入不同的狀態(tài),分別查詢不同數(shù)據(jù)表,并且在傳入page對象之前用map進行VO轉(zhuǎn)換。而pageable的使用地方不同影響到了分頁數(shù)據(jù)的正確性,以此進行探討。

  • pageable使用于new PageImpl<>中,且直到最后才將List -> Page
  • pageable使用于findAll()中

前提:Page對象封于VO內(nèi),返回數(shù)據(jù)包括了分頁數(shù)據(jù)

@ApiModelProperty("記錄")
private Page<ActivityRecordVO> activityRecordVOList;

@ApiModelProperty("數(shù)量")
private Integer num = 0;

@ApiModelProperty("金額")
private BigDecimal totalMoney = BigDecimal.valueOf(0);

錯誤運用:

List<ActivityRecordVO> activityRecordVOList = new ArrayList<>();

        if (receiveSendRecordRequestVO.getSendOrReceiveType() == SendOrReceiveType.RECEIVE) {

            List<ChallengeRecord> challengeRecordList = challengeRecordDao.findByUserIdAndDeleteType(userId,
                    DeleteType.FALSE);
            if (!CollectionUtils.isEmpty(challengeRecordList)) {
                activityRecordVOList = challengeRecordList.stream()
                        .map(this::challengeRecordToActivityRecordVO)
                        .collect(Collectors.toList());
            }

        } else if (receiveSendRecordRequestVO.getSendOrReceiveType() == SendOrReceiveType.SEND) {

            List<Activity> activityList = activityDao.findByUserIdAndDeleteType(userId, DeleteType.FALSE);
            if (!CollectionUtils.isEmpty(activityList)) {
                activityRecordVOList = activityList.stream()
                        .map(this::activityTOActivityRecordVO)
                        .collect(Collectors.toList());
            }
        }
activityReceiveSendRecordVO.setActivityRecordVOList(new PageImpl<>(activityRecordVOList,
                    pageable, activityRecordVOList.size()));

解析:傳入的pageable只在set進VO的時候,用new PageIml將List轉(zhuǎn)為page對象,前端報的問題 雖然總頁數(shù)、總條數(shù)均為正確,但第一頁的條數(shù)是全部 ,數(shù)據(jù)異常!

正確參考做法:

采用Specifications先根據(jù)查詢條件動態(tài)查詢并map出相應(yīng)分頁對象(此塊代碼因需求而異),這時 findAll 傳入的pageable是生效的,便會顯現(xiàn)正確的分頁信息。

代碼塊參考:

xxxCommonSpecUtil 是自封的specification工具類,與原生spring data jpa原生查詢方法類似。

Page<ActivityRecordVO> page = new PageImpl<>(activityRecordVOList, pageable, activityRecordVOList.size());

        if (receiveSendRecordRequestVO.getSendOrReceiveType() == SendOrReceiveType.RECEIVE) {

            Specifications<ChallengeRecord> spec = Specifications.where(
                    challengeCommonSpecUtil.equal("userId", userId))
                    .and(challengeCommonSpecUtil.equal("deleteType", DeleteType.FALSE));
            page = challengeRecordDao.findAll(spec, pageable).map(this::challengeRecordToActivityRecordVO);

        } else if (receiveSendRecordRequestVO.getSendOrReceiveType() == SendOrReceiveType.SEND) {

            Specifications<Activity> spec = Specifications.where(
                    activityCommonSpecUtil.equal("userId", userId))
                    .and(activityCommonSpecUtil.equal("deleteType", DeleteType.FALSE));
            page = activityDao.findAll(spec, pageable).map(this::activityTOActivityRecordVO);
        }

注:activityReceiveSendRecordVO為封裝的VO,包含了返回的Page對象

activityReceiveSendRecordVO.setActivityRecordVOList(page);

二、事務(wù),更新實體,并查詢

場景:@Transactional事務(wù),更新用戶余額(處理并發(fā)問題),更新完畢返回VO帶上用戶剩余余額,但卻非更新后的余額。

更新邏輯代碼:

userWebService.updateBalanceAfterTransaction(userId, transactionRecordAddVO.getMoney(),user.getBalance());
@Transactional
    public void updateBalanceAfterTransaction(Integer userId, BigDecimal money, BigDecimal userBalance) {
        int i = userDao.updateBalanceAfterTransaction(userId, money, userBalance);
        if (i == 0) {
            throw new ValidationException(MessageCodes.TRANSACTION_RECEIVE_IS_ERROR);
        }
    }
@Modifying
    @Query(value = "update User u set u.balance = u.balance - ?2 where u.id = ?1 and u.balance = ?3")
    int updateBalanceAfterTransaction(Integer userId, BigDecimal money, BigDecimal userBalance);

解析:
在jpa中使用 @Modifying ,雖然事務(wù)已經(jīng)能夠更新,但是在循環(huán)更新的時候,執(zhí)行modify語句后的查詢的實體仍然是沒有更新的。

執(zhí)行完modifying query, EntityManager可能會包含過時的數(shù)據(jù),因為EntityManager不會自動清除實體。
只有添加clearAutomatically屬性,EntityManager才會自動清除實體對象。

添加代碼:

@Modifying(clearAutomatically = true)

改正后的示例代碼:

@Modifying(clearAutomatically = true)
    @Query(value = "update User u set u.balance = u.balance - ?2 where u.id = ?1 and u.balance = ?3")
    int updateBalanceAfterTransaction(Integer userId, BigDecimal money, BigDecimal userBalance);

總結(jié)

使用了這么長時間spring data jpa,覺得Specifications巨好用,也不容易出錯,也是我喜歡的編碼風(fēng)格,而new PageImpl<>()這種簡單粗暴的方法我一般都用在查詢數(shù)據(jù)關(guān)聯(lián)太多表的情況,在最后直接返回,更深層次的還需要再探討!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容