# 光速上手 - JPA 原生 sql DTO 投影

news2025/1/2 16:44:33

                                                                     前言

        使用 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, // 映射到的 DTO
        columns = {
            @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 = """
SELECT
        sub.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 authorName
      FROM (
               SELECT
                   i.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,
                   case
                       when ROW_NUMBER() OVER (
                           PARTITION BY i.su_id
                           ORDER BY
                               CASE
                                   WHEN sp.su_id IS NOT NULL THEN (i.score + sp.score)
                                   ELSE i.score
                                   END DESC
                           ) <= 3 then 1
                      else 2
                      end AS rank_within_partition,
                   RANK() OVER (
                       PARTITION BY i.su_id
                       ORDER BY
                           CASE
                               WHEN sp.su_id IS NOT NULL THEN (i.score + sp.score)
                               ELSE i.score
                               END DESC
                       ) as global_rank
               FROM issue_video v
                        JOIN issue i ON i.iss_id = v.iss_id
                        JOIN user author ON author.u_id = i.u_id
                        LEFT JOIN subarea_preference sp
                            ON sp.su_id = i.su_id
                            and sp.u_id = :uId
              order by (i.score + sp.score) desc
           ) sub
      order by rank_within_partition, global_rank
"""
)
// endregion
public class IssueVideo {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    private 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 {
    @Autowired
    private IssueVideoRepo issueVideoRepo;

