# 光速上手 - 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 , 例如:

      1. 数据查询逻辑复杂,无法用 JPQL 表达
      2. 涉及数据库特定的功能(如窗口函数、分区排序等)
      3. 查询结果无法直接映射到实体类(如 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;
}

总结

       上述步骤以经过实际开发测试,保证有效!

  1. 高效性:直接通过原生 SQL 查询所需数据,减少不必要字段的查询和映射。
  2. 灵活性:可以自由定义 DTO 结构,满足复杂查询的需求。
  3. 可维护性:使用 @SqlResultSetMapping 将 SQL 与 Java 类关联,便于后续维护。

       该文章适用于需要自定义复杂查询且无需将查询结果绑定到实体类的场景。如果你也有类似需求,不妨以文章为参照上手试试!

end…

如果这篇文章帮到你, 帮忙点个关注呗, 不想那那那点赞或收藏也行鸭 (。•̀ᴗ-)✧ ~
在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述
                                                                                                                                   '(இ﹏இ`。)

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

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

相关文章

ThinkPHP 8开发环境安装

【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《ThinkPHP 8高效构建Web应用 夏磊 编程与应用开发丛书 清华大学出版社》【摘要 书评 试读】- 京东图书 1. 安装PHP8 Windows系统用户可以前往https://windows.php.net/downloads/releases/archives/下载PHP 8.0版本&am…

pikachu靶场搭建详细步骤

一、靶场下载 点我去下载 二、靶场安装 需要的环境&#xff1a; mysqlApaches&#xff08;直接使用小皮面板Phpstudy&#xff1a;https://www.xp.cn/&#xff09;&#xff0c;启动他们 设置网站&#xff0c;把靶场的路径对应过来 对应数据库的信息 由于没有核对数据库的信…

每天40分玩转Django:Django表单集

Django表单集 一、知识要点概览表 类别知识点掌握程度要求基础概念FormSet、ModelFormSet深入理解内联表单集InlineFormSet、BaseInlineFormSet熟练应用表单集验证clean方法、验证规则熟练应用自定义配置extra、max_num、can_delete理解应用动态管理JavaScript动态添加/删除表…

LabVIEW中实现多个Subpanel独立调用同一个VI

在LabVIEW中&#xff0c;如果需要通过多个Subpanel同时调用同一个VI并让这些VI实例独立运行&#xff0c;可以通过以下方法实现&#xff1a; 1. 问题背景 LabVIEW默认的VI是以单实例方式运行的。当将同一个VI加载到多个Subpanel时&#xff0c;会因为共享同一内存空间而导致冲突…

语聊系统:JAVA语聊大厅语音聊天APP系统源码

JAVA语聊大厅语音聊天APP系统源码详解 在当今数字化时代&#xff0c;语音社交已成为连接人与人之间情感的重要桥梁。JAVA语聊大厅语音聊天APP系统源码&#xff0c;作为一款集成了多种先进技术与功能的社交软件&#xff0c;正以其独特魅力引领着语音社交的新潮流。该系统不仅提…

【学生管理系统】权限管理之用户管理

目录 6. 权限管理 6.1 环境搭建 6.1.1 数据库 6.1.2 后端环境 6.2 用户管理 6.2.1 查询所有用户&#xff08;关联角色&#xff09; 6.2.2 核心1&#xff1a;给用户授予角色 6. 权限管理 6.1 环境搭建 6.1.1 数据库 权限管理的5张表的关系 添加4张表 # 权限表&…

Unity 和 OpenCV:结合计算机视觉和游戏开发

文章目录 前言一、Unity 中集成 OpenCV1. 安装OpenCV plus Unity 插件2. 导入 OpenCV 包 二、图像处理应用程序的创建1. 实时轮廓检测2. 粒子发射器3. 碰撞区域 三、效果四、总结 前言 Unity 和 OpenCV 是两个强大的开发工具&#xff0c;分别用于游戏开发和计算机视觉。结合它…

记一次内存泄漏分析(待写稿)

背景 线上Flink频繁重启&#xff0c;先后排查了很多情况&#xff0c;目前在内存阶段排查&#xff0c;首先说说学到的知识 内存泄漏分析 JVM常用命令 JConsole JVisualvm 快照的这里是最有用的&#xff0c;它和jmap不同&#xff0c;jmap查找的是占用字节最多的类&#xff…

剑指Offer|LCR 014. 字符串的排列

LCR 014. 字符串的排列 给定两个字符串 s1 和 s2&#xff0c;写一个函数来判断 s2 是否包含 s1 的某个变位词。 换句话说&#xff0c;第一个字符串的排列之一是第二个字符串的 子串 。 示例 1&#xff1a; 输入: s1 "ab" s2 "eidbaooo" 输出: True 解…

