本方法仅适用于 JPQL

构造查询条件并序列化到文本字符串

QueryDSL 提供了 com.querydsl.core.types.Visitor 以访问 DSL 结点,在此基础上提供了 com.querydsl.core.support.SerializerBase 以序列化 DSL,该接口对应 JPQL 的实现为 com.querydsl.jpa.JPQLSerializer.

主要用到的方法:

com.querydsl.core.support.SerializerBase#handle(com.querydsl.core.types.Expression<?>) 用以序列化条件 ( com.querydsl.core.types.Predicate ).

首先构造一个查询条件:

final Predicate predicate = QEduAction.eduAction.id.eq(2L)
    .and(QEduAction.eduAction.name.like("%:GetCurrentTime"));

然后以 HQL 风格序列化到文本字符串:

JPQLSerializer jpqlSerializer = new JPQLSerializer(new HQLTemplates());
jpqlSerializer.handle(predicate);
String jpql = jpqlSerializer.toString();
// eduAction.id = ?1 and eduAction.name like ?2 escape '!'
com.querydsl.jpa.JPQLTemplates 的默认实现有很多个,针对项目选择,这里使用 com.querydsl.jpa.HQLTemplates .

关于查询条件序列化时丢失参数的问题

由于此法本质上是将查询条件序列化为模板,因此参数并不会跟着序列化.

如果需要解析出参数也不是不行,通过构造 com.querydsl.core.QueryMetadata 来获取 com.querydsl.core.types.PredicateOperation 并遍历其 args 节点,但是这里就需要分辨节点上是 Path 还是常量值,这部分涉及到的问题比较多,简单粗暴的办法则是直接提前构造参数列表.

List<Object> args = new ArrayList<>();
args.put(2L);
args.put("%s:GetCurrentTime");
final Predicate predicate = QEduAction.eduAction.id.eq((Long) args.get(0))
    .and(QEduAction.eduAction.name.like((String) args.get(1));

然后将参数列表也序列化,这个就很容易了,直接转成 JSON 即可.

反序列化查询条件

核心点在于使用 com.querydsl.core.types.dsl.Expressions#booleanTemplate(java.lang.String, java.lang.Object...) 来通过字符串构造模板后生成查询条件.

int argIndex = 0;
StringBuilder stringBuilder = new StringBuilder();
// 由于模板使用的参数占位符是 {#index} 而不是 HQL 的 ?#index,因此这里需要转换一下.
final String[] split = jpql.split("\\?\\d+");
for (int i = 0; i < split.length; i++) {
    if (i == split.length - 1) {
        continue;
    }
    stringBuilder.append(String.format("%s{%s}", split[i], argIndex++));
}
jpql = stringBuilder.toString();
// eduAction.id = {0} and eduAction.name like {1}
其实这一步可以直接在序列化后顺便做了.

直接作为查询条件使用

 new JPAQueryFactory(entityManager.getEntityManager())
                .select(QEduAction.eduAction)
                .from(QEduAction.eduAction)
                .where(
     Expressions.booleanTemplate(jpql, 2L, "%:GetCurrentTime")
 );
参数列表从前面生成的 JSON 反序列化而来.

作为附加查询条件使用

final BooleanTemplate booleanTemplate = Expressions.booleanTemplate(jpql, 2L, "%:GetCurrentTime");
final JPAQuery<EduAction> query = new JPAQueryFactory(entityManager.getEntityManager())
    .select(QEduAction.eduAction)
    .from(QEduAction.eduAction)
    .where(QEduAction.eduAction.policies.isEmpty());
query.getMetadata().addWhere(booleanTemplate);

生成的 HQL 为:

select eduAction
from EduAction eduAction
where eduAction.policies is empty and eduAction.id = ?1 and eduAction.name like ?2

krun
6.9k 声望11.3k 粉丝