QueryDSL 查询条件的序列化与反序列化

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

一些尝试记录
记录一些技术上的尝试,主要在 Java、微服务一块但也不排除其他的 :)
6.9k 声望
11.4k 粉丝
0 条评论
推荐阅读
[工作随笔] 配置 cron 检查 K8s Pod 存活状态以推送钉钉消息
前段时间接手了一份维护老系统的任务。该系统使用了早期的 Spring Cloud 全家桶,其中有一个微服务随着时间运行会出现大量 CLOSE_WAIT 状态的 socket 连接以至于堵塞网关,检查后发现与 HttpClient 相关(可参考 ...

krun阅读 1.4k

PHP转Go实践:xjson解析神器「开源工具集」
我和劲仔都是PHP转Go,身边越来越多做PHP的朋友也逐渐在用Go进行重构,重构过程中,会发现php的json解析操作(系列化与反序列化)是真的香,弱类型语言的各种隐式类型转换,很大程度的减低了程序的复杂度。

王中阳Go10阅读 1.6k评论 2

封面图
与RabbitMQ有关的一些知识
工作中用过一段时间的Kafka,不过主要还是RabbitMQ用的多一些。今天主要来讲讲与RabbitMQ相关的一些知识。一些基本概念,以及实际使用场景及一些注意事项。

lpe2348阅读 1.9k

封面图
万字详解,吃透 MongoDB!
MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C++ 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂的数据类型,是一款非常...

JavaGuide4阅读 524

封面图
Git操作不规范,战友提刀来相见!
年终奖都没了,还要扣我绩效,门都没有,哈哈。这波骚Git操作我也是第一次用,担心闪了腰,所以不仅做了备份,也做了笔记,分享给大家。问题描述小A和我在同时开发一个功能模块,他在优化之前的代码逻辑,我在开...

王中阳Go5阅读 2.1k评论 2

封面图
Redis 发布订阅模式:原理拆解并实现一个消息队列
“65 哥,如果你交了个漂亮小姐姐做女朋友,你会通过什么方式将这个消息广而告之给你的微信好友?““那不得拍点女朋友的美照 + 亲密照弄一个九宫格图文消息在朋友圈发布大肆宣传,暴击单身狗。”像这种 65 哥通过朋...

码哥字节6阅读 1.4k

封面图
计算机网络连环炮40问
本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~

程序员大彬6阅读 794

6.9k 声望
11.4k 粉丝
宣传栏