V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
f0rb
V2EX  ›  Java

DoytoQuery vs SpringDataJPA

  •  1
     
  •   f0rb · 2 天前 · 775 次点击

    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 的核心功能。

    13 条回复    2026-03-08 00:59:47 +08:00
    beginor
        1
    beginor  
       2 天前
    建议可以参考一下 NHibernate 或者 EntityFramework 的 Linq 查询的实现,C#这边 Linq 已经快 20 年了,不知道为什么 Java 这边一直没有类似的东西出现,对 Java 不熟悉,不好评价。
    f0rb
        2
    f0rb  
    OP
       2 天前
    @beginor Linq 这种需要编译期支持的就算了吧,Linq 真这么好咋没有其他任何编程语言跟进呢?
    shuangbiaog
        3
    shuangbiaog  
       2 天前
    类似 mybatis-plus 的条件构造器?
    f0rb
        4
    f0rb  
    OP
       2 天前
    @shuangbiaog 纯依赖查询对象实现自动化构建,一行方法都不用写,这里有个对比仓库:
    https://github.com/f0rb/java-orm-comparison
    beginor
        5
    beginor  
       1 天前 via Android
    @f0rb 那 jpa 的这些方法是不是也依赖编译器呢?
    beginor
        6
    beginor  
       1 天前 via Android
    @beginor linq 其实不依赖编译器编,因为 linq 表达式树不需要编译,是动态解释表达式树,转换为对应的 SQL 语句,依赖的是类型
    f0rb
        7
    f0rb  
    OP
       17 小时 17 分钟前
    @beginor

    ```c#
    var query = from s in dbContext.Students
    where s.Age >= 18
    select s;
    ```
    你说的是这种么?有第 2 个把 SQL 里的关键字直接拿来当编程语言的关键字的么?这种放其他语言直接编译出错。

    JPA 的这些方法就只是普通的接口方法,解析方法名称用的是反射技术,不是编译器技术。
    beginor
        8
    beginor  
       16 小时 15 分钟前
    @f0rb 这种写法实际上会被转译成 lambda 表达式树,在实际开发中,往往大家更喜欢这么写:

    ```c#
    var query = dbContext.Students.Where(s => s.Age >= 18);
    var data = query.ToList();
    ```

    甚至还可以动态拼接:

    ```c#
    var age = //
    var query = dbContext.Students;
    if (age > 0) {
    query = query.Where(s => a.Age >= age);
    }
    var data = query.ToList()
    ```

    再高级一点儿,还可以运行时根据需要生成表达式树,添加数据库特定的扩展函数。

    这些表达式不会被编译, 编译器只做类型检查,运行时根据参数动态转换成对应的 sql 语句。

    Java (Hibernate/QueryDSL)就支持类似这样的查询

    ```java
    query.select(s).where(s.age.gt(18))
    ```

    但是使用体验和 c#相比真的很差。

    除了 C#,确实极少有主流通用编程语言会为了数据库查询而直接修改语言本身的编译器,把 from, where, select 变成一级关键字,但是正因为这样,C# 是这种“伪 SQL 体验”做得最极致的一个。
    f0rb
        9
    f0rb  
    OP
       11 小时 34 分钟前
    @beginor 所以 linq 还是需要`if`语句来构造动态查询。

    麻烦您评论前先了解一下别人的方案和区别好吗?
    引入查询对象后是不需要显示编写`if`语句来构造动态查询的。
    查询对象里哪个字段有赋值就把哪个字段的查询条件添加到查询语句里。
    这才是 DoytoQuery 和传统 ORM 的根本性区别。
    beginor
        10
    beginor  
       9 小时 52 分钟前 via Android
    这些是编程语言的特性,还有逻辑算符,开发者已经掌握,只要做好映射,就可以直接使用。

    如果一个 ORM 需要再学习并记住 le lt ge 等晦涩的方言表才能生成 SQL ,推广起来只会更难。
    freefcw
        11
    freefcw  
       5 小时 0 分钟前
    所以 https://docs.spring.io/spring-data/jpa/reference/jpa/specifications.html 解决的是什么问题呢?

    我觉得 jpa 这个命名只适合简单的,更多的还是要考虑 specifications
    f0rb
        12
    f0rb  
    OP
       3 小时 56 分钟前
    @freefcw Specification 接口是先根据查询参数构建 criteria ,再根据 criteria 构建查询语句。如果把 Specification 用到的参数聚合到一个对象中,就是先根据这个对象构建 criteria ,再构建查询语句。然后我就发现构建的查询语句跟这个查询对象直接相关,没必要依赖 criteria 多绕一圈。

    https://github.com/f0rb/java-orm-comparison 这个仓库有跟 Specification 的写法做对比的,用 Specification 还是需要写大量 if 语句。用 DoytoQuery 则能根据查询对象自动构建动态查询语句,一行方法都不用写。
    f0rb
        13
    f0rb  
    OP
       3 小时 49 分钟前
    @beginor 我知道你的意思。但是如果只学 le lt ge 这些后缀,不光能生成 SQL 语句,还能生成 MongoDB 以及其他 NoSQL 数据库的查询语句,是不是就能体现它的价值了。

    https://github.com/doytowin/doyto-query-mongodb 这是支持 MongoDB 的 Java 仓库。
    https://github.com/doytowin/goooqo 这是一个 Go 语言版本的实现,同时支持 SQL 和 MongoDB 。
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   875 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 20:49 · PVG 04:49 · LAX 12:49 · JFK 15:49
    ♥ Do have faith in what you're doing.