QueryDSL记录
🍎
本文主要是记录我在工作中使用QueryDSL所遇到的一些问题以及解决办法。
使用 find_in_set 函数
某次需要使用 find_in_set 作为条件进行查询,但是经过测试报错如下:
QuerySyntaxException: unexpected AST node
解决办法
MYSQL原生sql只需要使用下面的格式即可
select * from table where id find_in_set(1, '1,2,3');
querydsl可以使用其 BooleanTemplate
完成此表达式的构建
但是, JPA就必须使用一下格式
BooleanTemplate booleanTemplate =
Expressions.booleanTemplate("find_in_set({0}, {1}) > 0", 1, "1,2,3");
// 1,2,3可以更换为数据库字段(querydsl表达式)
区别在哪呢?就是 > 0
子查询结果排序
比如需要根据某汇总数据结果进行倒序
原生sql格式大致如下, 需要根据 t2_count
进行排序
select t.id,
(select count(1) from table2 t1 where t1.id = t.t_id)
as t2_count
from table t order by t2_count desc;
解决办法
querydsl中可以使用 StringTemplate
对as别名字段进行排序
//查询列使用
select(Expressions.as(t2Count, "t2_count"))
//排序条件使用
orderBy(new OrderSpecifier(Order.DESC,
Expressions.stringTemplate("t2_count")));
ifnull
原生sql中可以使用
select ifnull(sum(count), 0) from table
如果 sum(count)
的结果为null, 则默认使用0代替
在querydsl中语法如下
//就是使用 coalesce(0)
select(QTable.table.count.sum().coalesce(0)).from(QTable.table)
对查询结果某些列进行计算作为条件使用
比如查询学生的考试分数,表中有总分
和平均分
字段,需要找出总分 - 平均分 > 30
的记录。
原生sql大致如下
select sum_score, avg_score, student_name from stu_score where sum_score - avg_score > 30
querydsl实现
JPAQuery<Tuple> jpaQuery = select(QStuScore.stuscore.sumScore, QStuScore.stuscore.avgScore).from(QStuScore.stuscore);
//添加条件
jpaQuery.where(Expressions.numberOperation(Integer.class, Ops.SUB,QStuScore.stuscore.sumScore, QStuScore.stuscore.avgScore).gt(30));
//说明
//Expressions.numberOperation(Integer.class, Ops.SUB,QStuScore.stuscore.sumScore, QStuScore.stuscore.avgScore)
//表示创建一个数字操作, 类型为Integer, Ops.SUB表示减操作, 后面两个就是操作的列, gt(30)表示结果需大于30
聚合函数中使用子查询
原生 sql 大致如下:
SELECT
id,
SUM(SELECT SUM(score) FROM table2 t2 t2.tid = t1.id) AS scoreSum
FROM table t1
GROUP BY t1.number
querydsl 实现:
var subQuery = JPAExpressions.select(QTable2.table2.score.sum()).from(QTable2.table2).where(...);
var sumSubQuery = Expressions.as(Expressions.numberOperation(Integer.class, Ops.AggOps.SUM_AGG, subQuery), "scoreSum");
// 再将 sumSubQuery 放入到另一个 select 中作为子查询使用即可
使用 Expressions.numberOperation(Integer.class, Ops.AggOps.SUM_AGG, 子查询)
即可实现。
使用 SQLQueryFactory 进行原生 SQL 查询
创建 SQLQueryFactory 实例
SQLQueryFactory sqlQueryFactory = new SQLQueryFactory(
new Configuration(new MySQLTemplates()),
dataSource
);
使用 SQLQueryFactory
就不能直接使用 querydsl 生成的 Qxxxx
了。
比方有实体类如下:
@Entity
@Table(name = "user_info")
public class UserInfo {
private Long id;
private String userName;
private String tel;
private SexEnum sex;
}
对应的就会有一个 QUserInfo
类。
根据 userName 字段进行模糊搜索
JPAQueryFactory
实现如下:
JPAQueryFactory qf;
QUserInfo qUserInfo = QUserInfo.qUserInfo;
qf.selectFrom(qUserInfo).where(qUserInfo.userName.like("%Tom%")).fetch();
SQLQueryFactory
实现如下:
import com.querydsl.core.types.dsl.Expressions;
SQLQueryFactory qf = new SQLQueryFactory(
new Configuration(new MySQLTemplates()),
dataSource
);
// 下面的 user_info 字符串对应实体 UserInfo 的表名
QUserInfo qUserInfo = new QUserInfo("user_info");
// 下面这一行生成 sql 后就是: user_info.user_name
StringPath userNamePath = Expressions.stringPath(qUserInfo, "user_name");
qf.select().from(qUserInfo).where(userNamePath.like("%Tom%")).fetch();
主要就是通过 Expressions
类构建出各种 Path
进行查询字段或查询条件的处理。
SQLQueryFactory 原生 SQL 查询枚举结果处理
通过上面的例子很明显的可以猜到在 Expressions
类中有构造枚举 Path 的方法。这个方法就是 enumPath
。使用方式如下:
EnumPath<SexEnum> sexPath = Expressions.enumPath(SexEnum.class, qUserInfo, "sex");
这样我们通过 sexPath
来构造条件等是完全没有问题的。但是直接使用 sexPath
放在 select 块中是不行的,通过调试代码可以发现查询出的 sex
字段是 String
类型,而非 SexEnum
类型,导致在调用 setter 时失败。
如果要查询枚举字段,则需要通过 EnumConversion
进行一次转换,如下:
EnumConversion<SexEnum> enumConversion = new EnumConversion<SexEnum>(sexPath);
在 select 块中就需要使用 enumConversion
。
select(Expressions.as(enumConversion, "sex")).from(qUserInfo);