# 光速上手 - JPA 原生 sql DTO 投影
前言
使用 JPA 时,我们一般通过 @Entity 进行实体类映射,从数据库中查询出对象。然而,在实际开发中,有时需要自定义查询结果并将其直接映射到 DTO,而不是实体类。这种需求可以通过 JPA 原生 SQL 查询和 DTO 投影 来实现。博主将以实际开发场景 为例,快速摘要如何在 JPA 中实现基于原生 SQL 的 DTO 投影
开始 - 实现步骤
以下是实现 DTO 投影的完整步骤,包括实体类、SQL 映射配置、接口调用和 DTO 设计。
一、配置实体类及映射
首先在实体类中定义 @SqlResultSetMapping,用于将原生 SQL 查询结果映射到 DTO 类。在这个例子中,我们定义了 IssueVideo 实体,并通过 @SqlResultSetMapping 和 @NamedNativeQuery 配置了一个纯sql查询
两个注解 详解 (已理解可以跳过)
-
1. @NamedNativeQuery 核心作用
-
@NamedNativeQuery是用来定义 原生 SQL 查询 的。 -
尽管JPA 中已经为sql 提供了许多方便的解决方式,但是某些场景下,我们还是需要直接使用原生 SQL , 例如:
- 数据查询逻辑复杂,无法用 JPQL 表达
- 涉及数据库特定的功能(如窗口函数、分区排序等)
- 查询结果无法直接映射到实体类(如 DTO、聚合结果)
-
通过 @NamedNativeQuery,我们可以直接在实体类中绑定一个原生 SQL 查询,并为这个查询命名。在调用时,可以通过指定这个命名的查询名称直接执行该 SQL
-
2. @SqlResultSetMapping
核心作用-
@SqlResultSetMapping是用来定义 查询结果的映射 规则的。 -
当我们使用原生 SQL 查询时,返回的结果是数据库的行列数据,与实体类的属性或 DTO 的结构未必完全匹配。
@SqlResultSetMapping(name = "", ...) -> 它长这样它用来告诉 JPA:
- 查询返回的列和 DTO 的字段如何一一对应
- 如何将原生 SQL 查询的结果映射为自定义的类(DTO)
没有
@SqlResultSetMapping时,JPA 会尝试将查询结果映射到实体类,但如果结果不是直接对应实体类那么映射就会失败。这时,我们就需要@SqlResultSetMapping来自定义映射规则。
-
-
3. 为什么需要将它们写在实体类上?-
实体类是 SQL 映射的入口
在 JPA 中,实体类是我们与数据库表交互的核心对象。因此:-
将
@NamedNativeQuery和@SqlResultSetMapping写在实体类上,可以明确这段查询与该实体相关,方便维护和查阅。 -
JPA 的原生查询和结果映射机制依赖于实体类的原数据,通过注解绑定的方式,可以让这些查询和映射规则作为实体类的一部分,便于复用。
-
-
关联性强
-
@NamedNativeQuery
定义了原生 SQL 查询,而@SqlResultSetMapping定义了如何映射这个查询的结果,它们是 成对使用的。二者写在同一个实体类上,能清晰地表达“该查询和该实体类相关”的逻辑。 -
如果你把它们分散到其他地方,可能会增加代码复杂性和维护成本。
-
-
4. 总结
简单来说,@NamedNativeQuery 和 @SqlResultSetMapping 分别解决了两个不同的问题:
@NamedNativeQuery负责“如何查询”
它定义了 SQL 的逻辑以及参数。@SqlResultSetMapping负责“如何处理查询结果”
它定义了如何将 SQL 返回的数据映射到 DTO中
二者通过查询名称(name 属性)联系在一起。例如:
@NamedNativeQuery(name = "IssueRecommendRespDTOQuery", // 查询名称resultSetMapping = "IssueRecommendRespDTOResult", // 映射↓query = """SELECT ... -- 原生 SQL 查询"""
)
@SqlResultSetMapping(name = "IssueRecommendRespDTOResult", // 映射名称 对应↑classes = @ConstructorResult(targetClass = IssueRecommendRespDTO.class, // 映射到的 DTOcolumns = {@ColumnResult(name = "isdId", type = Integer.class),...})
)
两个注解解释 - end
实体类代码示例
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@Table(name = "issue_video")
// region jpa 投影主页查询目标dto (可以通过 "region <> endregion" 将其折叠起来)
@SqlResultSetMapping(name = "IssueRecommendRespDTOResult",classes = @ConstructorResult(targetClass = IssueRecommendRespDTO.class,columns = {@ColumnResult(name = "isdId", type = Integer.class),@ColumnResult(name = "videoUrl", type = String.class),@ColumnResult(name = "duration", type = Integer.class),@ColumnResult(name = "issId", type = Integer.class),@ColumnResult(name = "title", type = String.class),@ColumnResult(name = "cover", type = String.class),@ColumnResult(name = "watchNum", type = BigInteger.class),@ColumnResult(name = "commentNum", type = Integer.class),@ColumnResult(name = "creTime", type = LocalDateTime.class),@ColumnResult(name = "authorId", type = Integer.class),@ColumnResult(name = "authorName", type = String.class)})
)
// endregion// region jpa 投影主页推荐查询sql
@NamedNativeQuery(name = "IssueRecommendRespDTOQuery",resultSetMapping = "IssueRecommendRespDTOResult",query = """
SELECTsub.isd_id AS isdId,sub.video_url AS videoUrl,sub.duration AS duration,sub.iss_id AS issId,sub.title AS title,sub.cover AS cover,sub.watch_num AS watchNum,sub.comment_num AS commentNum,sub.cre_time AS creTime,sub.u_id AS authorId,sub.name AS authorNameFROM (SELECTi.score,v.isd_id,v.video_url,v.duration,i.iss_id,i.title,i.cover,i.watch_num,i.comment_num,i.cre_time,author.u_id,author.name,casewhen ROW_NUMBER() OVER (PARTITION BY i.su_idORDER BYCASEWHEN sp.su_id IS NOT NULL THEN (i.score + sp.score)ELSE i.scoreEND DESC) <= 3 then 1else 2end AS rank_within_partition,RANK() OVER (PARTITION BY i.su_idORDER BYCASEWHEN sp.su_id IS NOT NULL THEN (i.score + sp.score)ELSE i.scoreEND DESC) as global_rankFROM issue_video vJOIN issue i ON i.iss_id = v.iss_idJOIN user author ON author.u_id = i.u_idLEFT JOIN subarea_preference spON sp.su_id = i.su_idand sp.u_id = :uIdorder by (i.score + sp.score) desc) suborder by rank_within_partition, global_rank
"""
)
// endregion
public class IssueVideo {@GeneratedValue(strategy = GenerationType.IDENTITY)@Idprivate Integer isdId;private String videoUrl;private int duration;private int size;private LocalDateTime updTime;private String remark;private LocalDateTime issueTime;private String permission;private Boolean isDeclare;private Boolean offDanmu;private Boolean offComm;private Boolean onGretestComm;private Boolean isDel;@ManyToOne@JoinColumn(name = "vd_id")private VideoDeclare videoDeclare;private BigInteger danmuNum;@OneToOne@JoinColumn(name = "iss_id")private Issue issue;
}
二、配置对应 JPA 接口
在 JPA 接口中,直接通过 @Query 注解调用刚刚定义的原生 SQL 查询,并将结果映射成期望的 DTO 返回类型
Repository 代码示例
public interface IssueVideoRepo extends JpaRepository<IssueVideo, Integer> {/*** 获取主页推荐视频* @param uId 用户 ID* @return DTO 列表*/@Query(name = "IssueRecommendRespDTOQuery", nativeQuery = true)List<IssueRecommendRespDTO> getRecommendVideos(@Param("uId") Integer uId);
}
三、事务层调用接口
事务层负责调用 Repository 接口,并将返回的结果处理为最终服务层需要的数据。以下为服务实现代码:
Service 代码示例
@Service
public class IssueVideoServiceImpl implements IssueVideoService {@Autowiredprivate IssueVideoRepo issueVideoRepo;@Overridepublic List<IssueRecommendRespDTO> getRecommendVideos(Integer uId) {List<IssueRecommendRespDTO> recommendVideos = null;try {recommendVideos = issueVideoRepo.getRecommendVideos(uId);} catch (Exception exception) {exception.printStackTrace();}return recommendVideos;}
}
四、定义 DTO 类
最后,定义与查询结果对应的 DTO 类。DTO 结构需要与 @SqlResultSetMapping 中的字段一一对应。
DTO 代码示例
@Data // 主dto 不需要lombok 全参数注解 @AllArgsConstructor, 因为要额外配置
public class IssueRecommendRespDTO {private Integer isdId;private String videoUrl;private Integer duration;private IssueRecommendInDTO issue;public IssueRecommendRespDTO() {}public IssueRecommendRespDTO(Integer isdId, String videoUrl, Integer duration,Integer issId, String title, String cover,BigInteger watchNum, Integer commentNum, LocalDateTime creTime,Integer authorId, String authorName) {this.isdId = isdId;this.videoUrl = videoUrl;this.duration = duration;this.issue = new IssueRecommendInDTO(issId, title, cover,watchNum, commentNum, creTime,new AuthorInDTO(authorId, authorName));}
}@AllArgsConstructor
@NoArgsConstructor
@Data
public class IssueRecommendInDTO { // In命名表示 该dto 很可能处于内部使用 > internal : 内部的private Integer issId;private String title;private String cover;private BigInteger watchNum;private Integer commentNum;private LocalDateTime creTime;private AuthorInDTO author;
}@AllArgsConstructor
@NoArgsConstructor
@Data
public class AuthorInDTO {private Integer authorId;private String authorName;
}
总结
上述步骤以经过实际开发测试,保证有效!
- 高效性:直接通过原生 SQL 查询所需数据,减少不必要字段的查询和映射。
- 灵活性:可以自由定义 DTO 结构,满足复杂查询的需求。
- 可维护性:使用
@SqlResultSetMapping将 SQL 与 Java 类关联,便于后续维护。
该文章适用于需要自定义复杂查询且无需将查询结果绑定到实体类的场景。如果你也有类似需求,不妨以文章为参照上手试试!
end…
如果这篇文章帮到你, 帮忙点个关注呗, 不想那那那点赞或收藏也行鸭 (。•̀ᴗ-)✧ ~
'(இ﹏இ`。)
相关文章:
# 光速上手 - JPA 原生 sql DTO 投影
前言 使用 JPA 时,我们一般通过 Entity 进行实体类映射,从数据库中查询出对象。然而,在实际开发中,有时需要自定义查询结果并将其直接映射到 DTO,而不是实体类。这种需求可以通过 JPA 原生 SQL 查询和 DTO 投影 来实现…...
ASP.NET Web应用程序出现Maximum request length exceeded报错
一、问题描述 在ASP.NET的web应用中,导出数据时出现500 - Internal server error.Maximum request length exceeded。 二、原因分析 这个错误通常出现在Web应用程序中,表示客户端发送的HTTP请求的长度超过了服务器配置的最大请求长度限制。这可能是因为…...
HTML——16.相对路径
<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><body><a href"../../fj1/fj2/c.html" target"_blank">链接到c</a><!--相对路径:-->…...
windows 默认的消息ID有那些---我与大模型对话
前言: 与大模型交流,提问要尽量简短,突出关键词。否则它的回答就可能事是而非。用它总结和查资料还行,用它解决问题路还很远。它非常注重标准格式并机械的执行标准格式,并且事无巨细,不能灵活简要的回答问…...
CSV vs 数据库:爬虫数据存储的最佳选择是什么
介绍 在爬虫技术中,数据存储是一个不可缺少的环节。然而,选择合适的存储方式对数据分析和结果应用都致关重要。CSV和数据库是常用的两种存储方式,但它们各有优缺。这篇文章将分析两者在爬虫数据存储方面的选择值。 微博热搜是当前网络热点话…...
编译原理学习笔记——CH7-Runtime Environments运行时环境
本章重点: 为什么函数调用可以采用栈式存储? 函数调用和返回过程中需要记录哪些信息?如何记录? 主要知识点: 环境、状态、activation (激活) of procedures 、elaboration (确立…...
机器学习DAY7: 特征工程和特征选择(数据预处理)(完)
本文通过特征提取、特征转换、特征选择三个过程介绍数据预处理方法,特征提取将原始数据转换为适合建模的特征,特征转换将数据进行变换以提高算法的准确性,特征选择用来删除无用的特征。 知识点 特征提取特征转换特征选择 本次实验的一些示…...
vue3动态加载组件
如何在Vue3中动态加载组件 需求根据下拉框的值,加载不同的组件 新建文件aaa.vue,bbb.vue <template><div class"container">我是bbbb组件</div> </template><script lang"ts" setup name"taskPus…...
12.29 redis缓存一致性
更新操作 如果先更新数据库再更新缓存 先更新缓存再更新数据库 更新缓存为1 更新缓存尾2 更新数据库为2 更新数据库为1 那么最后缓存为2 数据库为1 数据不一致 先更新数据库,再更新缓存 数据库为1 数据库为2 缓存为2 缓存为1 还是不一致 于是这种情况我们改为将缓…...
SqlSugar配置连接达梦数据库集群
安装达梦数据库时,会自动在当前操作系统中创建dm_svc.conf文件,可以在其中配置集群信息,不同操作系统下的文件位置如下图所示: dm_svc.conf文件内的数据分为全局配置区域、服务配置区域,以参考文献1中的示例说明&…...
评分模型在路网通勤习惯分析中的应用——提出问题(1)
1、问题的由来、目标和意义 最近一段时间和公司其它业务部门讨论时,发现一个有趣的交通路网问题,车辆从S点行驶到V点共用时40分钟,这段时间内路网中的卡口摄像头识别到了车辆通过的信息。如下图所示: 设计师需要通过这些有限的路…...
使用 OpenCV 绘制线条和矩形
OpenCV 是一个功能强大的计算机视觉库,它不仅提供了丰富的图像处理功能,还支持图像的绘制。绘制简单的几何图形(如线条和矩形)是 OpenCV 中常见的操作。在本篇文章中,我们将介绍如何使用 OpenCV 在图像上绘制线条和矩形…...
npm 切换镜像源
设置镜像源 npm config set registry https://mirrors.huaweicloud.com/repository/npm/ npm 官方原始镜像网址是:https://registry.npmjs.org/ 淘宝 NPM 镜像:https://registry.npm.taobao.org 阿里云 NPM 镜像:https://npm.aliyun.com 腾…...
CSS(四)display和float
display display 属性用于控制元素的显示类型,用的 display 值包括: block:块级元素 使元素成为块级元素,占据一整行,前后有换行宽度默认为父容器的 100%,可以设置宽高,支持 margin、padding、…...
MMaudio AI:如何通过 AI 实现精准的视频到音频合成
1. 引言:视频音效制作的新纪元 无论是短视频创作者还是电影后期制作团队,音效始终是提升作品质量的关键。然而,手动调整音效不仅耗时,还容易出错。试想,如果一项 AI 技术能够根据视频内容自动生成与画面完美同步的音效…...
SQL进阶技巧:如何分析双重职务问题?
目录 0 背景描述 1 数据准备 2 问题分析 方法2:利用substr函数,充分利用数据特点【优秀解法】 3 小结...
OpenCV相机标定与3D重建(37)计算两幅图像之间单应性矩阵(Homography Matrix)的函数findHomography()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 找到两个平面之间的透视变换。 cv::findHomography 是 OpenCV 库中用于计算两幅图像之间单应性矩阵(Homography Matrix)的…...
Nacos配置管理+共享配置、配置热更新
1. 什么是配置管理? Nacos 配置管理是一个集中管理配置的工具。 它把微服务的配置集中存放,方便管理。可以动态更新配置,配置变了,微服务能马上知道并更新,不用重启。还能进行版本控制,记录配置的历史版本方便回滚。…...
asp.net core系统记录当前在线人数
实时记录当前在线人数,登录后保持120秒在线状态,在线状态保存在缓存中,采用滑动过期,在120秒内请求了系统,自动续活120秒;超过时间则移除用户在线状态; 需要在登录过滤器标记用户在线状态需要排…...
秒杀场景的设计思考
秒杀场景的设计思考 在学习Redis的之后,一个绕不开的话题就是秒杀系统的设计。本文将从下面👇🏻几个方面展开一下个人简单的理解: 秒杀场景的介绍设计的核心思路怎么限流、削峰、异步planB总结 秒杀场景的介绍 秒杀场景是…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
