百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

JPA的好搭档 - QueryDSL(jpa的优势)

liuian 2025-06-28 15:16 3 浏览

今天给大家介绍一个JPA的好搭档:QueryDSL。


0. 前言

相对于 MyBatis ,本人更喜欢 Spring Data JPA ,因为它更符合面向对象的思想,然而 JPA 对复杂的查询支持较弱,常见的有两种方式:

一种方式是Repository继承JpaSpecificationExecutor接口,优点是支持复杂查询、编译期可以规避一些语法错误,缺点是语法晦涩难懂,学习成本太高。

还有一种方式就是直接写 SQL ,可以通过JdbcTemplate或者@Query注解的方式查询,优点是简单,缺点嘛...先看段代码:

@Query(value="select bs.* from " +
            "(select t.baseid,t.basesn,t.basecreatorfullname,t.basestatus,t.basesummary,'北京' faultprovince, '北京' faultcity, " +
            "replace(t.INC_ResponseLevel,'响应','') inc_responselevel,t.inc_happentime,t.basecreatedate,t.inc_equipmentmanufacturer, " +
            "t.inc_ne_name,t.inc_alarm_id,t.site_alerttype,t.alarmlevel,t.inc_alarm_desc,t.baseacceptouttime,t.basedealouttime, " +
            "decode(t.flagPretreatment,null,'否','未预处理','否','是') flagpretreatment,decode(t.flagPretreatment,null,'否','未预处理','否','是') flagpretreatment2, " +
            "decode(nvl(t.isImportantIncident, 0),1,'是','否') isimportantincident, decode(nvl(t.INC_IsEffectOP, 0),1,'是','否') inc_iseffectop, " +
            "t.operationdeal, t.t0_deal,t.faultdescription,t.dealguomodo,t.inc_alarm_cleartime,t.clearinctime, " +
            "t.renewtime,t.baseclosedate,nvl2(t.checkdealresult,'是','否') checkdealresult,g.jtitem1,g.jtitem2,g.jtitem3,t.reasontype " +
            "from T_DEMO t, T_DEMO_MAP g " +
            "where t.Sheet_type = '传输网络故障处理工单' and t.basestatus = '已归档' " +
            "and t.baseCloseDate >= :beginTime and t.baseCloseDate < :endTime " +
            "and (t.Withdraw_Desc = g.withdraw_desc or t.alarmname = g.alarmname) " +
            "union all " +
            "select t.baseid,t.basesn,t.basecreatorfullname,t.basestatus,t.basesummary,'北京' faultprovince, '北京' faultcity, " +
            "replace(t.INC_ResponseLevel,'响应','') inc_responselevel,t.inc_happentime,t.basecreatedate,t.inc_equipmentmanufacturer, " +
            "t.inc_ne_name,t.inc_alarm_id,t.site_alerttype,t.alarmlevel,t.inc_alarm_desc,t.baseacceptouttime,t.basedealouttime, " +
            "decode(t.flagPretreatment,null,'否','未预处理','否','是') flagpretreatment,decode(t.flagPretreatment,null,'否','未预处理','否','是') flagpretreatment2, " +
            "decode(nvl(t.isImportantIncident, 0),1,'是','否') isimportantincident, decode(nvl(t.INC_IsEffectOP, 0),1,'是','否') inc_iseffectop, " +
            "t.operationdeal,t.t0_deal,t.faultdescription,t.dealguomodo,t.inc_alarm_cleartime,t.clearinctime, " +
            "t.renewtime,t.baseclosedate,nvl2(t.checkdealresult,'是','否') checkdealresult,w.jtitem1,w.jtitem2,w.jtitem3,t.reasontype " +
            "from WF_BMCC_EOMS_ITDealFault t, (select distinct c.value,c.jtitem1,c.jtitem2,c.jtitem3 from WF_Config_EL_00_NetType c) w " +
            "where (t.Sheet_type = 'test0' or t.Sheet_type = 'test1' or t.Sheet_type = 'test2' " +
            "or t.Sheet_type = 'test3' or t.Sheet_type = 'test4') and t.basestatus = '已归档' " +
            "and t.baseCloseDate >= :beginTime and t.baseCloseDate < :endTime " +
            "and t.faultclass = w.value "+
            "  ) bs " +
            "where not exists (select dp.processbaseid from T_DEAL dp,T_GROUPS dg where dp.ProcessBaseSchema = " +
            "'DEALFAULT' and dp.processbaseid = bs.baseid and dp.groupid = dg.groupid) ",
            nativeQuery=true)
    List<DemoEntity> findOne(@Param("beginTime") long beginTime, @Param("endTime") long endTime);

看着想吐,就是直接写SQL的缺点。

