在Spring Data JPA中使用@Query注解

目录

  • 前言
  • 示例
    • 简单示例
    • 只查询部分字段,映射到一个实体类中
    • 只查询部分字段时,也可以使用List<Object[]>接收返回值
    • 再复杂一些

前言

在以往写过几篇spring data jpa相关的文章,分别是
Spring Data JPA 使用JpaSpecificationExecutor实现多条件查询(分页查询和非分页查询)
Spring Data JPA实现分页多条件查询2

都是通过代码而不是sql来完成查询的,但是在做复杂情况的查询时,难免会用到@Query写sql语句。

示例

简单示例

在@Query中用:paramName标识参数,再用@Param来指定方法参数与查询语句中的参数之间的映射关系。
例如:

@Query("select r from RelationDO r where r.indexCode in :idList")
List<RelationDO> findByIdListIn(@Param("idList") Collection<String> idList);

只查询部分字段,映射到一个实体类中

注意类的路径写完整

@Query("SELECT new com.xxx.service.impl.bo.RecordBO(e.code, e.day, e.total, e.success, e.fail, e.app) " +"FROM RecordDO e " +"WHERE e.code = :code AND e.day = :day " +"AND e.app in :appCodes")
List<RecordBO> findCalendarDetail(@Param("code") String code,@Param("day") String day,@Param("appCodes") List<String> appCodes);

这里为什么映射了一个新的BO出来呢… 是RecordDO中有一个id字段,在实体类中添加了@Id注解(实体必须有@Id,不然会报错),这个id字段本来设计的是不会重复的,但是后续经过一些改动,它在某些情况下会重复了,这个时候就会有一个问题,我直接select整个RecordDO,id字段重复的它会当成同一条记录(不确定为什么,但是实际跑出来确实是这样),但我又不想再去改表结构,因此这里我select的时候直接省略了id字段,就正常了。(可能不是一个很好的解决方案,但是确实是可以这么做的)
只查询部分字段在表字段较多,所需字段比较少的时候还是可以用的。

只查询部分字段时,也可以使用List<Object[]>接收返回值

例如我现在需要用code和month查出这么一个结果:

[{"day":"20240601","result":[{"rate": 98.77"app": "0001"},{"rate": 95.32"app": "0002"}]},{"day":"20240602","result":[{"rate": 95.65"app": "0001"},{"rate": 96.89"app": "0002"}]},……
]

也就是说要把月份中的每一天抽取出来,再在下面放每个app对应的明细
这个时候写sql:

@Query("SELECT e.day, e.app, e.success, e.total" +"FROM RecordDO e " +"WHERE e.code = :code AND SUBSTRING(e.day, 1, 6) = :month AND e.total > 0")
List<Object[]> findByMonth(@Param("code") String code,@Param("month") String month);

调用上述方法后封装返回数据:

