为 JPA 插上翅膀的 QueryDSL(为梦想插上翅膀)
ccwgpt 2024-09-21 13:44 33 浏览 0 评论
如果我的文章对您有帮助,请关注支持下作者的公众号:极客挖掘机,获取最新干货推送:)
在前面的文章中,我们介绍了 JPA 的基础使用方式,《Spring Boot (三): ORM 框架 JPA 与连接池 Hikari》,本篇文章,我们由入门至进阶的介绍一下为 JPA 插上翅膀的 QueryDSL。
1. 引言
不可否认的是 JPA 使用是非常方便的,极简化的配置,只需要使用注解,无需任何 xml 的配置文件,语义简单易懂,但是,以上的一切都建立在单表查询的前提下的,我们可以使用 JPA 默认提供的方法,简单加轻松的完成 CRUD 操作。
但是如果涉及到多表动态查询, JPA 的功能就显得有些捉襟见肘了,虽然我们可以使用注解 @Query ,在这个注解中写 SQL 或者 HQL 都是在拼接字符串,并且拼接后的字符串可读性非常的差,当然 JPA 还为我们提供了 Specification 来做这件事情,从我个人使用体验上来讲,可读性虽然还不错,但是在初学者上手的时候, Predicate 和 CriteriaBuilder 使用方式估计能劝退不少人,而且如果直接执行 SQL 连表查询,获得是一个 Object[] ,类型是什么?字段名是什么?这些都无法直观的获得,还需我们手动将 Object[] 映射到我们需要的 Model 类里面去,这种使用体验无疑是极其糟糕的。
这一切都在 QueryDSL 出世以后终结了, QueryDSL 语法与 SQL 非常相似,代码可读性非常强,异常简介优美,,并且与 JPA 高度集成,无需多余的配置,从笔者个人使用体验上来讲是非常棒的。可以这么说,只要会写 SQL ,基本上只需要看一下示例代码完全可以达到入门的级别。
2. QueryDSL 简介
QueryDSL 是一个非常活跃的开源项目,目前在 Github 上的发布的 Release 版本已经多达 251 个版本,目前最新版是 4.2.1 ,并且由 Querydsl Google组 和 StackOverflow 两个团队提供支持。
QueryDSL 是一个框架,可用于构造静态类型的类似SQL的查询。可以通过诸如 QueryDSL 之类的 API 构造查询,而不是将查询编写为内联字符串或将其外部化为XML文件。
例如,与简单字符串相比,使用 API 的好处是
- IDE中的代码完成
- 几乎没有语法无效的查询
- 可以安全地引用域类型和属性
- 更好地重构域类型的更改
3. QueryDSL 使用实战
3.1 引入 Maven 依赖
代码清单:spring-boot-jpa-querydsl/pom.xml
<!--QueryDSL支持--> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <scope>provided</scope> </dependency> <!--QueryDSL支持--> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> </dependency>
- 这里无需指定版本号,已在 spring-boot-dependencies 工程中定义。
3.2 添加 Maven 插件
添加这个插件是为了让程序自动生成 query type (查询实体,命名方式为:"Q"+对应实体名)。
上文引入的依赖中 querydsl-apt 即是为此插件服务的。
注:在使用过程中,如果遇到 query type 无法自动生成的情况,用maven更新一下项目即可解决(右键项目 -> Maven -> Update Folders)。
代码清单:spring-boot-jpa-querydsl/pom.xml
<plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin> </plugins>
3.3 更新和删除
在 JPA 中已经为我们提供了非常简便的更新和删除的使用方式,我们完全没有必要使用 QueryDSL 的更新和删除,不过这里还是给出用法,供大家参考:
代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java
@Override public Long update(String id, String nickName) { QUserModel userModel = QUserModel.userModel; // 更新 return queryFactory.update(userModel).set(userModel.nickName, nickName).where(userModel.id.eq(id)).execute(); } @Override public Long delete(String id) { QUserModel userModel = QUserModel.userModel; // 删除 return queryFactory.delete(userModel).where(userModel.id.eq(id)).execute(); }
3.2 查询
QueryDSL 在查询这方面可以说玩的非常花了,比如一些有关 select() 和 fetch() 常用的写法如下:
代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java
@Override public List<String> selectAllNameList() { QUserModel userModel = QUserModel.userModel; // 查询字段 return queryFactory.select(userModel.nickName).from(userModel).fetch(); } @Override public List<UserModel> selectAllUserModelList() { QUserModel userModel = QUserModel.userModel; // 查询实体 return queryFactory.selectFrom(userModel).fetch(); } @Override public List<UserDTO> selectAllUserDTOList() { QUserModel userModel = QUserModel.userModel; QLessonModel lessonModel = QLessonModel.lessonModel; // 连表查询实体并将结果封装至DTO return queryFactory .select( Projections.bean(UserDTO.class, userModel.nickName, userModel.age, lessonModel.startDate, lessonModel.address, lessonModel.name) ) .from(userModel) .leftJoin(lessonModel) .on(userModel.id.eq(lessonModel.userId)) .fetch(); } @Override public List<String> selectDistinctNameList() { QUserModel userModel = QUserModel.userModel; // 去重查询 return queryFactory.selectDistinct(userModel.nickName).from(userModel).fetch(); } @Override public UserModel selectFirstUser() { QUserModel userModel = QUserModel.userModel; // 查询首个实体 return queryFactory.selectFrom(userModel).fetchFirst(); } @Override public UserModel selectUser(String id) { QUserModel userModel = QUserModel.userModel; // 查询单个实体,如果结果有多个,会抛`NonUniqueResultException`。 return queryFactory.selectFrom(userModel).fetchOne(); }
3.4 复杂查询操作
上面列举了简单的查询,但实际我们会遇到相当复杂的操作,比如子查询,多条件查询,多表连查,使用示例如下:
代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/LessonServiceImpl.java
@Service public class LessonServiceImpl implements LessonService { @Autowired JPAQueryFactory queryFactory; @Override public List<LessonModel> findLessonList(String name, Date startDate, String address, String userId) throws ParseException { QLessonModel lessonModel = QLessonModel.lessonModel; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); // 多条件查询示例 return queryFactory.selectFrom(lessonModel) .where( lessonModel.name.like("%" + name + "%") .and(lessonModel.address.contains(address)) .and(lessonModel.userId.eq(userId)) .and(lessonModel.startDate.between(simpleDateFormat.parse("2018-12-31 00:00:00"), new Date())) ) .fetch(); } @Override public List<LessonModel> findLessonDynaList(String name, Date startDate, String address, String userId) throws ParseException { QLessonModel lessonModel = QLessonModel.lessonModel; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); // 动态查询示例 BooleanBuilder builder = new BooleanBuilder(); if (!StringUtils.isEmpty(name)){ builder.and(lessonModel.name.like("%" + name + "%")); } if (startDate != null) { builder.and(lessonModel.startDate.between(simpleDateFormat.parse("2018-12-31 00:00:00"), new Date())); } if (!StringUtils.isEmpty(address)) { builder.and(lessonModel.address.contains(address)); } if (!StringUtils.isEmpty(userId)) { builder.and(lessonModel.userId.eq(userId)); } return queryFactory.selectFrom(lessonModel).where(builder).fetch(); } @Override public List<LessonModel> findLessonSubqueryList(String name, String address) { QLessonModel lessonModel = QLessonModel.lessonModel; // 子查询示例,并无实际意义 return queryFactory.selectFrom(lessonModel) .where(lessonModel.name.in( JPAExpressions .select(lessonModel.name) .from(lessonModel) .where(lessonModel.address.eq(address)) )) .fetch(); } }
3.5 Mysql 聚合函数
QueryDSL 已经内置了一些常用的 Mysql 的聚合函数,如果遇到 QueryDSL 没有提供的聚合函数也无需慌张, QueryDSL 为我们提供了 Expressions 这个类,我们可以使用这个类手动拼接一个就好,如下示例:
代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java
@Override public String mysqlFuncDemo(String id, String nickName, int age) { QUserModel userModel = QUserModel.userModel; // Mysql 聚合函数示例 // 聚合函数-avg() Double averageAge = queryFactory.select(userModel.age.avg()).from(userModel).fetchOne(); // 聚合函数-sum() Integer sumAge = queryFactory.select(userModel.age.sum()).from(userModel).fetchOne(); // 聚合函数-concat() String concat = queryFactory.select(userModel.nickName.concat(nickName)).from(userModel).fetchOne(); // 聚合函数-contains() Boolean contains = queryFactory.select(userModel.nickName.contains(nickName)).from(userModel).where(userModel.id.eq(id)).fetchOne(); // 聚合函数-DATE_FORMAT() String date = queryFactory.select(Expressions.stringTemplate("DATE_FORMAT({0},'%Y-%m-%d')", userModel.createDate)).from(userModel).fetchOne(); return null; }
4. 小结
有关 QueryDSL 的介绍到这里就结束了,不知道各位读者看了上面的示例,有没有一种直接读 SQL 的感觉,而且这种 SQL 还是使用 OOM 的思想,将原本 Hibernate 没有做好的事情给出了一个相当完美的解决方案,上手简单易操作,而又无需写 SQL ,实际上我们操作的还是对象类。
5. 示例代码
示例代码-Github:https://github.com/meteor1993/spring-boot-examples/tree/master/spring-boot-jpa-querydsl
示例代码-Gitee:https://gitee.com/inwsy/spring-boot-examples/tree/master/spring-boot-jpa-querydsl
6. 参考
《QueryDSL 官方文档》:http://www.querydsl.com/static/querydsl/latest/reference/html/index.html
相关推荐
- 丨公司丨公司大架构整理汇总
-
注:本文转自团队成员原创作品,特此鸣谢(公号:法海图鉴)今日话题公司大架构整理背景介绍经过前几期话题对各种企业类型的介绍,想必大家已经有了初步认识。之后我将带着大家开启对公司的深入了解。本期...
- 图解物理--八年级物理下册最全知识框架导图
-
第七章力1力2弹力3重力第八章运动和力1牛顿第一定律2二力平衡3摩擦力第九章压强1压强2液体压强3大气压强4流体压强与流速的关系第十章浮力1浮力2阿基米德原理3物体的浮沉条件及应用第十一章功...
- 八年级上册生物,思维导图,期末高分必备资料,家长收藏
-
这是八年级上册生物的思维导图,孩子在背诵知识点的时候,可以看一下知识点在导图中的位置,形成对知识点整体的把握,有助于学生拿高分,特别是图片中带红色星星的部分,更是要注意背诵,是重点内容。家长可以把图片...
- 2019政府工作报告精华,这张思维导图里全都有
-
每经记者:李可愚每经编辑:陈星每日经济新闻
- 图解薪酬体系结构设计
-
...
- 司考复习独家总结!一张图总结行政法知识结构体系
-
作为三大实体法之一,行政法的分值在60分左右,行政法在司法考试中一直比较平稳常规,没有偏题怪题,还是比较容易得分的。小编要提醒大家,在3月之前要把三大实体法学习一遍。下图是厚大在线360导学师小周总结...
- 实用干货!高中物理框架图,让零碎知识“串联”起来
-
高中物理学习一定要抓好逻辑结构大框架!了解整个知识框架体系后,更易抓住骨干知识,干掉重难知识点~今天给大家分享高中物理的框架图同学们赶紧收藏起来吧!力学知识结构图光学知识结构图热学、原子物理知识结构图...
- 254m超高层办公楼型钢砼框架-核心筒结构图
-
高度类别:超高层建筑钢筋混凝土结构:框架,框架核心筒钢结构:钢框架建筑功能:办公包含:办公楼57层(-3层)254.150m钻孔灌注桩桩+筏板型钢混凝土框架-钢筋混凝土核心筒西裙房2层(-...
- 砖混结构与框架结构,究竟有何区别?千万别被坑!
-
现在买房装修的人最怕啥?不是价格高,而是房子不安全!两种主流建筑结构,砖混靠墙,框架靠柱子,选错了隔墙都可能要命。简单说,砖混便宜但别碰高层,框架贵点但能保命。砖混那些承重墙根本不能拆,想砸墙改个开放...
- 大师一百——高中化学必考:《元素周期律》考点框架图
-
今天大师给大家带来的是高中化学的《元素周期律》考点框架图,高中的同学必须牢记于心,这种重要的考点,考试是一定会考的!化学大师...
- 需求分析框架图
-
需求分析框架图
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- JAVA集合框架 (47)
- mfc框架 (52)
- abb框架断路器 (48)
- beego框架 (52)
- java框架spring (58)
- grpc框架 (65)
- tornado框架 (48)
- 前端框架bootstrap (54)
- orm框架有哪些 (51)
- ppt框架 (48)
- 内联框架 (52)
- cad怎么画框架 (58)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)