jpa使用@Query自定義分頁(yè)查詢報(bào)錯(cuò)You have an error in your SQL syntax; check the manual that corresponds t...

因?yàn)闃I(yè)務(wù)需要一個(gè)分頁(yè)查詢的列表,但是對(duì)數(shù)據(jù)內(nèi)容有去重的需求,簡(jiǎn)單了解了JPA的@Query注解使用方式后就直接用了自定義sql來(lái)做,測(cè)試通過(guò)后上線測(cè)了兩筆也沒(méi)問(wèn)題就沒(méi)太在意。但是隔了一天運(yùn)營(yíng)就反應(yīng)說(shuō)功能報(bào)錯(cuò)了,而且我去看日志發(fā)現(xiàn)報(bào)錯(cuò)很奇怪,是sql語(yǔ)法錯(cuò)誤,如果是sql語(yǔ)法錯(cuò)誤那么是怎么測(cè)試通過(guò)的呢?報(bào)錯(cuò)如下:

Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '*) from test a where a.id in (select max(b.id) from test' at line 1

但是我寫的sql在結(jié)果集那里是沒(méi)有括號(hào)的

select a.* from test a where a.id in (select max(b.id) from test b group by name) order by a.id desc;

這個(gè)括號(hào)是如何生成的呢,經(jīng)過(guò)反復(fù)的測(cè)試樣本測(cè)試之后發(fā)現(xiàn),這個(gè)是jpa在做分頁(yè)需要查詢總數(shù)量的時(shí)候自動(dòng)加了一個(gè)sql

select count(a.*) from test a where a.id in (select max(b.id) from test b group by name) order by a.id desc;

這個(gè)語(yǔ)法在高版本的mysql中是不支持的,只能寫成

select count(*) from test a where a.id in (select max(b.id) from test b group by name) order by a.id desc;

那么一開(kāi)始又是怎么測(cè)試通過(guò)的呢,為什么有的查詢(pageSize>count)jpa不會(huì)加上count(pageSize<count)而有的又會(huì)自動(dòng)count?
debug到j(luò)pa源碼之后,才知道jpa在執(zhí)行分頁(yè)查詢的時(shí)候有對(duì)記錄總數(shù)的獲取方式做優(yōu)化,具體實(shí)現(xiàn)在org.springframework.data.repository.support.PageableExecutionUtils的getPage方法,jpa就是通過(guò)這個(gè)方法將結(jié)果集封裝成page,而其中在計(jì)算total的時(shí)候會(huì)判斷當(dāng)前頁(yè)偏移量,當(dāng)前pageSize是否大于查詢條件的pageSize,如果能直接計(jì)算出來(lái)total就不會(huì)調(diào)用count去數(shù)據(jù)庫(kù)查詢記錄總數(shù)

/**
     * Constructs a {@link Page} based on the given {@code content}, {@link Pageable} and {@link Supplier} applying
     * optimizations. The construction of {@link Page} omits a count query if the total can be determined based on the
     * result size and {@link Pageable}.
     *
     * @param content must not be {@literal null}.
     * @param pageable must not be {@literal null}.
     * @param totalSupplier must not be {@literal null}.
     * @return the {@link Page}.
     */
    public static <T> Page<T> getPage(List<T> content, Pageable pageable, LongSupplier totalSupplier) {

        Assert.notNull(content, "Content must not be null!");
        Assert.notNull(pageable, "Pageable must not be null!");
        Assert.notNull(totalSupplier, "TotalSupplier must not be null!");

        if (pageable.isUnpaged() || pageable.getOffset() == 0) {

            if (pageable.isUnpaged() || pageable.getPageSize() > content.size()) {
                return new PageImpl<>(content, pageable, content.size());
            }

            return new PageImpl<>(content, pageable, totalSupplier.getAsLong());
        }

        if (content.size() != 0 && pageable.getPageSize() > content.size()) {
            return new PageImpl<>(content, pageable, pageable.getOffset() + content.size());
        }

        return new PageImpl<>(content, pageable, totalSupplier.getAsLong());
    }

jpa生成count語(yǔ)句的邏輯也非常簡(jiǎn)單,就是做了一個(gè)正則來(lái)替換查詢語(yǔ)句,對(duì)于我這個(gè)查詢,count語(yǔ)句就變成了上面那個(gè)樣子,jpa也考慮到這樣生成的sql可能是有問(wèn)題的,所以@Query注解提供了countQuery屬性,查詢語(yǔ)句比較復(fù)雜的時(shí)候可以自己寫一個(gè)count語(yǔ)句給jpa使用。

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

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