下面隆重介绍 JPA 的最佳搭档:QueryDSL。它的语法跟SQL一样简单,且代码清晰,具有代码提示、编译期错误检查等优势。同时,在架构的层次上实现了读写分离:JPA负责增删改、QueryDSL负责查询。

1. QueryDsl介绍

QueryDSL 是一个通用的查询框架,专注于通过 Java API 构建类型安全的SQL查询。

QueryDSL 可以通过一组通用的查询 API 为用户构建出适合不同类型ORM框架或者是 SQL 的查询语句,也就是说 QueryDSL 是基于各种 ORM 框架以及 SQL 之上的一个通用的查询框架。

借助 QueryDSL 可以在任何支持的 ORM 框架或者 SQL 平台上以一种通用的API方式来构建查询。目前 QueryDSL 支持的平台包括 JPA、JDO、SQL、Mongodb 等等。

2. 引入QueryDSL

本文以gradle构建为例,前提是已引入 Spring Data Jpa ,且JPA可正常使用。

implementation 'com.querydsl:querydsl-jpa'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jpa'
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'

引入JPAQueryFactory

@Service
public class DemoQueryDSL {
    @Autowired
    private JPAQueryFactory queryFactory;
}

3. 创建JPA Entity

我们分别创建两个示例实体类:职员表、部门表。

/**
 * 职员表
 */
@Data
@Entity
@Table(name = "T_EMP")
public class Emp {
    @Id
    private String id;//ID
    @Column
    private String name;//姓名
    @Column
    private Integer age;//年龄
    @Column
    private String sex;//性别
    @Column
    private String depId;//部门ID
}

/**
 * 部门表
 */
@Data
@Entity
@Table(name = "T_DEP")
public class Dep {
    @Id
    private String id;//ID
    @Column
    private String depName;//部门名称
}

编译后,QueryDSL 会帮助我们会自动生成两个Q类:QEmp、QDep ,我们之后进行的所有查询都是围绕这些Q类来进行的。

4. 简单查询

QueryDSL提供了一种类似于SQL的面向对象写法,并且是类型安全的,搭配上IDEA的语句提示功能,写起SQL来非常舒服。

请欣赏下面的例子:

public void simpleSql() {
    QEmp emp = QEmp.emp;//员工表
    /**
     * 简单条件查询、排序,相当于sql:
     * select * from T_EMP
     *  where name like '张%' and age > 25
     *  order by age desc;
     */
    List<Emp> empList = queryFactory.select(emp)
            .where(emp.name.startsWith("张").and(emp.age.gt(25)))
            .orderBy(emp.age.desc())
            .fetch();

    /**
     * 分组查询,相当于sql:
     * select e.dep_id, max(e.age) from T_EMP e
     *  grouping by e.depId;
     */
    List<Tuple> list = queryFactory.select(emp.depId, emp.age.max())
            .groupBy(emp.depId)
            .fetch();

    /**
     * 分页查询,相当于sql:
     * select * from T_EMP e
     *  where e.dep_id='123'
     *  limit 20 10;
     */
    List<Emp> pageList = queryFactory.select(emp)
            .where(emp.depId.eq("123"))
            .offset(20)
            .limit(10)
            .fetch();
}

5. 复杂查询

QueryDSL对于复杂查询的支持也是相当优秀的,比如多表关联、动态条件查询等。

public void complexSql() {
    QEmp emp = QEmp.emp;//员工表
    QDep dep = QDep.dep;//部门表

    /**
     * 关联查询,相当于sql:
     * select e.* from T_EMP e
     *  left join T_DEP d on e.dep_id = d.id
     *  where d.dep_name = '财务部'
     */
    List<Emp> empList = queryFactory.select(emp)
            .from(emp)
            .leftJoin(dep).on(emp.depId.eq(dep.id))
            .where(dep.depName.eq("财务部"))
            .fetch();

    /**
     * 嵌套查询,相当于sql:
     * select e.* from T_EMP e
     *  where e.dep_id = (
     *      select id form T_DEP where dep_name = '财务部'
     *  )
     */
    List<Emp> empList1 = queryFactory.select(emp)
            .from(emp)
            .where(emp.depId.eq(
                    queryFactory.select(dep.id).from(dep)
                            .where(dep.depName.eq("财务部"))
            ))
            .fetch();

    /**
     * 动态条件、别名、获取不同结果:

     */
    BooleanBuilder condition = new BooleanBuilder();
    condition.and(emp.age.goe(25));
    if (true) {//动态条件
        condition.and(emp.sex.eq("男"));
    }
    queryFactory.select(emp.name.as("fullname"))
            .where(condition)
            .fetch();//查找多个结果,返回集合
          //.fetchOne();//查询只返回一个结果,如果返回多个则抛异常
          //.fetchFirst();//返回多个结果时,只取第一个
}

