使用 QueryDSL 的 BooleanExpression 构建类型安全的查询表达式


文章目录
  • 使用 QueryDSL 的 BooleanExpression 构建类型安全的查询表达式
  • 常用方法及代码示例
  • 1. 等式和不等式条件
  • 2. 集合条件
  • 3. 空值检查
  • 4. 比较条件
  • 5. 模糊查询
  • 6. 别名和聚合
  • 综合代码案例
  • 案例 1:查找特定年龄范围和特定邮箱域的用户
  • 案例 2:统计某个城市中年龄大于 25 岁的用户数量
  • 案例 3:计算每个城市用户的平均年龄,并按平均年龄降序排序
  • 结论



在现代应用开发中,与数据库进行交互是不可避免的。传统的 SQL 查询虽然功能强大,但在复杂查询中容易引入错误。为了解决这个问题,QueryDSL 提供了一种类型安全的方式来构建查询表达式,使得查询的构建更加直观和安全。本文将介绍如何使用 QueryDSL 的 Expression 接口及其实现类来构建和操作查询表达式。

常用方法及代码示例

QueryDSL 提供了一系列方法来帮助我们生成各种查询条件。以下是一些常用的方法及其应用示例。

1. 等式和不等式条件
  • eq (equals): 用于生成等式条件。
QUser user = QUser.user;
BooleanExpression eqExample = user.name.eq("Alice");
// 生成条件:name = 'Alice'
  • 1.
  • 2.
  • 3.
  • ne (not equals): 用于生成不等式条件。
BooleanExpression neExample = user.name.ne("Bob");
// 生成条件:name != 'Bob'
  • 1.
  • 2.
2. 集合条件
  • in: 用于生成元素是否在集合中的条件。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
BooleanExpression inExample = user.name.in(names);
// 生成条件:name IN ('Alice', 'Bob', 'Charlie')
  • 1.
  • 2.
  • 3.
  • notIn: 用于生成元素是否不在集合中的条件。
BooleanExpression notInExample = user.name.notIn(names);
// 生成条件:name NOT IN ('Alice', 'Bob', 'Charlie')
  • 1.
  • 2.
3. 空值检查
  • isNull: 用于生成检查表达式是否为 null 的条件。
BooleanExpression isNullExample = user.email.isNull();
// 生成条件:email IS NULL
  • 1.
  • 2.
  • isNotNull: 用于生成检查表达式是否不为 null 的条件。
BooleanExpression isNotNullExample = user.email.isNotNull();
// 生成条件:email IS NOT NULL
  • 1.
  • 2.
4. 比较条件
  • lt (less than): 用于生成小于的条件。
BooleanExpression ltExample = user.age.lt(30);
// 生成条件:age < 30
  • 1.
  • 2.
  • loe (less or equal): 用于生成小于或等于的条件。
BooleanExpression loeExample = user.age.loe(30);
// 生成条件:age <= 30
  • 1.
  • 2.
  • gt (greater than): 用于生成大于的条件。
BooleanExpression gtExample = user.age.gt(18);
// 生成条件:age > 18
  • 1.
  • 2.
  • goe (greater or equal): 用于生成大于或等于的条件。
BooleanExpression goeExample = user.age.goe(18);
// 生成条件:age >= 18
  • 1.
  • 2.
  • between: 用于生成在两个值之间的条件。
BooleanExpression betweenExample = user.age.between(18, 30);
// 生成条件:age BETWEEN 18 AND 30
  • 1.
  • 2.
5. 模糊查询
  • like: 用于生成类似 SQL 中 LIKE 操作的条件。
BooleanExpression likeExample = user.name.like("A%");
// 生成条件:name LIKE 'A%'
  • 1.
  • 2.
6. 别名和聚合
  • as: 用于为表达式指定别名。
Expression<String> aliasExample = user.name.as("username");
// 为查询结果指定别名
  • 1.
  • 2.
  • count: 用于生成计数操作。
NumberExpression<Long> countExample = user.id.count();
// 生成条件:COUNT(id)
  • 1.
  • 2.
  • countDistinct: 用于生成去重计数操作。
NumberExpression<Long> countDistinctExample = user.id.countDistinct();
// 生成条件:COUNT(DISTINCT id)
  • 1.
  • 2.
  • sum: 用于生成求和操作。
NumberExpression<Integer> sumExample = user.age.sum();
// 生成条件:SUM(age)
  • 1.
  • 2.
  • avg: 用于生成平均值操作。
NumberExpression<Double> avgExample = user.age.avg();
// 生成条件:AVG(age)
  • 1.
  • 2.
  • min 和 max: 用于生成最小值和最大值操作。
NumberExpression<Integer> minExample = user.age.min();
// 生成条件:MIN(age)

NumberExpression<Integer> maxExample = user.age.max();
// 生成条件:MAX(age)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

综合代码案例

案例 1:查找特定年龄范围和特定邮箱域的用户
QUser user = QUser.user;
// 创建一个布尔表达式,用于查找年龄在 20 到 30 岁之间,并且邮箱域为 "@example.com" 的用户
BooleanExpression predicate = user.age.between(20, 30)
    .and(user.email.like("%@example.com"));

// 使用 QueryDSL 查询工厂,从用户表中选择符合条件的用户
List<User> users = queryFactory.selectFrom(user)
    .where(predicate) // 应用布尔表达式作为查询条件
    .fetch(); // 执行查询并获取结果列表
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
案例 2:统计某个城市中年龄大于 25 岁的用户数量
QUser user = QUser.user;
// 使用 QueryDSL 查询工厂,选择用户表中的用户 ID 计数
long count = queryFactory.select(user.id.count())
    .from(user) // 指定查询的表
    .where(user.city.eq("New York").and(user.age.gt(25))) // 条件:城市为 "New York" 且年龄大于 25 岁
    .fetchOne(); // 执行查询并获取单个结果(用户数量)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
案例 3:计算每个城市用户的平均年龄,并按平均年龄降序排序
QUser user = QUser.user;
// 使用 QueryDSL 查询工厂,选择每个城市的平均年龄
List<Tuple> result = queryFactory.select(user.city, user.age.avg().as("averageAge"))
    .from(user) // 指定查询的表
    .groupBy(user.city) // 按城市分组
    .orderBy(user.age.avg().desc()) // 按平均年龄降序排序
    .fetch(); // 执行查询并获取结果列表

// 遍历查询结果并输出每个城市的平均年龄
for (Tuple tuple : result) {
    String city = tuple.get(user.city); // 获取城市名称
    Double averageAge = tuple.get("averageAge", Double.class); // 获取平均年龄
    System.out.println(city + ": " + averageAge); // 打印城市名称和平均年龄
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

结论

通过使用 QueryDSL 提供的这些方法,我们可以轻松构建复杂的查询条件,避免传统字符串拼接方式带来的错误风险。QueryDSL 不仅提高了代码的可读性和维护性,还增强了查询的安全性。如果你还没有尝试过 QueryDSL,现在是时候开始探索这种强大的工具了。通过熟练掌握这些方法,你可以编写出更高效、健壮的数据库查询代码。