    @Override
    public 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.coloradmin.cn/o/2268434.html

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

相关文章

智能故障诊断和寿命预测期刊推荐

往期精彩内容&#xff1a; Python-凯斯西储大学&#xff08;CWRU&#xff09;轴承数据解读与分类处理 基于FFT CNN - BiGRU-Attention 时域、频域特征注意力融合的轴承故障识别模型-CSDN博客 基于FFT CNN - Transformer 时域、频域特征融合的轴承故障识别模型-CSDN博客 P…

MTK抓log方法log机制以及如何抓取log

目录 离线log抓取&#xff1a; adb命令打开mtklog的方法 &#xff1a; 实时log抓取&#xff1a; 设置log等级和打开平台log: 实时抓取&#xff0c;两种方式&#xff1a; Kernel Log: LOG 优先级 logcat 离线log抓取&#xff1a; 1.手机先进入开发者模式 2.进入拨号页面…

人工智能与云计算的结合:如何释放数据的无限潜力?

引言&#xff1a;数据时代的契机 在当今数字化社会&#xff0c;数据已成为推动经济与技术发展的核心资源&#xff0c;被誉为“21世纪的石油”。从个人消费行为到企业运营决策&#xff0c;再到城市管理与国家治理&#xff0c;每个环节都在生成和积累海量数据。然而&#xff0c;数…

如何在 Ubuntu 22.04 上安装 Varnish HTTP 教程

简介 在本教程中&#xff0c;我们将学习如何在 Ubuntu 22.04 服务器上安装和配置 Varnish HTTP。 Varnish 是一款高性能的 HTTP 加速器&#xff0c;旨在提高内容密集型动态网站的速度。它通过将网页缓存在内存中来工作&#xff0c;从而减少 Web 服务器的负载&#xff0c;并显…

AI辅助编码提高病案首页主要诊断编码正确率数据优化方法(2025增量优化版附python源代码)

一、引言 1.1 研究背景与意义 在医疗信息化进程中,病案首页作为病历信息的核心浓缩,承载着疾病分类、医疗统计、医保结算等关键任务,其主要诊断编码的准确性至关重要。准确的编码不仅是医疗质量评估、科研数据分析的基石,更是合理分配医疗资源、保障医保精准支付的关键依…

CSES-1135 Distance Queries

题目传送门https://vjudge.net/problem/CSES-1135#authorGPT_zh 解题思路 题目让我们求树上两个点的距离。 那么就可以转化为两点到其 LCA 的距离之和。 代码 #include<bits/stdc.h> using namespace std;int n,q; vector<int> g[200001]; int dis[200001],dep…

「Mac畅玩鸿蒙与硬件49」UI互动应用篇26 - 数字填色游戏

本篇教程将带你实现一个数字填色小游戏&#xff0c;通过简单的交互逻辑&#xff0c;学习如何使用鸿蒙开发组件创建趣味性强的应用。 关键词 UI互动应用数字填色动态交互逻辑判断游戏开发 一、功能说明 数字填色小游戏包含以下功能&#xff1a; 数字选择&#xff1a;用户点击…

001__VMware软件和ubuntu系统安装(镜像)

[ 基本难度系数 ]:★☆☆☆☆ 一、Vmware软件和Ubuntu系统说明&#xff1a; a、Vmware软件的说明&#xff1a; 官网&#xff1a; 历史版本&#xff1a; 如何下载&#xff1f; b、Ubuntu系统的说明&#xff1a; 4、linux系统的其他版本&#xff1a;红旗(redhat)、dibian、cent…

【NebulaGraph】变化的多跳查询

【NebulaGraph】变化的多跳查询 1. 需求2. 解决方案2.1 确定查询结构2.2 构建查询语句 3. 追加需求&#xff1a;如果增加每一跳都要指定查询某SPACE下的Tag&#xff0c;或者不查询某个Tag怎么办 1. 需求 存在多跳请求&#xff0c;其中每一跳是从上一跳查询结果为基础的。但是 …

【Compose multiplatform教程06】用IDEA编译Compose Multiplatform常见问题

当我们从Kotlin Multiplatform Wizard | JetBrains 下载ComposeMultiplatform项目时 会遇到无法正常编译/运行的情况&#xff0c;一般网页和桌面是可以正常编译的&#xff0c; 我这里着重解决如下问题 1:Gradle版本不兼容或者Gradle连接超时 2:JDK版本不兼容 3:Gradle依赖库连…

如何利用无线路由器实现水泵房远程监测管理

水泵站广泛部署应用在工农业用水、防洪、排涝和抗旱减灾等方面&#xff0c;如果水泵站发生异常&#xff0c;往往会对生产生活造成诸多损失&#xff0c;甚至引发安全事故。因此&#xff0c;建立一套高效、可靠的泵站远程监测管理系统至关重要。 方案背景 目前&#xff0c;我国大…

vue3学习笔记(9)-pinia、storeToRefs、getters

1.新的集中式状态&#xff08;数据&#xff09;管理库&#xff0c;redux vuex pinia 搭建 2.ref拆包 如果在reactive里面定义ref&#xff0c;则打印c时&#xff0c;无需.value 他自动拆包&#xff0c;如果直接在外面定义的ref则需要.value,他没有拆包 3.pinia存储读取数据 存…

【Qt】容器控件、布局管理控件

目录 容器控件 QGroupBox QTabWidget 布局管理控件 QVBoxLayout 例子&#xff1a; QHBoxLayout 例子&#xff1a; QGridLayout 例子&#xff1a; 例子&#xff1a; QFormLayout 例子&#xff1a; QSpacerItem 例子&#xff1a; 容器控件 QGroupBox 表示一个带有…

计算机的错误计算(一百九十六)

摘要 用两个大模型计算 arccos(0.444). 结果保留 4位有效数字。两个大模型的计算结果相同&#xff0c;并均有误差。 例1. 计算 arccos(0.444). 结果保留 4位有效数字。 下面是与一个大模型的对话。 以上为与一大模型的对话。 下面是与另一大模型的对话。 点评&#xff1a; &…

Unity开发微信小游戏踩坑总结

前言 不记录真记不住&#xff0c;这个帖子以后不定时更新。 问题1&#xff1a;图片模糊 问题描述&#xff1a; 在Unity里什么事没有&#xff0c;进入到微信开发者工具里就已经模糊了&#xff0c;人物动画是一团马赛克&#xff0c;图片看着倒是没事。 问题原因&#xff1a;…

Python爬虫教程——7个爬虫小案例(附源码)_爬虫实例

本文介绍了7个Python爬虫小案例&#xff0c;包括爬取豆瓣电影Top250、猫眼电影Top100、全国高校名单、中国天气网、当当网图书、糗事百科段子和新浪微博信息&#xff0c;帮助读者理解并实践Python爬虫基础知识。 包含编程资料、学习路线图、源代码、软件安装包等&#xff01;【…

kafka小实站

需要先在前面的文章里面照着下载好kafka&#xff0c;并且启动 先启动zookeeper 项目目录 package kafka; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.springframework.kafka.annotation.KafkaListener; import…

【从零开始入门unity游戏开发之——C#篇39】C#反射使用——Type 类、Assembly 类、Activator 类操作程序集

文章目录 前言一、前置知识1、编译器2、程序集&#xff08;Assembly&#xff09;3、元数据&#xff08;Metadata&#xff09; 二、反射1、反射的概念2、反射的作用3、反射的核心Type 类3.1 Type 类介绍3.2 不同方法获取 Type3.3 获取type类型所在的程序集的相关信息 4、反射的常…

(桌面运维学习)通过备份C盘,进行Windows系统的软件初始化

通过PE工具备份C盘&#xff0c;进行Windows系统的软件初始化 需求场景&#xff1a;快速初始化一批型号和主板一样的电脑系统型号也要一致&#xff08;Win10专业版就最好全是WIn10专业版&#xff09;&#xff0c;初始化的内容包括已配置好的环境和已安装的软件。主要用于公司桌面…

【ELK】ES单节点升级为集群模式--太细了!

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言准备工作1. 查看现状【单节点】2. 原节点改集群模式3. 改es配置文件&#xff0c;增加集群相关配置项4. *改docker映射的端口* 启动新节点5. docker-compose起一…