当前位置: 首页 > news >正文

在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();

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

相关文章:

在Spring Data JPA中使用@Query注解

目录 前言示例简单示例只查询部分字段&#xff0c;映射到一个实体类中只查询部分字段时&#xff0c;也可以使用List<Object[]>接收返回值再复杂一些 前言 在以往写过几篇spring data jpa相关的文章&#xff0c;分别是 Spring Data JPA 使用JpaSpecificationExecutor实现…...

【UE5.1】Chaos物理系统基础——01 创建可被破坏的物体

目录 步骤 一、通过笔刷创建静态网格体 二、破裂静态网格体 三、“统一” 多层级破裂 四、“簇” 群集化的破裂 五、几何体集的材质 六、防止几何体集自动破碎 步骤 一、通过笔刷创建静态网格体 1. 可以在Quixel Bridge中下载两个纹理&#xff0c;用于表示石块的内外纹…...

Linux下SUID提权学习 - 从原理到使用

目录 1. 文件权限介绍1.1 suid权限1.2 sgid权限1.3 sticky权限 2. SUID权限3. 设置SUID权限4. SUID提权原理5. SUID提权步骤6. 常用指令的提权方法6.1 nmap6.2 find6.3 vim6.4 bash6.5 less6.6 more6.7 其他命令的提权方法 1. 文件权限介绍 linux的文件有普通权限和特殊权限&a…...

Redis主从复制搭建一主多从

1、创建/myredis文件夹 2、复制redis.conf配置文件到新建的文件夹中 3、配置一主两从&#xff0c;创建三个配置文件 ----redis6379.conf ----redis6380.conf ----redis6381.conf 4、在三个配置文件写入内容 redis6379.conf里面的内容 include /myredis/redis.conf pidfile /va…...

GPT-4o文科成绩超一本线,理科为何表现不佳?

目录 01 评测榜单 02 实际效果 什么&#xff1f;许多大模型的文科成绩竟然超过了一本线&#xff0c;还是在竞争激烈的河南省&#xff1f; 没错&#xff0c;最近有一项大模型“高考大摸底”评测引起了广泛关注。 河南高考文科今年的一本线是521分&#xff0c;根据这项评测&…...

Lombok的hashCode方法

Lombok对于重写hashCode的算法真的是很经典&#xff0c;但是目前而言有一个令人难以注意到的细节。在继承关系中&#xff0c;父类的hashCode针对父类的所有属性进行运算&#xff0c;而子类的hashCode却只是针对子类才有的属性进行运算&#xff0c;立此贴提醒自己。 目前重写ha…...

关于springboot创建kafkaTopic

工具类提供&#xff0c;方法名见名知意。使用kafka admin import org.apache.kafka.clients.admin.*; import org.apache.kafka.common.KafkaFuture;import java.util.*; import java.util.concurrent.ExecutionException;import org.apache.kafka.clients.admin.AdminClient; …...

OOAD的概念

面向对象分析与设计&#xff08;OOAD, Object-Oriented Analysis and Design&#xff09;是一种软件开发方法&#xff0c;它利用面向对象的概念和技术来分析和设计软件系统。OOAD 主要关注对象、类以及它们之间的关系&#xff0c;通过抽象、封装、继承和多态等面向对象的基本原…...

Day47

Day47 手写Spring-MVC之DispatcherServlet DispatcherServlet的思路&#xff1a; 前端传来URI&#xff0c;在TypeContainer容器类中通过uri得到对应的类描述类对象&#xff08;注意&#xff1a;在监听器封装类描述类对象的时候&#xff0c;是针对于每一个URI进行封装的&#x…...

【面试系列】后端开发工程师 高频面试题及详细解答

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来&#xff1a;详细讲解AIGC的概念、核心技术、…...

mac|浏览器链接不上服务器但可以登微信

千万千万千万不要没有关梯子直接关机&#xff0c;不然就会这样子呜呜呜 设置-网络&#xff0c;点击三个点--选择--位置--编辑位置&#xff08;默认是自动&#xff09; 新增一个&#xff0c;然后选中点击完成 这样就可以正常上网了...

Spring Cloud Alibaba之负载均衡组件Ribbon

