Spring JPA自定义SQL分页查询

JPA自定义SQL分页查询

【存在的问题】

在使用Spring JPA原生SQL进行分页查询时存在如下问题:

1、使用List转Page工具类导致查询速度较慢,原因在于先把所有数据查询出来返回成List,然后再进行分页转换

常用ListConvertToPageUtils如下:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import java.util.ArrayList;
import java.util.List;

public class ListConvertToPageUtils {
    public static <T> Page<T> listConvertToPage(List<T> list, Pageable pageable) {

        int start = ( int )pageable.getOffset();
        int end = (start + pageable.getPageSize()) > list.size() ? list.size() : (start + pageable.getPageSize());
        if(start > end){
            return new PageImpl<T>(new ArrayList<>(), pageable, list.size());
        }
        return new PageImpl<T>(list.subList(start, end), pageable, list.size());
    }
}

使用示例

Page<Event> events = ListConvertToPageUtils.listConvertToPage(list, pageable);

2、使用countQuery造成书写麻烦,代码显得过于臃肿

@Query(value = "SELECT  * FROM log where createtime BETWEEN :startTime AND :stopTime" ,
      countQuery = "SELECT  COUNT(1)  FROM  log where createtime BETWEEN :startTime AND :stopTime",
      nativeQuery = true)
Page<AlertLog> getLogByPage(Pageable pageable, Timestamp startTime, Timestamp stopTime);

【正确书写方法】

不需要使用countQuery即可实现分页,需要注意的是SQL语句的书写规范,主表必须使用别名

常见错误一

主表不加别名,造成select count(where)报错

@Query(value = "SELECT  username,userid,ip,path,name,type,createtime " +
            "FROM  system_log where createtime BETWEEN :startTime AND :stopTime ORDER BY createtime DESC" ,
            nativeQuery = true)
    Page<SystemLogVO> getSystemLog(Pageable pageable, Timestamp startTime, Timestamp stopTime);

在这里插入图片描述

select count(where) FROM  system_log where createtime BETWEEN ? AND ?

加上别名后正常查出结果
在这里插入图片描述

select count(T) FROM  system_log T where createtime BETWEEN ? AND ?

常见错误二

SQL语句不规范,造成select count(WHERE)报错

@Query(value = "SELECT DISTINCT T1.typename AS algoname,T2.type AS isopen,T3.*,T3.camurl AS cam_url FROM(" +
        "select * from analyse_apply " +
        "WHERE typeid in (SELECT id from algo_model_sub_type WHERE  id =:id)) T1\n" +
        "LEFT JOIN analyse_camera T2\n" +
        "ON T1.id = T2.analyseid\n" +
        "LEFT JOIN (SELECT  * FROM camera WHERE districtid in :idlist AND name Like :name) T3\n" +
        "ON T2.cameraid = T3.id\n" +
        "WHERE T3.id is not null",
        nativeQuery = true)
Page<CameraVO> findAllCameraById(Pageable pageable,Integer id, List<Integer> idlist, String name);

在这里插入图片描述
有人会觉得是不是没加别名造成的,加上之后重新执行仍然报错
在这里插入图片描述

--查询SQL
SELECT DISTINCT T1.typename AS algoname,T2.type AS isopen,T3.*,T3.camurl AS cam_url FROM(select * from analyse_apply T11 WHERE typeid in (SELECT id from algo_model_sub_type T12 WHERE  id =?)) T1
LEFT JOIN analyse_camera T2
ON T1.id = T2.analyseid
LEFT JOIN (SELECT  * FROM camera T13 WHERE districtid in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) AND name Like ?) T3
ON T2.cameraid = T3.id
WHERE T3.id is not null limit ?
--生成的count SQL
select count(DISTINCT T11) from analyse_apply T11 WHERE typeid in (SELECT id from algo_model_sub_type T12 WHERE  id =?)) T1
LEFT JOIN analyse_camera T2
ON T1.id = T2.analyseid
LEFT JOIN (SELECT  * FROM camera T13 WHERE districtid in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) AND name Like ?) T3
ON T2.cameraid = T3.id
WHERE T3.id is not null

仔细对比会发现countSQL与查询SQL并不太一样,造成此问题的原因是SQL语句主表与关联表书写不规范

正确示例如下

@Query(value = "SELECT DISTINCT T1.algoname,T2.*,T2.camurl AS cam_url " +
        "FROM camera_administer T1 \n" +
        "LEFT JOIN (SELECT  * FROM camera WHERE districtid in (:idlist) AND name Like :name) T2\n" +
        "ON T1.cameraid = T2.id " +
        "WHERE T2.id is not null " +
        "AND T1.algoid in (SELECT id from algo_model_sub_type WHERE  id =:id) ",
        nativeQuery = true)
Page<CameraVO> findAllCameraById(Pageable pageable,Integer id, List<Integer> idlist, String name);

在这里插入图片描述
我们知道在进行LEFT JOIN的时候,结果数据的数量就等于主表查询结果的数量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值