`
Swifie
  • 浏览: 10712 次
  • 性别: Icon_minigender_2
  • 来自: 广州
社区版块
存档分类
最新评论

Spring Boot : 为 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

Java代码 
  1. <!--QueryDSL支持-->  
  2. <dependency>  
  3.     <groupId>com.querydsl</groupId>  
  4.     <artifactId>querydsl-apt</artifactId>  
  5.     <scope>provided</scope>  
  6. </dependency>  
  7. <!--QueryDSL支持-->  
  8. <dependency>  
  9.     <groupId>com.querydsl</groupId>  
  10.     <artifactId>querydsl-jpa</artifactId>  
  11. </dependency>  

 

  • 这里无需指定版本号,已在 spring-boot-dependencies 工程中定义。

3.2 添加 Maven 插件

添加这个插件是为了让程序自动生成 query type (查询实体,命名方式为:"Q"+对应实体名)。
上文引入的依赖中 querydsl-apt 即是为此插件服务的。

注:在使用过程中,如果遇到 query type 无法自动生成的情况,用maven更新一下项目即可解决(右键项目 -> Maven -> Update Folders)。

代码清单:spring-boot-jpa-querydsl/pom.xml

Java代码 
  1. <plugins>  
  2.     <plugin>  
  3.         <groupId>org.springframework.boot</groupId>  
  4.         <artifactId>spring-boot-maven-plugin</artifactId>  
  5.     </plugin>  
  6.     <plugin>  
  7.         <groupId>com.mysema.maven</groupId>  
  8.         <artifactId>apt-maven-plugin</artifactId>  
  9.         <version>1.1.3</version>  
  10.         <executions>  
  11.             <execution>  
  12.                 <goals>  
  13.                     <goal>process</goal>  
  14.                 </goals>  
  15.                 <configuration>  
  16.                     <outputDirectory>target/generated-sources/java</outputDirectory>  
  17.                     <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>  
  18.                 </configuration>  
  19.             </execution>  
  20.         </executions>  
  21.     </plugin>  
  22. </plugins>  

 

3.3 更新和删除

在 JPA 中已经为我们提供了非常简便的更新和删除的使用方式,我们完全没有必要使用 QueryDSL 的更新和删除,不过这里还是给出用法,供大家参考:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java

Java代码 
  1. @Override  
  2. public Long update(String id, String nickName) {  
  3.     QUserModel userModel = QUserModel.userModel;  
  4.     // 更新  
  5.     return queryFactory.update(userModel).set(userModel.nickName, nickName).where(userModel.id.eq(id)).execute();  
  6. }  
  7.   
  8. @Override  
  9. public Long delete(String id) {  
  10.     QUserModel userModel = QUserModel.userModel;  
  11.     // 删除  
  12.     return queryFactory.delete(userModel).where(userModel.id.eq(id)).execute();  
  13. }  

 3.2 查询

QueryDSL 在查询这方面可以说玩的非常花了,比如一些有关 select() 和 fetch() 常用的写法如下:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java

Java代码 
  1. @Override  
  2. public List<String> selectAllNameList() {  
  3.     QUserModel userModel = QUserModel.userModel;  
  4.     // 查询字段  
  5.     return queryFactory.select(userModel.nickName).from(userModel).fetch();  
  6. }  
  7.   
  8. @Override  
  9. public List<UserModel> selectAllUserModelList() {  
  10.     QUserModel userModel = QUserModel.userModel;  
  11.     // 查询实体  
  12.     return queryFactory.selectFrom(userModel).fetch();  
  13. }  
  14.   
  15. @Override  
  16. public List<UserDTO> selectAllUserDTOList() {  
  17.     QUserModel userModel = QUserModel.userModel;  
  18.     QLessonModel lessonModel = QLessonModel.lessonModel;  
  19.     // 连表查询实体并将结果封装至DTO  
  20.     return queryFactory  
  21.             .select(  
  22.                     Projections.bean(UserDTO.class, userModel.nickName, userModel.age, lessonModel.startDate, lessonModel.address, lessonModel.name)  
  23.             )  
  24.             .from(userModel)  
  25.             .leftJoin(lessonModel)  
  26.             .on(userModel.id.eq(lessonModel.userId))  
  27.             .fetch();  
  28. }  
  29.   
  30. @Override  
  31. public List<String> selectDistinctNameList() {  
  32.     QUserModel userModel = QUserModel.userModel;  
  33.     // 去重查询  
  34.     return queryFactory.selectDistinct(userModel.nickName).from(userModel).fetch();  
  35. }  
  36.   
  37. @Override  
  38. public UserModel selectFirstUser() {  
  39.     QUserModel userModel = QUserModel.userModel;  
  40.     // 查询首个实体  
  41.     return queryFactory.selectFrom(userModel).fetchFirst();  
  42. }  
  43.   
  44. @Override  
  45. public UserModel selectUser(String id) {  
  46.     QUserModel userModel = QUserModel.userModel;  
  47.     // 查询单个实体,如果结果有多个,会抛<code>NonUniqueResultException</code>。  
  48.     return queryFactory.selectFrom(userModel).fetchOne();  

 

3.4 复杂查询操作

上面列举了简单的查询,但实际我们会遇到相当复杂的操作,比如子查询,多条件查询,多表连查,使用示例如下:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/LessonServiceImpl.java

Java代码 
  1. @Service  
  2. public class LessonServiceImpl implements LessonService {  
  3.   
  4.     @Autowired  
  5.     JPAQueryFactory queryFactory;  
  6.   
  7.     @Override  
  8.     public List<LessonModel> findLessonList(String name, Date startDate, String address, String userId) throws ParseException {  
  9.         QLessonModel lessonModel = QLessonModel.lessonModel;  
  10.         SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");  
  11.         // 多条件查询示例  
  12.         return queryFactory.selectFrom(lessonModel)  
  13.                 .where(  
  14.                         lessonModel.name.like("%" + name + "%")  
  15.                         .and(lessonModel.address.contains(address))  
  16.                         .and(lessonModel.userId.eq(userId))  
  17.                         .and(lessonModel.startDate.between(simpleDateFormat.parse("2018-12-31 00:00:00"), new Date()))  
  18.                 )  
  19.                 .fetch();  
  20.     }  
  21.   
  22.     @Override  
  23.     public List<LessonModel> findLessonDynaList(String name, Date startDate, String address, String userId) throws ParseException {  
  24.         QLessonModel lessonModel = QLessonModel.lessonModel;  
  25.         SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");  
  26.   
  27.         // 动态查询示例  
  28.         BooleanBuilder builder = new BooleanBuilder();  
  29.   
  30.         if (!StringUtils.isEmpty(name)){  
  31.             builder.and(lessonModel.name.like("%" + name + "%"));  
  32.         }  
  33.   
  34.         if (startDate != null) {  
  35.             builder.and(lessonModel.startDate.between(simpleDateFormat.parse("2018-12-31 00:00:00"), new Date()));  
  36.         }  
  37.   
  38.         if (!StringUtils.isEmpty(address)) {  
  39.             builder.and(lessonModel.address.contains(address));  
  40.         }  
  41.   
  42.         if (!StringUtils.isEmpty(userId)) {  
  43.             builder.and(lessonModel.userId.eq(userId));  
  44.         }  
  45.   
  46.         return queryFactory.selectFrom(lessonModel).where(builder).fetch();  
  47.     }  
  48.   
  49.     @Override  
  50.     public List<LessonModel> findLessonSubqueryList(String name, String address) {  
  51.         QLessonModel lessonModel = QLessonModel.lessonModel;  
  52.         // 子查询示例,并无实际意义  
  53.         return queryFactory.selectFrom(lessonModel)  
  54.                 .where(lessonModel.name.in(  
  55.                         JPAExpressions  
  56.                                 .select(lessonModel.name)  
  57.                                 .from(lessonModel)  
  58.                                 .where(lessonModel.address.eq(address))  
  59.                 ))  
  60.                 .fetch();  
  61.     }  
  62. }  

 

3.5 Mysql 聚合函数

QueryDSL 已经内置了一些常用的 Mysql 的聚合函数,如果遇到 QueryDSL 没有提供的聚合函数也无需慌张, QueryDSL 为我们提供了 Expressions 这个类,我们可以使用这个类手动拼接一个就好,如下示例:

代码清单:spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java

Java代码 
  1. @Override  
  2. public String mysqlFuncDemo(String id, String nickName, int age) {  
  3.   
  4.     QUserModel userModel = QUserModel.userModel;  
  5.   
  6.     // Mysql 聚合函数示例  
  7.   
  8.     // 聚合函数-avg()  
  9.     Double averageAge = queryFactory.select(userModel.age.avg()).from(userModel).fetchOne();  
  10.   
  11.     // 聚合函数-sum()  
  12.     Integer sumAge = queryFactory.select(userModel.age.sum()).from(userModel).fetchOne();  
  13.   
  14.     // 聚合函数-concat()  
  15.     String concat = queryFactory.select(userModel.nickName.concat(nickName)).from(userModel).fetchOne();  
  16.   
  17.     // 聚合函数-contains()  
  18.     Boolean contains = queryFactory.select(userModel.nickName.contains(nickName)).from(userModel).where(userModel.id.eq(id)).fetchOne();  
  19.   
  20.     // 聚合函数-DATE_FORMAT()  
  21.     String date = queryFactory.select(Expressions.stringTemplate("DATE_FORMAT({0},'%Y-%m-%d')", userModel.createDate)).from(userModel).fetchOne();  
  22.   
  23.     return null;  
  24. }  

 

4. 小结

有关 QueryDSL 的介绍到这里就结束了,不知道各位读者看了上面的示例,有没有一种直接读 SQL 的感觉,而且这种 SQL 还是使用 OOM 的思想,将原本 Hibernate 没有做好的事情给出了一个相当完美的解决方案,上手简单易操作,而又无需写 SQL ,实际上我们操作的还是对象类。

分享到:
评论

相关推荐

    spring-boot-jpa-querydsl整合源码

    spring-boot-jpa-querydsl 整合源码

    springdata-jpa-querydsl

    3. 配置Querydsl:在Spring Boot项目中,通常在配置类中启用Querydsl并设置实体路径,以便自动生成Q类。 4. 创建查询:在Repository接口中,可以使用Querydsl提供的Q类来创建复杂查询,例如,通过比较、聚合、排序等...

    详解spring boot jpa整合QueryDSL来简化复杂操作

    详解Spring Boot JPA整合QueryDSL来简化复杂操作 在Spring Boot项目中,使用JPA(Java Persistence API)来进行数据库操作时,经常会遇到复杂的SQL查询问题,而QueryDSL正是用来简化JPA操作的。QueryDSL定义了一种...

    Spring Data JPA 笔记

    Spring Data JPA则是在JPA之上构建的一层抽象,它扩展了JPA的功能,提供了更多的便利。例如,Spring Data JPA支持自动化的查询生成,只需定义Repository接口,无需编写任何实现代码,就可以执行CRUD(创建、读取、...

    整合Spring Data JPA1

    Spring Data JPA 是 Spring Data 框架的一个重要组成部分,它为 Java 持久层提供了一种基于 JPA(Java Persistence API)的简单、高效的解决方案。JPA 是 Java 标准,用于管理关系数据库中的对象,它通过 ORM(对象...

    spring-boot-querydsl.zip

    综上所述,本教程详细介绍了如何在Spring Boot项目中集成QueryDSL,包括其配置、Q类的生成、查询表达式的构建以及分页排序功能的实现。通过学习和实践,开发者能够充分利用QueryDSL的便利性,提升Java开发中的数据...

    springboot+spring-data-jpa maven项目

    - Spring Data JPA 提供了 Querydsl 或 Specifications API 来实现动态查询,可以根据参数灵活构造查询条件。 6. **分页查询**: - Spring Data JPA 支持 Pageable 接口,可以通过 PageRequest 实例来指定分页和...

    Springboot-JPA-QueryDSL-H2:Springboot + JPA +查询DSL + H2

    在"Springboot-JPA-QueryDSL-H2"项目中,开发者结合了这三种技术,构建了一个基于Spring Boot的应用,该应用使用JPA进行数据库操作,通过Querydsl创建类型安全的查询,而数据库则选用H2。以下是这个项目可能涉及的...

    springboot,jpa,spring data,jdbc template使用代码

    这是Spring Data针对JPA的扩展,它为JPA提供了一套通用的 Repository 模式。开发者只需定义接口并添加特定的注解,如`@Query`,就能利用Spring Data JPA自动处理JPA操作。这样,我们无需编写大量的DAO层代码,大大...

    Spring Data JPA

    - **Spring Boot集成**:在Spring Boot项目中,Spring Data JPA能自动配置,极大地简化了设置过程。 - **Spring MVC**:与Spring MVC的Controller和Service层配合,实现Web应用的数据处理。 - **Spring Data REST**...

    spring-data-jpa-master.rar

    1. **配置**:在 Spring Boot 的配置文件中启用 Spring Data JPA 和数据库连接,并配置 JPA 实体扫描路径。 2. **定义实体**:创建 JPA 实体类,使用注解定义表名和字段。 3. **创建 Repository**:定义 Repository ...

    尚硅谷 佟刚 spring_data+jpa pdf 资源

    Spring Data JPA是Spring Framework的重要组成部分,它简化了Java Persistence API (JPA) 的使用,为开发者提供了更高效、更便捷的数据操作方式。 首先,Spring Data JPA的核心概念是Repository抽象。它通过定义一...

    spring boot教程源码

    Spring Boot 是一个由 Pivotal Team 开发的用于简化 Spring 应用程序初始搭建以及开发...通过深入学习和实践这些章节的内容,你将能够熟练地创建、配置和管理 Spring Boot 应用,并为更复杂的微服务架构打下坚实基础。

    基于spring boot+spring data+Thymeleaf+mysql的简单工程

    7. **JPA Querydsl**:Spring Data 支持使用 Querydsl 进行类型安全的查询,这是一种静态类型查询API,可提高代码的可读性和可维护性。 8. **H2 内存数据库**:在开发阶段,Spring Boot 默认会启用 H2 数据库,这是...

    springData-jpa-demo

    通过这个“springData-jpa-demo”,开发者可以逐步学习如何配置Spring Data JPA,创建和使用Repository,以及如何在实际项目中整合Spring Boot和数据库操作。此外,它也可以作为模板,帮助开发者快速搭建自己的...

    Spring-data-jpa示例源码

    6. **Integration with Spring Boot**: 如果例子中包含 Spring Boot,那么可以看到如何在 Spring Boot 应用中配置 Spring Data JPA,包括数据源配置、JPA 配置以及自动配置的使用。 7. **Auditing**: Spring Data ...

    spring-data Jpa Jar包

    Spring Data JPA在JPA的基础上提供了一层抽象,提供了更高级别的查询支持和自动化配置。 1. **核心概念** - **Repository接口**:Spring Data JPA的核心在于Repository接口,它定义了通用的CRUD操作。开发者可以...

    spring data jpa demo

    5. **启动服务**:Spring Boot 应用启动后,Spring Data JPA 会自动扫描并创建 Repository 实例。 【查询方法的创建】 1. **自动查询方法**:Spring Data JPA 可以根据方法名自动解析生成对应的 SQL。例如,`...

Global site tag (gtag.js) - Google Analytics