在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.mfbz.cn/a/762946.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JS(JavaScript)的BOM操作

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

C语言实现简单的minishell

探索开源项目&#xff1a;MiniShell 引言 在计算机编程的世界里&#xff0c;Shell 是一个至关重要的组成部分&#xff0c;它允许用户与操作系统交互&#xff0c;执行命令和程序。MiniShell 是一个简化版的 Shell 程序&#xff0c;通常用于教学和学习目的。在本文中&#xff0…

印尼火出圈的本土网盟okspin助力slot游戏广告代投策略

印尼火出圈的本土网盟okspin助力slot游戏广告代投策略 在当今日益全球化的数字营销环境中&#xff0c;本土网盟广告平台在推广特定地区的产品和服务方面发挥着至关重要的作用。特别是在印尼这样的多元文化市场中&#xff0c;本土网盟okspin投放印尼slots游戏广告的优势尤为显著…

汽车零部件材料耐候性测试氙光太阳辐射系统试验箱

概述 汽车零部件等领域的材料耐候性测试是一项关键的质量控制环节&#xff0c;它关乎汽车部件在各种气候条件下的性能表现和寿命。塑料件光照老化实验箱&#xff0c;即氙灯老化试验箱&#xff0c;在其中扮演着至关重要的角色。通过模拟自然环境中的光照、温度、湿度等条件&…

顺序表(C语言详细版)

1. 线性表 线性表(lina list)是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串...... 线性表在逻辑上是线性结构&#xff0c;也就是说连续的一条直线。但是在物理结构上并…

开源205W桌面充电器,140W+65W升降压PD3.1快充模块(2C+1A口),IP6557+IP6538

开源一个基于IP6557和IP6538芯片的205W升降压快充模块&#xff08;140W65W&#xff09;&#xff0c;其中一路C口支持PD3.1协议&#xff0c;最高输出28V5A&#xff0c;另一路是A口C口&#xff0c;最高输出65W&#xff08;20V3.25A&#xff09;&#xff0c;可搭配一个24V10A的开关…

Ubuntu20.04 安装 cudatookit 12.2 + cudnn 安装

最简约的部署Ubuntu20.04深度学习环境的教程 1. 安装Ubuntu20.04 系统 B站详细的安装教程 简约安装版 2. 安装Nvidia显卡驱动 我参考了各种资料&#xff0c;重装系统&#xff0c;完美解决开机显示器黑屏无法进入桌面的情况 黑屏问题主要是由linux内核更新导致&#xff0c;…

混合注意力机制 -- Convolutional Block Attention Module(CBAM)

CBAM CBAM 模块概述 通道注意力模块&#xff08;Channel Attention Mechanism&#xff09;和空间注意力模块&#xff08;Spatial Attention Mechanism&#xff09;是注意力机制的两种主要形式&#xff0c;它们分别通过对通道维度和空间维度的特征图进行加权&#xff0c;从而使…

算法金 | Transformer,一个神奇的算法模型!!

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 抱个拳&#xff0c;送个礼 在现代自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;Transformer 模型的出现带来了革命性的变…

每日一题-验证回文串

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” //验证回文串 #include<vector> class Solution { public:bool reverseString(char s) {return (s > a && s < z) ||(s > 0 && s < 9) ||(s…

Lesson 43 Hurry up!

Lesson 43 Hurry up! 词汇 of course 当然【口语】 经常出现在口语交际中&#xff1a; Of course not. 当然不。 同义词&#xff1a; Certainly 当然。 Certainly not. 当然不。 注意语气&#xff1a;略带挑衅。Sure. 当然。 Sure not. 当然不。 Not sure. 不一定。 kettle…

Pandas 学习笔记(一)

一、pandas简介 Pandas 是 Python 语言的一个扩展程序库&#xff0c;用于数据分析。 Pandas 名字衍生自术语 "panel data"&#xff08;面板数据&#xff09;和 "Python data analysis"&#xff08;Python 数据分析&#xff09;。 Pandas 是一个开放源码…

Python + OpenCV 酷游地址教学V鄋KWK3589

本篇文章汇整了一系列的Python OpenCV 教学&#xff0c;只要按照教学文的顺序阅读和实作&#xff0c;就可以轻松入门OpenCV&#xff0c;并透过OpenCV 实现许多影像相关的创意应用。 接下来我们来介绍OpenCV-- OpenCV 是一个跨平台的电脑视觉函式库( 模组) &#xff0c;可应用…

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

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

SSM泰华超市商品管理系统-计算机毕业设计源码11946

目 录 摘要 1 绪论 1.1 研究背景 1.2 研究意义 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据新增流程 3.2.2 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 系统总体设…

一键把二次元老婆拉进现实(Stable Diffusion进阶:ControlNet LineArt模型)

大家好我是极客菌&#xff01;&#xff01;&#xff01; 操作&#xff0c;就能将二次元老婆拉进现实&#xff0c;成为你的专属女友。本文将带你深入了解ControlNet LineArt模型的使用方法&#xff0c;助你轻松实现这一梦想。 ControlNet LineArt模型是Stable Diffusion的最新…

AI大模型日报#0701:Meta发布LLM Compiler、扒一扒Sora两带头人博士论文

导读&#xff1a;AI大模型日报&#xff0c;爬虫LLM自动生成&#xff0c;一文览尽每日AI大模型要点资讯&#xff01;目前采用“文心一言”&#xff08;ERNIE-4.0-8K-latest&#xff09;生成了今日要点以及每条资讯的摘要。欢迎阅读&#xff01;《AI大模型日报》今日要点&#xf…

32.哀家要长脑子了!

1.299. 猜数字游戏 - 力扣&#xff08;LeetCode&#xff09; 公牛还是挺好数的&#xff0c;奶牛。。。妈呀&#xff0c;一朝打回解放前 抓本质抓本质&#xff0c;有多少位非公牛数可以通过重新排列转换公牛数字&#xff0c;意思就是&#xff0c;当这个数不是公牛数字时&#x…

控制器方法执行流程和 @InitBinder【Spring源码学习】

控制器方法执行流程 InitBinder 加在ControllerAdvice中 首先说明ControllerAdvice和aop没有任何关系&#xff01; 加在ControllerAdvice中只对所有控制器都生效 全局的在开始时就会保存到handlerMappingAdapter中的cache中&#xff1b; 加在Controller中 加在controller中只对…

TS---typescript的安装和tsc命令使用

什么是TS---typescript&#xff1f; &#xff08;TypeScript是Microsoft公司注册商标&#xff09; TypeScript具有类型系统&#xff0c;且是JavaScript的超集&#xff0c; 它可以编译成普通的JavaScript代码。TypeScript支持任意浏览器&#xff0c;任意环境&#xff0c;任意系…