一、什么是负载均衡&#xff1f; &#xff08;1&#xff09;概念&#xff1a; 在基于微服务架构开发的系统里&#xff0c;为了能够提升系统应对高并发的能力&#xff0c;开发人员通常会把具有相同业务功能的模块同时部署到多台的服务器中&#xff0c;并把访问业务功能的请求均…...

tkinter显示图片

tkinter显示图片 效果代码解析打开和显示图像 代码 效果 代码解析 打开和显示图像 def open_image():file_path filedialog.askopenfilename(title"选择图片", filetypes(("PNG文件", "*.png"), ("JPEG文件", "*.jpg;*.jpeg&q…...

000.二分查找算法题解目录

000.二分查找算法题解目录 69. x 的平方根&#xff08;简单&#xff09;34. 在排序数组中查找元素的第一个和最后一个位置&#xff08;中等&#xff09;...

数据资产赋能企业决策:通过精准的数据分析和洞察,构建高效的数据资产解决方案,为企业提供决策支持,助力企业实现精准营销、风险管理、产品创新等目标,提升企业竞争力

一、引言 在信息化和数字化飞速发展的今天&#xff0c;数据已成为企业最宝贵的资产之一。数据资产不仅包含了企业的基本信息&#xff0c;还蕴含了丰富的市场趋势、消费者行为和潜在商机。如何通过精准的数据分析和洞察&#xff0c;构建高效的数据资产解决方案&#xff0c;为企…...

【java开发环境】多版本jdk 自由切换window和linux

win10 一、准备 各种版本的jdk&#xff0c;按自己的需要下载。 我这里是需要jdk17和jdk8。 1、jdk17 下载&#xff1a;Java Downloads | Oracle&#xff0c;选择exe后缀文件 2、jdk8下 载&#xff1a;Java Downloads | Oracle&#xff0c;选择exe后缀文件 二、详细步骤 1、…...

MySQL实训项目——餐饮点餐系统

项目简介&#xff1a;餐饮点餐系统是一款为餐厅和顾客提供便捷点餐服务的在线平台。通过该系统&#xff0c;餐厅能够展示其菜单&#xff0c;顾客可以浏览菜品&#xff0c;并将其加入购物车或直接下单。系统还提供了订单管理功能&#xff0c;方便餐厅跟踪和处理顾客的订单。 1. …...

昇思MindSpore学习总结七——模型训练

1、模型训练 模型训练一般分为四个步骤&#xff1a; 构建数据集。定义神经网络模型。定义超参、损失函数及优化器。输入数据集进行训练与评估。 现在我们有了数据集和模型后&#xff0c;可以进行模型的训练与评估。 2、构建数据集 首先从数据集 Dataset加载代码&#xff0…...

AI时代创新潮涌,从探路到引路,萤石云引领千行百业创新

步入AI新时代&#xff0c;AI、云计算、大数据等技术迅速迭代&#xff0c;并日益融入经济社会发展各领域全过程&#xff0c;数字经济成为推动千行百业转型升级的重要驱动力量。 今年的政府工作报告提出&#xff0c;深入推进数字经济创新发展。积极推进数字产业化、产业数字化&a…...

计算机毕业设计Python深度学习美食推荐系统 美食可视化 美食数据分析大屏 美食爬虫 美团爬虫 机器学习 大数据毕业设计 Django Vue.js

Python美食推荐系统开题报告 一、项目背景与意义 随着互联网和移动技术的飞速发展&#xff0c;人们的生活方式发生了巨大变化&#xff0c;尤其是餐饮行业。在线美食平台如雨后春笋般涌现&#xff0c;为用户提供了丰富的美食选择。然而&#xff0c;如何在海量的餐饮信息中快速…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...

华为OD机试-最短木板长度-二分法(A卷,100分)

此题是一个最大化最小值的典型例题&#xff0c; 因为搜索范围是有界的&#xff0c;上界最大木板长度补充的全部木料长度&#xff0c;下界最小木板长度&#xff1b; 即left0,right10^6; 我们可以设置一个候选值x(mid)&#xff0c;将木板的长度全部都补充到x&#xff0c;如果成功…...

6.9本日总结

一、英语 复习默写list11list18&#xff0c;订正07年第3篇阅读 二、数学 学习线代第一讲&#xff0c;写15讲课后题 三、408 学习计组第二章&#xff0c;写计组习题 四、总结 明天结束线代第一章和计组第二章 五、明日计划 英语&#xff1a;复习l默写sit12list17&#…...