Spring Data JPA @Query使用原生SQL如何分页查询

问题描述

在Spring Boot应用程序中使用Spring Data JPA对某个表的记录进行分组统计,要求对两种状态下的个数和金额统计,并将这两种状态下的统计结果合成一条记录,并对统计后的记录进行分页查询。

产品名总数完成数完成金额
产品一106100
产品二2014260

由于需要用子查询,用JPQL不好实现(用JPQL写了后启动报错,因为JPQL 不支持使用子查询的方式来构建查询。在 JPQL 中,不能直接在 SELECT 语句中嵌套子查询),就想用原生SQL直接查询,至于分页想着直接传入Pageable pageable参数即可,刚开始写的统计方法如下:

@Query(value = "select t1.uuid, t1.title,t1.count as count1,coalesce(t2.count,0) as count2, coalesce(t2.sum_amount,0) as amount from (\n" +
            "  select uuid, title,count(*) as count\n" +
            "  from table_example\n" +
            "  where org = :org and state != 1\n" +
            "  group by uuid, title\n" +
            ") t1 \n" +
            "left join (\n" +
            "  select uuid, title, count(*) as count, sum(amount) as sum_amount\n" +
            "  from table_example\n" +
            "  where org = :org and state in (9, 10, 11)\n" +
            "  group by uuid, title\n" +
            ") t2 on t1.uuid = t2.uuid and t1.title = t2.title", nativeQuery = true)
    Page<Object[]> getStatistics(String org, Pageable pageable);

这段代码运行起来没有报错,同时测试了几个查询也没有问题。但在联调时发现如何满足条件的记录是n条,pageSize小于等于n时查询就报错,大于n时就没问题。

问题分析

Spring Data JPA @Query注解如果为原生SQL时,不支持直接传入Pageable pageable参数对查询结果分页,参见Spring官网的说明:

https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html
在这里插入图片描述

解决方法

方法一

按照Spring Data JPA @Query注解的说明,传入countQuery参数,参数值为查询满足条件记录的原生SQL语句,这样在Service层代码中直接传入Pageable参数就可以获取到分页后的结果,修改后的查询方法如下:

 @Query(value = "select t1.uuid, t1.title,t1.count as count1,coalesce(t2.count,0) as count2, coalesce(t2.sum_amount,0) as amount from (\n" +
            "  select uuid, title,count(*) as count\n" +
            "  from table_example\n" +
            "  where org = :org and state != 1\n" +
            "  group by uuid, title\n" +
            ") t1 \n" +
            "left join (\n" +
            "  select uuid, title, count(*) as count, sum(amount) as sum_amount\n" +
            "  from table_example\n" +
            "  where org = :org and state in (9, 10, 11)\n" +
            "  group by uuid, title\n" +
            ") t2 on t1.uuid = t2.uuid and t1.title = t2.title", 
		countQuery = "select count(*) from (\n" +
            "  select uuid, title,count(*) as count\n" +
            "  from table_example\n" +
            "  where org = :org and state != 1\n" +
            "  group by uuid, title\n" +
            ") t1 \n" +
            "left join (\n" +
            "  select uuid, title, count(*) as count, sum(amount) as sum_amount\n" +
            "  from table_example\n" +
            "  where org = :org and state in (9, 10, 11)\n" +
            "  group by uuid, title\n" +
            ") t2 on t1.uuid = t2.uuid and t1.title = t2.title", 
			nativeQuery = true)
    Page<Object[]> getStatistics(String org, Pageable pageable);

方法二

不使用Spring Data JPA进行分页,自己实现分页,Repository就要提供两个方法,一个方法查询分页记录,另一个方法查询总记录数:

@Query(value = "select t1.uuid, t1.title,t1.count as count1,coalesce(t2.count,0) as count2, coalesce(t2.sum_amount,0) as amount from (\n" +
            "  select uuid, title,count(*) as count\n" +
            "  from table_example\n" +
            "  where org = :org and state != 1\n" +
            "  group by uuid, title\n" +
            ") t1 \n" +
            "left join (\n" +
            "  select uuid, title, count(*) as count, sum(amount) as sum_amount\n" +
            "  from table_example\n" +
            "  where org = :org and state in (9, 10, 11)\n" +
            "  group by uuid, title\n" +
            ") t2 on t1.uuid = t2.uuid and t1.title = t2.title limit :offset, :limit", nativeQuery = true)
    Page<Object[]> getStatistics(String org,  int offset, int limit);
	
	
	 @Query(value = "select count(*) from (\n" +
            "  select uuid, title,count(*) as count\n" +
            "  from table_example\n" +
            "  where org = :org and state != 1\n" +
            "  group by uuid, title\n" +
            ") t1 \n" +
            "left join (\n" +
            "  select uuid, title, count(*) as count, sum(amount) as sum_amount\n" +
            "  from table_example\n" +
            "  where org = :org and state in (9, 10, 11)\n" +
            "  group by uuid, title\n" +
            ") t2 on t1.uuid = t2.uuid and t1.title = t2.title", nativeQuery = true)
    long getCount(String org);

在Service层代码中分别调用这两个方法返回分页结果:

 ...
 int offset = (pageNum - 1) * pageSize;
 List<Object[]> queryResult = repository.getStatistics(org, offset, pageSize);
        long total = repository.getCount(org);
        PageDTO<StatisticsDTO> result = new PageDTO<>();
        result.setPageNum(pageNum);
        result.setPageSize(pageSize);
        result.setTotalPage((int) Math.ceil((double) total / pageSize));
        result.setTotalSize(total);
        result.setData(queryResult.stream().map(item -> {
            StatisticsDTO dto = new StatisticsDTO();
            dto.setUuid((String) item[0]);
            dto.setitle((String) item[1]);
            dto.setCount1(Long.valueOf(String.valueOf(item[2])));
            dto.setCount2(Long.valueOf(String.valueOf(item[3])));
            dto.setAmount((BigDecimal) item[4]);
            return dto;
        }).collect(Collectors.toList()));
        return result;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值