# 光速上手 - 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总结 秒杀场景的介绍 秒杀场景是…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...