如何搞一个支持自定义函数和变量的四则运算的抽象语法树出来
这是一个可以优化生成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要动态拼接,如何更加方便的进行拼接还是需要思考的问题。
表达式由左右两边构成,左右两边都可以解析。
如:expression and expression
表达式只有变量和关键字构成,不能继续解析。
如:xxx IS NULL | xxx IS NOT NULL | xxx ISNULL…
单边的表达式,该表达式的右边还会继续解析。
如:!(expression) / not(expression)
最终的节点,无法被继续分析。
感觉过程有点复杂,简单点描述来说,就是深度搜索AST树,在先序、中序、后序中分别让表达式入栈出栈的过程,最后predicate栈剩下的一个元素就是我们需要的Predicate。类协同比较核心的图我给出来,但是过程太过麻烦,就不给图了。
https://gitee.com/jiangjinghong520/jpa-specification.git
目前是不能支持链式调用的中间产品,先一步一步做中间产品,做链式挺简单,但是要做好还是挺难,得研究一下SpringSecurity中build/config和Java Stream的设计,模仿它们。要更加方便的编程,还要借鉴lombok,掌握JSR 256技术。一下子是完全做不出来的,对于我来说一下子要学的东西还挺多,我觉得也挺难。