1. 概述
Querydsl是一款开源的JAVA框架,用于构造安全的SQL查询语句,在使用的风格上更贴近于SQL。本文将讨论其集成在JPA中的使用方法。Querydsl起初仅提供了对Hibernate中的HQL的支持,但如今其已经全面支持JPA, JDO, JDBC, Lucene, Hibernate Search, MongoDB, Collections 以及 RDFBean 了。
2. 准备
在Spring Boot项目中编辑pom.xml文件,添加以下信息:
<properties> ... <querydsl.version>4.1.3</querydsl.version> </properties> <dependencies> ... <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-apt</artifactId> <version>${querydsl.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> <version>${querydsl.version}</version> </dependency> </dependencies> <build> <plugins> ... <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> </build>
如上所示,我们使用的Querdsl版本为4.1.3。添加了两个依赖以及一个MAVEN APT插件。该插件中声明的com.querydsl.apt.jpa.JPAAnnotationProcessor的作用是:查找以 javax.persistence.Entity注解的类,并自动生成其对应的用于Querdsl查询的类。
3. 小试牛刀
Querdsl根据实体来构造查询类型,然后在查询的过程中直接应用查询类型的属性,从而保证了在编译的阶段便能够发现其拼写错误,被认为是安全的。
相较于JPA Criteria Queries中在查询时指定字符串而造成不安全的特性,Querdsl在安全性上更加具有优势。
同时,Querdsl在查询时也保持了较高的一致性。下面,让我们分步查看Querds是如何工作的。
3.1 建立实体
新建学生Student实体如下:
/** * 学生 */ @Entity public class Student { @Id @GeneratedValue private Long id; /** * 姓名 */ private String name; /** * 学号 */ private String no; /** * 体重 */ private Integer weight; // setter and getter }
此时执行mvn compile,将在target文件夹对应的包中对应生成供查询使用的QStudent类。
我们可以通过获取QStudent中的静态变量student来获取一个QStudent的实例:
QStudent qStudent = QStudent.student;
如果你使用的是IDEA编辑器,可能会接收到找不到类的错误,此时可以尝试找到pom.xml,点击右键 -> maven -> Generate Sources and Update Folders。
3.2 使用JPAQueryFactory构建查询
我们可以如下代码来获取一个JPAQueryFactory实例:
JPAQueryFactory query = new JPAQueryFactory(entityManager);
注意:上述代码中的entityManager是由JPA中的EntityManager。
假设需要查找姓名为X的学生,则代码为:
/** * 查询姓名为X的学生 * @param name 学生姓名 * @return */ Student findByName(String name) { JPAQueryFactory query = new JPAQueryFactory(entityManager); QStudent qStudent = QStudent.student; return query.selectFrom(qStudent) .where(qStudent.name.eq(name)) .fetchOne(); }
上述代码中:selectFrom指定了查询的数据表为student,where()方法规定的查询的条件,fetchOne()返回相关的某条学生记录。
多条件组件查询示例:
query.selectFrom(qStudent) .where(qStudent.name.eq(name), qStudent.no.eq(no)) .fetchOne();
where()方法可能接收多个表达式(查询条件),在查询过程中对多个表达式(查询条件)做组合查询。此外,也可以调用表达式的and方法来完成组合查询:
query.selectFrom(qStudent) .where(qStudent.name.eq(name).and(qStudent.no.eq(no))) .fetchOne();
如果将上述代码转换为JPQL语言,则相当于:
select student form Student as student where student.name = ?1 and student.no = ?2
此外,还支持or查询:
/** * 按姓名或学号查询学生 * 满足任一条件即可 * * @param name 姓名 * @param no 学号 * @return */ List<Student> findByNameOrNo(String name, String no) { JPAQueryFactory query = new JPAQueryFactory(entityManager); QStudent qStudent = QStudent.student; return query.selectFrom(qStudent) .where(qStudent.name.eq(name).or(qStudent.no.eq(no))) .fetch(); }
4. 排序及聚合查询
4.1 排序
使用orderBy方法来实现按指定字段排序:
/** * 按体重排序 * * @return */ List<Student> findAllOrderByWeight() { JPAQueryFactory query = new JPAQueryFactory(entityManager); QStudent qStudent = QStudent.student; return query.selectFrom(qStudent) .orderBy(qStudent.weight.asc()) .fetch(); }
4.2 聚合
querydsl支持Sum, Avg, Max, Min等聚合函数,比如查询出所有学生中最大的体重:
int maxWeight = query.selectFrom(qStudent) .select(qStudent.weight.max()) .fetchFirst();
4.3 GroupBy
当然也支持groupBy,比如接学生的体重进行分类:
query.select(qStudent.weight) .from(qStudent) .groupBy(qStudent.weight) .fetch();
5. 单元测试
单元测试是保证代码正确的高效有力的保障手段,为此我们使用data.sql创建了4个学生,并新建如下单元测试进行验证:
package club.codedemo.querydslwithjpatutorial; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; import java.util.List; @SpringBootTest class StudentDaoTest { @Autowired StudentDao studentDao; @Test void findByName() { Assertions.assertEquals(1L, this.studentDao.findByName("zhangsan").getId()); } @Test void findByNameAndNo() { Assertions.assertEquals(1L, this.studentDao.findByNameAndNo("zhangsan", "200001").getId()); } @Test void findByNameAndNo1() { Assertions.assertEquals(1L, this.studentDao.findByNameAndNo1("zhangsan", "200001").getId()); } @Test void findByNameOrNo() { Assertions.assertEquals(2, this.studentDao.findByNameOrNo("zhangsan", "200002").size()); } @Test void findAllOrderByWeight() { List<Student> students = this.studentDao.findAllOrderByWeight(); Assertions.assertEquals(56, students.get(0).getWeight()); Assertions.assertEquals(65, students.get(students.size() - 1).getWeight()); } @Test void getMaxWeight() { Assertions.assertEquals(65, this.studentDao.getMaxWeight()); } @Test void groupByWeight() { Assertions.assertEquals(3, this.studentDao.groupByWeight().size()); } }
6. 总结
本文对Spring Boot JPA中使用Querydsl进行了初步介绍。Querydsl类型安全、语法简短、贴近于SQL的特点,使其很好做为JPA查询的一种补充。更多用户请参考Querydsl简介。