本方法仅适用于 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