Java编程题_面向对象和常用API01_B级

Java编程题_面向对象和常用API01_B级 第1题 面向对象、异常、集合、IO 题干: 请编写程序&#xff0c;完成键盘录入学生信息&#xff0c;并计算总分将学生信息与总分一同写入文本文件 需求&#xff1a;键盘录入3个学生信息(姓名,语文成绩,数学成绩) 求出每个学生的总分 ,并…

用户界面的UML建模05

4 抽象表示层建模&#xff08;Abstract Presentation Modeling&#xff09; 在进行应用程序建模时&#xff0c;很自然地会需要对UI 表示层进行建模。甚至是对于非常简单的场景&#xff08;scenario&#xff09;而言&#xff0c;UI 表示层部分的建模都是必不可少的。在这个阶段…

2024.12.25在腾讯云服务器上使用docker部署flask

2024.12.25在腾讯云服务器上使用docker部署flask 操作系统&#xff1a;Ubuntu 根据腾讯云的说明文档安装 Docker 并配置镜像加速源&#xff0c;注意需要安装腾讯云的加速源&#xff0c;使用官网的加速源连接极其不稳定&#xff0c;容易导致运行失败。使用哪个公司的云服务器就…

存储块的删除与状态查询

目录 存储块的删除 设计实现 存储块的删除 仅需删除任务列表的所有任务&#xff0c;无需在意空闲存储块。 设计实现 存储块的删除&#xff08;清空过程中可能有任务就绪&#xff0c;需执行一次调度&#xff09;存储块的状态查询&#xff08;当前存储块的计数、允许的最大计数、…

如何在IDEA一个窗口中导入多个项目

一般在IDEA窗口中想导入一个新项目&#xff0c;会提示我们在当前窗口还是新窗口。如果选新窗口&#xff0c;就会新打开一个窗口&#xff0c;此时新窗口里面只有新导入的项目。 而为了浏览起来更方便&#xff0c;需要实现在IDEA一个窗口中导入多个项目。具体步骤如下&#xff1…

SQL 实战:基于经纬度的距离计算与位置查询

在位置服务&#xff08;LBS&#xff09;系统中&#xff0c;基于地理位置查询和距离计算是核心功能之一。例如&#xff1a; 查找附近的商铺、加油站或医院。计算两点之间的实际直线距离。筛选出指定范围内的用户或设备位置。 MySQL 提供了多种方式实现地理位置查询&#xff0c…

SpringCloudAlibaba技术栈-Higress

1、什么是Higress? 云原生网关&#xff0c;干啥的&#xff1f;用通俗易懂的话来说&#xff0c;微服务架构下Higress 就像是一个智能的“交通警察”&#xff0c;它站在你的网络世界里&#xff0c;负责指挥和调度所有进出的“车辆”&#xff08;也就是数据流量&#xff09;。它的…

现代图形API综合比较:Vulkan DirectX Metal WebGPU

Vulkan、DirectX、Metal 和 WebGPU 等低级图形 API 正在融合为类似于当前 GPU 构建方式的模型。 图形处理单元 (GPU) 是异步计算单元&#xff0c;可以处理大量数据&#xff0c;例如复杂的网格几何形状、图像纹理、输出帧缓冲区、变换矩阵或你想要计算的任何数据。 NSDT工具推荐…

工业大数据分析算法实战-day19

文章目录 day19讨论范畴介绍知识工程 知识沉淀方法的维度模型模型要素维度传感器异常报警异常预警的范式&#xff1a;特征量-征兆量-研判规则健康评估的范式&#xff1a;劣化度-健康度-综合评价故障类型研判范式&#xff1a;特征量-证据/现象-推理逻辑故障处理效果监控范式&…

轮胎识别数据集,可对生产流水线里的轮胎图片标注,支持yolo,coco json,voc xml格式的标注,一共785张采集图片

轮胎识别数据集&#xff0c;可对生产流水线里的轮胎图片标注&#xff0c;支持yolo&#xff0c;coco json&#xff0c;voc xml格式的标注&#xff0c;一共785张采集图片 数据集分割 训练组90&#xff05; 706图片 有效集6% 46图片 测试集4% 33图片 预处理…

【RabbitMQ的死信队列】

死信队列 什么是死信队列死信队列的配置方式死信消息结构 什么是死信队列 消息被消费者确认拒绝。消费者把requeue参数设置为true(false)&#xff0c;并且在消费后&#xff0c;向RabbitMQ返回拒绝。channel.basicReject或者channel.basicNack。消息达到预设的TTL时限还一直没有…