我做了一个JPA Specification的优化

一、参考

如何搞一个支持自定义函数和变量的四则运算的抽象语法树出来

二、概述

这是一个可以优化生成Specification对象的轮子,目前处于一个不太靠近中间的产品。

流程

在这里插入图片描述

效果感受

这是随便测试的一个例子,与下面讲解的例子不一样。
在这里插入图片描述

Specification正常的写法:

public Page<Image> pagingImage(String title, Integer start, Integer size) {
    Pageable pageable = PageRequest.of(start, size, Sort.by(Sort.Direction.DESC , "id"));
    Specification<Image> specification = (root, query, criteriaBuilder) -> {
        List<Predicate> and = new ArrayList<>();
        and.add(criteriaBuilder.like(root.get("title").as(String.class), String.format("%%%s%%", title)));
        and.add(criteriaBuilder.notLike(root.get("file").as(String.class), "%http://%"));
        and.add(criteriaBuilder.notLike(root.get("file").as(String.class), "%https://%"));
        List<Predicate> and$2 = new ArrayList<>();
        and$2.add(criteriaBuilder.equal(root.get("status").as(Integer.class), 3));
        and$2.and(criteriaBuilder.equal(root.get("deleted").as(Boolean.class), false));
        return criteriaBuilder.or(and.toArray(new Predicate[and.size()]), and$2.toArray(new Predicate[and$2.size()]));
    };
    Page<Image> page = imageRepository.findAll(specification, pageable);
    return page;
}

目前的写法:

private final SpecificationApplication<GroundEntity> specificationApplication = new SpecificationApplication<>(Image.class);

public Page<Image> pagingImage(String title, Integer start, Integer size) {
    Pageable pageable = PageRequest.of(start, size, Sort.by(Sort.Direction.DESC , "id"));
    String sql$1 = "file not like %http://% and file not like %https://%";
    String sql = String.format("(title like %%%s%% and %s) or (status=3 and deleted=false)", title, sql$1);
    Specification<Image> specification = specificationApplication.specification(sql);
    Page<Image> page = imageRepository.findAll(specification, pageable);
    return page;
}

上面的代码相对来说确实少了一些,但是字段在IDEA中没有提示、关键字没有提示,所以这是后面的改进方向,后面会用链式调用去改进,还有sql字段也没有相对应的验证,根据验证来决定拼接sql。

未来的写法可能是这样:

public Page<Image> pagingImage(String title, Integer start, Integer size) {
    Pageable pageable = PageRequest.of(start, size, Sort.by(Sort.Direction.DESC , "id"));
    Image image = new Image();
    Specification<Image> specification = image.openBracket()
        .title().notLike("%http://%")
        .and()
        .file().notLike("%https://%")
        .closeBracket()
        .openBracket()
        .or()
        .status().eq(3)
        .deleted().eq(false)
        .build();
    Page<Image> page = imageRepository.findAll(specification, pageable);
    return page;
}

因为在IDEA这样的编辑器中,方法都是有提示的,所有写上面的代码会显得非常轻松。但还是不够的,因为SQL要动态拼接,如何更加方便的进行拼接还是需要思考的问题。

三、实现原理

AST树结构

在这里插入图片描述

AstNode主要结构

  • AstDichotomyOperation

表达式由左右两边构成,左右两边都可以解析。
如:expression and expression

  • AstNoDichotomyOperation

表达式只有变量和关键字构成,不能继续解析。
如:xxx IS NULL | xxx IS NOT NULL | xxx ISNULL…

  • AstSymmetryOperation

单边的表达式,该表达式的右边还会继续解析。
如:!(expression) / not(expression)

  • AstStatic

最终的节点,无法被继续分析。

在这里插入图片描述

把表达式解析成Ast

  • AbstractOperationHandle:解决字符串的解析。
  • NoDichotomyHandle:解析 xxx IS NULL | xxx IS NOT NULL | xxx ISNULL …
  • DichotomyHandle:解析expression and expression。
  • SymmetryHandle:解析!(expression) / not(expression)
  • BracktHandle:解析(expression)

在这里插入图片描述

Ast 解析成 JPA Specification

感觉过程有点复杂,简单点描述来说,就是深度搜索AST树,在先序、中序、后序中分别让表达式入栈出栈的过程,最后predicate栈剩下的一个元素就是我们需要的Predicate。类协同比较核心的图我给出来,但是过程太过麻烦,就不给图了。

  • AstRecursion 深度搜索
  • OperationOrStaticHandle 协同各种逻辑、变量、值入栈出栈、创建Predicate并入栈出栈
  • StackParse 四个栈,存储值、变量、Predicate
  • PredicateGenerate 一个抽象的接口,用于创建对于类型的Predicate

在这里插入图片描述

三、项目地址

https://gitee.com/jiangjinghong520/jpa-specification.git

四、目前阶段的总结

目前是不能支持链式调用的中间产品,先一步一步做中间产品,做链式挺简单,但是要做好还是挺难,得研究一下SpringSecurity中build/config和Java Stream的设计,模仿它们。要更加方便的编程,还要借鉴lombok,掌握JSR 256技术。一下子是完全做不出来的,对于我来说一下子要学的东西还挺多,我觉得也挺难。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值