























这是一个创建于 101 天前的主题,其中的信息可能已经有所发展或是发生改变。
SpringDataJPA 支持通过 findBy 方法构建查询语句,然而 findBy 方法构建的查询条件是固定的,不支持忽略值为 null 的参数对应的查询条件,这导致我们需要为每一组参数组合定义一个 findBy 方法。例如:
findByAuthor
findByAuthorAndPublishedYearGreaterThan
findByAuthorAndPublishedYearLessThan
findByAuthorAndPublishedYearGreaterThanAndPublishedYearLessThan
当条件变多时,方法名会变长,参数也会变多,并触发“长参数”坏味道。解决这一问题的重构方法是“引入参数对象”,即把所有的参数定义在一个对象里。同时,我们把 findBy 的方法名中对应查询条件的部分作为这个对象的字段名称,我们便能为每个字段构建一个查询条件,并且根据对象的赋值情况,动态地组合非空字段对应的查询条件组合为查询子句。
public class BookQuery {
String author;
Integer publishedYearGreaterThan;
Integer publishedYearLessThan;
//...
}
基于此对象,我们可以把所有的 findBy 方法合并为一个泛型方法,从而简化查询接口的设计。
public class CrudRepository<E, I, Q> {
List<E> findBy(Q query);
//...
}
提出查询对象这一概念,并利用它来解决动态查询问题,正是 DoytoQuery 的核心功能。
1 beginor 3 月 5 日建议可以参考一下 NHibernate 或者 EntityFramework 的 Linq 查询的实现,C#这边 Linq 已经快 20 年了,不知道为什么 Java 这边一直没有类似的东西出现,对 Java 不熟悉,不好评价。 |
6 beginor 3 月 6 日 via Android@beginor linq 其实不依赖编译器编,因为 linq 表达式树不需要编译,是动态解释表达式树,转换为对应的 SQL 语句,依赖的是类型 |
7 f0rb 3 月 7 日```c# JPA 的这些方法就只是普通的接口方法,解析方法名称用的是反射技术,不是编译器技术。 |
8 beginor 3 月 7 日@f0rb 这种写法实际上会被转译成 lambda 表达式树,在实际开发中,往往大家更喜欢这么写: ```c# 甚至还可以动态拼接: ```c# 再高级一点儿,还可以运行时根据需要生成表达式树,添加数据库特定的扩展函数。 这些表达式不会被编译, 编译器只做类型检查,运行时根据参数动态转换成对应的 sql 语句。 Java (Hibernate/QueryDSL)就支持类似这样的查询 ```java 但是使用体验和 c#相比真的很差。 除了 C#,确实极少有主流通用编程语言会为了数据库查询而直接修改语言本身的编译器,把 from, where, select 变成一级关键字,但是正因为这样,C# 是这种“伪 SQL 体验”做得最极致的一个。 |
9 f0rb 3 月 7 日@beginor 所以 linq 还是需要`if`语句来构造动态查询。 麻烦您评论前先了解一下别人的方案和区别好吗? |
10 beginor 3 月 7 日 via Android这些是编程语言的特性,还有逻辑算符,开发者已经掌握,只要做好映射,就可以直接使用。 如果一个 ORM 需要再学习并记住 le lt ge 等晦涩的方言表才能生成 SQL ,推广起来只会更难。 |
12 f0rb 3 月 8 日@freefcw Specification 接口是先根据查询参数构建 criteria ,再根据 criteria 构建查询语句。如果把 Specification 用到的参数聚合到一个对象中,就是先根据这个对象构建 criteria ,再构建查询语句。然后我就发现构建的查询语句跟这个查询对象直接相关,没必要依赖 criteria 多绕一圈。 https://github.com/f0rb/java-orm-comparison 这个仓库有跟 Specification 的写法做对比的,用 Specification 还是需要写大量 if 语句。用 DoytoQuery 则能根据查询对象自动构建动态查询语句,一行方法都不用写。 |
14 beginor 3 月 8 日 via Android如果能再进一步,将 le lt ge 这些改为使用语开发内置的 '>=' '>' '<=' 逻辑算符来生成对应的查询语句(也就是构建并解析λ表达式树),不仅学习起来更加容易,使用上也更加流畅,对于现有的 Java ORM 来说,才是实质的进步,否则大家还是会倾向选择大众化的 MyBatis 或者 JPA 。 |
15 f0rb 3 月 8 日@beginor 有一点我想你没有理解清楚,就是引入查询对象后就可以实现查询语句的自动化构建。比如下面这个类,你看眼字段名就知道每个字段对应的查询条件是什么,完全不再需要去编写方法,用'>=' '>' '<=' 这些逻辑运算符去构建查询语句。这也是为什么需要把逻辑运算符转换成谓词后缀的原因。 public class BookQuery 你在 C#里把这个对象转换成 WHERE 语句恐怕连 300 行代码都用不了。 |
16 beginor 3 月 8 日就个人来说,如果要先记住 Gt Lt 这些方言表,还不如用 mybatis 写 xml+sql 简单吧; 强依赖属性名称+谓词查询不是人人都能接受, 万一属性名称拼或谓词错了一个字母呢? 到 C#这边来说,也就是这几行代码,而且编译器会做类型检查,提前发现并排除不必要的错误: ```c# if (author != null) { var books = query.ToList(); |
17 f0rb 3 月 8 日@beginor 所以你有没有想过我这里为什么会拿 SpringDataJpa 的 findBy 方法作为切入点呢? mybatis 的 xml 里用 if 拼 SQL 会比定义几个字段更简单吗?那为什么都去用 mybatis-plus 了? 还有你这段 C#代码,你没发现用我前面写的 BookQuery 类可以生成一样涵义的代码吗? 你还是理解不了我说的自动化构建动态查询是什么意思吗? |
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。