小结

以上就是 QueryDSL 的简单应用,用写SQL的方法来写代码,是不是很舒服呢!文中的例子仅仅浅尝辄止,希望大家可以实际应用一下,后续我也会更深入的讲解一些 QueryDSL 的高级用法。

相关推荐

什么是Java消息系统(JMS)?(java消息机制原理)

在JavaEE应用程序的典型体系结构中,数据访问层具有与数据存储进行通信的技术,如Java持久性API(JPA)、Java连接器体系结构(JCA),它将应用程序连接到企业信息系统(EIS);和Jav...

学习java没规划?2020最新路线图,大堆资源秒变大神

为什么编程就跟数学一样弯下头捡铅笔从此就听不懂?为什么学完之后感觉简单,刷题却像刷人生?为什么这个规划图这么受欢迎,这么多人看?为什么这个小图能在我们官网占据这么重要的位置?请观看本文《黑马程序员——...

Spring Data JPA:用最少的代码搞定数据库操作

SpringDataJPA:用最少的代码搞定数据库操作今天咱们聊聊SpringDataJPA这个神器,它简直就是JAVA开发者的好帮手,特别是那些被复杂的SQL语句折磨得苦不堪言的朋友。想象一...

Spring Data JPA的高级查询:从基础到进阶

SpringDataJPA的高级查询:从基础到进阶在Java的生态系统中,SpringDataJPA以其强大的功能和便捷性,成为了许多开发者处理数据库操作的首选工具。它简化了JPA的操作流程,...

客观对比Spring boot JPA 和Mybatis plus

SpringBootJPA和MyBatisPlus都是在Java生态系统中广泛使用的持久化框架,用于简化和加速与关系型数据库的交互。然而,它们在设计哲学、用法和功能上有一些不同。设计哲...

Hibernate 和 JPA(hibernate和jpa的区别)

Hibernate在5以后的版本中全面推进使用JPA的查询语法,甚至准备废弃掉自己已有的查询语法。什么是JPAJPA全称为JavaPersistenceAPI,Java持久化A...

[Java速成] 数据库基础,Connector/J、JDBC、JPA的关系(day 7)

先来看一个问题:在创建Java项目,选择数据库依赖时,如下图,你知道该如何正确的选择合适你项目的依赖吗?看完这篇文章,就能有个很好的认知了。后续的文章中,我们也需要使用数据库,所以在正式开始使用...

Java从入门到实战总踩坑?这6大核心+实战案例,帮你打通任督二脉

开篇:你是不是也这样?学Java越学越懵!最近有个刚学Java的朋友跟我吐槽:“看了3个月教程,语法背得滚瓜烂熟,结果写个‘用户登录’的小程序都卡壳——对象不会用、内存总溢出、多线程代码越调越乱……”...

盘点JPA中的骚操作(jpa使用教程)

相信大多数的Java后端开发者都听说过MyBatis,但是用过JPA的人却相对较少,有些人可能没有听说过。JPA在国外比较火用的人也比较多,国内大多被MyBatis的简单易上手所吸引。相对的,JPA和...

SpringBoot中使用Spring Data JPA

Springboot中如何集成springdatajpa一什么是ORM?ORM即Object-RelationlMapping,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体...

SpringBoot整合Spring Data JPA(SpringBoot整合JWT)

本篇要点简单介绍JPA。介绍快速SpringBoot快速整合JPAJPA是啥?TheJavaPersistenceAPIisastandardtechnologythatletsy...

Spring Boot中使用JPA与MyBatis有什么区别?

JPA(JavaPersistenceAPI)和MyBatis都是在SpringBoot应用程序中比较常用的两种持久化框架,他们各自有各自的优点和缺点,以及适合的使用场景,下面我们就来详细介绍...

Spring JPA – Multiple Databases

1.概述在本教程中,我们将为具有多个数据库的Spring数据JPA系统实现一个简单的Spring配置。2.实体首先,让我们创建两个简单的实体,每个实体都位于一个单独的数据库中。下面是第...

Spring Data JPA 的 JpaRepository与MyBatis区别对比

SpringDataJPA的JpaRepository和MyBatis是Java生态中两种非常流行但设计理念和使用方式截然不同的持久层框架。它们的核心区别在于如何管理对象-关系映射(...

Spring Data Jpa 介绍和详细入门案例搭建

1.SpringDataJPA的概念在介绍SpringDataJPA的时候,我们首先认识下Hibernate。Hibernate是数据访问解决技术的绝对霸主,使用O/R映射(Object-Re...