List<CalendarBO> calendarBOS = Lists.newArrayList();
List<Object[]> resultList = recordRepository.findByMonth(code,month);if (!CollectionUtils.isEmpty(resultList)){for (Object[] result : resultList) {String day = (String) result[0];String app = (String) result[1];Integer success = (Integer) result[2];Integer total = (Integer) result[3];double rate = (double) success * 100 / total ;double roundedRate = Math.round(rate * 100.0) / 100.0;CalendarBO.Result result = CalendarBO.Result.builder().app(app).rate(roundedRate).build();// 组装返回内容Optional<CalendarBO> optionalBO = calendarBOS.stream().filter(bo -> bo.getDay().equals(day)).findFirst();// 该日期值不存在则创建 存在则添加不同app的记录if (!optionalBO.isPresent()) {CalendarBO calendarBO = CalendarBO.builder().day(day).result(Collections.singletonList(result)).build();calendarBOS.add(calendarBO);}else {CalendarBO calendarBO = optionalBO.get();List<CalendarBO.Result> results = calendarBO.getResult();results.add(result);calendarBO.setAssessResult(results);}}
}

再复杂一些

通过beginMonth、endMonth和appCodes筛选,需要返回的数据格式如下
这里的pass是有一个标准rate,当data中success/total(rate) > 标准rate时单项视为pass,而total中的total则代表该月份区间共统计次数。

{"total": [{"total": 13,"pass": 13,"app": "0001"},{"total": 13,"pass": 12,"app": "0002"}],"data": [{"code": "101","month": 202406,"result": [{"total": 13,"success": 13,"rate": 100,"app": "0001"},{"total": 12,"success": 11,"rate": 92,"app": "0002"}]},{"code": "102","month": 202406,"result": [{"total": 15,"success": 15,"rate": 100,"app": "0001"}]},……]
}

此时的sql:

@Query("SELECT e.code, e.app, SUBSTRING(e.day, 1, 6), COUNT(e.statId), " +"SUM(CASE WHEN (CAST(e.success AS double) / e.total) >= :rate THEN 1 ELSE 0 END) " +"FROM RecordDO e " +"WHERE e.code = :code" +"    AND SUBSTRING(e.day, 1, 6) BETWEEN :beginMonth AND :endMonth " +"    AND ((:appCodes) IS NULL OR e.app IN (:appCodes)) AND e.total > 0 " +"GROUP BY e.code, e.app, SUBSTRING(e.day, 1, 6)")
List<Object[]> findByCodeGroupBy(@Param("code") String code,@Param("beginMonth") String beginMonth,@Param("endMonth") String endMonth,@Param("appCodes") List<String> appCodes,@Param("rate") Double rate);

这样就直接把总数和pass的计数给取出来了(statId和总数可以对应)
调用上述方法后封装返回数据,和之前基本一致:

// 根据分类计算总数的映射
Map<String, Integer> totalCounts = new HashMap<>();
Map<String, Integer> passCounts = new HashMap<>();
//返回的明细对象
List<DataBO> dataList = new ArrayList<>();//假设已获取到code和标准rate的对应关系passRate
for (Map.Entry<String, Double> entry : passRate.entrySet()) {List<Object[]> resultList = recordRepository.findByCodeGroupBy(entry.getKey(), reqBO.getBeginMonth(),reqBO.getEndMonth(), reqBO.getAppCodeList(), entry.getValue());if (CollectionUtils.isEmpty(resultList)) {continue;}for (Object[] result : resultList) {String code = (String) result[0];String app = (String) result[1];String month = (String) result[2];Long totalLong = (Long) result[3];String total = totalLong.toString();Long successLong = (Long) result[4];String success = successLong.toString();double rateDouble = Double.parseDouble(success) / Double.parseDouble(total);String rate = String.format("%.2f", rateDouble * 100);DataBO.Result result = DataBO.Result.builder().total(total).success(success).rate(rate).app(app).build();//查看dataList中是否该编码和月份的数据已存在 不存在则新建 存在则获取DataBO data = dataList.stream().filter(a -> a.getCode().equals(code) && a.getMonth().equals(month)).findFirst().orElseGet(() -> {DataBO newAccount = new DataBO();newAccount.setCode(code);newAccount.setMonth(month);accountList.add(newAccount);return newAccount;});if (data.getResult() == null) {data.setResult(Lists.newArrayList());}data.getResult().add(result);// 更新统计totalCounts.put(app, totalCounts.getOrDefault(app, 0) + Integer.parseInt(total));passCounts.put(app, passCounts.getOrDefault(app, 0) + Integer.parseInt(success));}
}
//组装统计类
totalCounts.entrySet().stream().map(entry -> {String app = entry.getKey();int total = entry.getValue();int pass = passCounts.getOrDefault(app, 0);TotalCountBO totalCount = new TotalCountBO();totalCount.setAppCode(app);totalCount.setTotal(String.valueOf(total));totalCount.setPass(String.valueOf(pass));return totalCount;}).collect(Collectors.toList());return RespBO.builder().data(dataList).total(totalCounts).build();

匆忙所写,不确定有没有问题,有的话联系我~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/38887.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python 笔试面试八股(自用版~)

1 解释型和编译型语言的区别 解释是翻译一句执行一句&#xff0c;更灵活&#xff0c;eg&#xff1a;python; 解释成机器能理解的指令&#xff0c;而不是二进制码 编译是整个源程序编译成机器可以直接执行的二进制可运行的程序&#xff0c;再运行这个程序 比如c 2 简述下 Pyth…

运维锅总详解RocketMQ

本文尝试从Apache RocketMQ的简介、主要组件及其作用、3种部署模式、Controller集群模式工作流程、最佳实践等方面对其进行详细分析。希望对您有所帮助&#xff01; 一、Apache RocketMQ 简介 Apache RocketMQ 是一个开源的分布式消息中间件&#xff0c;由阿里巴巴集团开发并…

祝贺《华为战略管理法:DSTE实战体系》被《中国企业家》杂志评为企业家枕边书50本之一(宏观战略类书籍)

祝贺《华为战略管理法&#xff1a;DSTE实战体系》被《中国企业家》杂志评为企业家枕边书50本之一 2024年4月23日&#xff08;周二&#xff09;下午13:00&#xff0c;《中国企业家》杂志如期举办“每天都是读书日”线下活动。 《中国企业家》杂志携手商界大咖共同推选50本枕边书…

Vue.js中的计算属性

Vue.js中的计算属性&#xff08;computed properties&#xff09;是用于声明响应式依赖的属性。它们会根据它们的依赖进行缓存&#xff0c;并且只有在相关依赖发生改变时才会重新求值。这使得它们非常适合用来处理复杂逻辑和数据处理。 基本用法 在Vue实例中&#xff0c;可以…

镭速实现AD域集成助力企业文件安全传输管控

在当今这个信息量爆炸扩张的年代&#xff0c;企业数据宛如一座蕴藏无限价值的宝库&#xff0c;它不仅是企业核心竞争力的载体&#xff0c;也成为了各种潜在风险的聚焦点。随着数字化转型步伐的加快&#xff0c;安全文件传输的管理控制显得尤为重要&#xff0c;它构成了保护企业…

各类排序方法 归并排序 扩展练习 逆序对数量

七月挑战一个月重刷完Y总算法基础题&#xff0c;并且每道题写详细题解 进度:(3/106) 归并排序的思想也是分而治之 归并优点&#xff1a;速度稳定,排序也稳定 排序也稳定&#xff08;数组中有两个一样的值&#xff0c;排序之后他们的前后顺序不发生变化&#xff0c;我们就说…

Leetcode 2065. 最大化一张图中的路径价值(DFS / 最短路)

Leetcode 2065. 最大化一张图中的路径价值 暴力DFS 容易想到&#xff0c;从0点出发DFS&#xff0c;期间维护已经走过的距离&#xff08;时间&#xff09;和途径点的权值之和&#xff0c;若访问到0点则更新答案&#xff0c;若下一步的距离与已走过的距离和超出了maxTime&#…

oracle sql语句 排序 fjd = ‘0101‘ 排在 fjd = ‘0103‘ 的前面

要实现这个排序需求&#xff0c;你可以使用 CASE 表达式来自定义排序逻辑。假设你有一个表格名为 your_table&#xff0c;并且有一个字段 fjd 存储类似 ‘0101’, ‘0103’ 这样的值&#xff0c;你可以这样编写 SQL 查询&#xff1a; SELECT * FROM your_table ORDER BY CASE …

专题六:Spring源码之初始化容器BeanFactory

上一篇咱们通过一个例子介绍初始化容器上下文相关内容&#xff0c;并通过两个示例代码看到了Spring在设计阶段为我预留的扩展点&#xff0c;和我们应该如何利用这两个扩展点在Spring初始化容器上下文阶段为我们提供服务。这一篇咱们接着往下看。 老这样子下回到refresh方法上来…

第55期:MySQL 频繁 Crash 怎么办?

社区王牌专栏《一问一实验&#xff1a;AI 版》全新改版归来&#xff0c;得到了新老读者们的关注。其中不乏对 ChatDBA 感兴趣的读者前来咨询&#xff0c;表达了想试用体验 ChatDBA 的意愿&#xff0c;对此我们表示感谢 &#x1f91f;。 目前&#xff0c;ChatDBA 还在最后的准备…

MSVCR120.DLL丢失的多种修复方法,助你快速解决dll问题

在日常生活和工作中&#xff0c;电脑已经成为我们不可或缺的工具。然而&#xff0c;在使用电脑的过程中&#xff0c;我们常常会遇到一些问题&#xff0c;其中之一就是电脑运行软件时提示找不到msvcr120.dll。如果该文件缺失或损坏&#xff0c;可能会导致依赖它的应用程序无法启…

高优先线程

你开发的时候有么有遇到过一个问题&#xff1a;服务器的一个服务线程过几个小时断连一次&#xff0c;断连之后会马上重连这种情况。这是由于CPU负载较高,线程调度时将处理数据的线程挂起了一段时间导致的。 因此&#xff0c;我有考虑到把cpu的核心进行分散开来&#xff0c;就类…

CesiumJS【Basic】- #042 绘制纹理线(Primitive方式)

文章目录 绘制纹理线(Primitive方式)1 目标2 代码2.1 main.ts3 资源文件绘制纹理线(Primitive方式) 1 目标 使用Primitive方式绘制纹理线 2 代码 2.1 main.ts var start = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883);var

【剑指Offer系列】68-二叉树的最近公共祖先(哈希)

思路&#xff1a;使用map存储每个节点的父节点&#xff0c;则两个节点的最近公共祖先&#xff0c;即二者的最近父节点 1、中序遍历二叉树&#xff08;当前节点的下一个节点&#xff09; 2、记录每个节点的父节点 3、列出p的族谱、q的族谱 4、寻找二者最近的祖先 class Soluti…

微信小程序毕业设计-英语互助系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

PS系统教程31

调色之色阶 调色与通道最基本的关系通道是记录颜色最基本的信息有些图片可以用通道去改变颜色信息的说明这些图像是比较高级的PS是一款图像合成软件&#xff0c;在合成过程中需要处理大量素材&#xff0c;比如要用这些素材进行抠背景&#xff0c;就要用到图层蒙版以及Alpha通道…

Qt编程技巧总结篇(2)-信号-槽-多线程(一)

文章目录 Qt编程技巧总结篇&#xff08;2&#xff09;-信号-槽-多线程&#xff08;一&#xff09;信号与槽实例与应用 小结 Qt编程技巧总结篇&#xff08;2&#xff09;-信号-槽-多线程&#xff08;一&#xff09; 最近学习信号与槽以及多线程&#xff0c;非常有技术含量&#…

【详解】RV1106移植opencv-mobile库

文章目录 前言一、烧入镜像二、编译项目1.创建项目文件 三、移植四、运行文件五、总结 前言 硬件&#xff1a;瑞芯微Rv1106【Luckfox Pro\Max Pico、网线一根、USB线、串口助手、摄像头 软件&#xff1a;ubuntu 20.4 编译器&#xff1a;arm-rockchip830-linux-uclibcgnueabihf…

人工智能——常用数学基础之线代中的矩阵

1. 矩阵的本质&#xff1a; 矩阵本质上是一种数学结构&#xff0c;它由按照特定规则排列的数字组成&#xff0c;通常被表示为一个二维数组。矩阵可以用于描述一组数据&#xff0c;或者表示某种关系&#xff0c;比如线性变换。 在人工智能中&#xff0c;矩阵常被用来表示数据集…

【单片机与嵌入式】stm32串口通信入门

一、串口通信/协议 &#xff08;一&#xff09;串口通信简介 串口通信是一种通过串行传输方式在电子设备之间进行数据交换的通信方式。它通常涉及两条线&#xff08;一条用于发送数据&#xff0c;一条用于接收数据&#xff09;&#xff0c;适用于各种设备&#xff0c;从微控制…