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

Spring JPA 动态列查询有什么好的思路

  •  
  •   leiuu · 2022-06-06 15:28:35 +08:00 · 3309 次点击
    这是一个创建于 906 天前的主题,其中的信息可能已经有所发展或是发生改变。

    例如数据库表可能存放有 30 列,本身是列式存储。

    实际的查询场景可能每次查询 5 列,且组合可能不一样。

    如果用 jpa projections 可能面临需要定义一堆 entiny ,组合数爆炸。

    30 条回复    2022-07-22 21:19:41 +08:00
    bz5314520
        1
    bz5314520  
       2022-06-06 15:35:53 +08:00
    楼主是想说,会定义太多的 dto?
    leiuu
        2
    leiuu  
    OP
       2022-06-06 15:38:51 +08:00
    @bz5314520 对的 动态列每一个组合可能都需要一个 dto 不太优雅 😂
    Vaspike
        3
    Vaspike  
       2022-06-06 15:50:53 +08:00
    @leiuu #2
    DTO 处:返回的数据字段换成 Map 类型会不会好些
    JPA 处: 我还真忘了 JPA 返回 Map 类型是不是一定要写 native sql ,OP 自己评估下吧
    Tenlearn
        4
    Tenlearn  
       2022-06-06 15:51:11 +08:00
    这和 jpa 有什么关系,映射到对象不就这样吗
    iosyyy
        5
    iosyyy  
       2022-06-06 15:51:22 +08:00
    用 QueryDsl 试试
    sujin190
        6
    sujin190  
       2022-06-06 15:54:44 +08:00   ❤️ 3
    @Query("select new com.demo.ArticleQueryDto(a.id, a.viewCount) from t_article a")

    Query 声明的时候 select 用 new 啊,然后写不同的参数的构造函数就是了呗,转换 sql 语句的时候似乎只会 select 你访问的属性字段,似乎不会再 select 所有字段了
    suman
        7
    suman  
       2022-06-06 16:08:27 +08:00
    JPA Specification
    EastLord
        8
    EastLord  
       2022-06-06 16:29:22 +08:00
    querydsl 如何
    bz5314520
        9
    bz5314520  
       2022-06-06 16:37:56 +08:00
    @leiuu 我没觉得不优雅,你这样查询估计也是一个接口对应一个前端视图,静态类型检查反而方便调试。这种生成 dto 的体力活,用 IDEA 的插件 JPA BUDDY 生成就行。
    leiuu
        10
    leiuu  
    OP
       2022-06-06 17:11:51 +08:00
    @Vaspike 这是个方法 但估计得用 createQuery 写 nativeSql

    @Tenlearn

    @iosyyy QueryDsl 很强大 👍 用 DSL 可以实现

    @suman 可以解决动态 where 但 select column 不行的

    @sujin190 哈哈 666 的操作可行 !

    @bz5314520 理解了。这也是一个法子哈。静态定义理解代价小。但总觉得需要维护很多类, 如果有更新会比较麻烦。
    leiuu
        11
    leiuu  
    OP
       2022-06-06 17:13:45 +08:00
    @EastLord 可行! Dsl 可以搞定。 哈哈,其实想知道「更 jpa 」的写法。目前看 new object 不错。
    sky857412
        12
    sky857412  
       2022-06-06 17:16:34 +08:00
    hakr
        13
    hakr  
       2022-06-06 17:23:32 +08:00
    PopRain
        14
    PopRain  
       2022-06-06 17:28:56 +08:00
    这好像和我之前的问题类似:
    https://www.v2ex.com/t/843880#reply10
    nothingistrue
        15
    nothingistrue  
       2022-06-06 17:31:11 +08:00   ❤️ 1
    你这个返回的列是动态的,这就算到 SQL 那里也不是动态查询,你每次动态选择的列,对 SQL 执行屁影响都没有。WHERE 条件每次都是随机生成的,这才是 ORM 或者 SQL 查询上的动态查询。

    请先把你的动态查询给分成两个过程。首先,WHERE 条件动态是一个过程,用 JPA 的 Specification 。然后,返回列的动态是一个过程,这个简单的很,你不论如何都查询出来一个 Entity ( JPA 的要求也必须是一个 Entity 对应一套表),然后在将这个 Entity ,根据接口返回的需要,返回不同的 DTO/VO ,或者仅返回指定字段的 Map (看你的场景,返回哪些列是用得时候才知道的,这样返回 MAP 更合适)。
    Seney
        16
    Seney  
       2022-06-06 17:33:40 +08:00
    除了上面 dsl/jpql, 还可以用投射,另外定义个 interface 相当于 dto 来接受结果集
    leiuu
        17
    leiuu  
    OP
       2022-06-06 17:59:55 +08:00
    @nothingistrue 对理解有帮助 👍 这里之所以说是动态,因为默认是全部列返回,如果后端是列存数据库性能就会比较差
    @Seney 你和楼上头像好像。dto 的方法可以👍,但可能一个 entity 我的场景中可以衍生出 15 个 dto 。他们只有个别字段不一样。
    leiuu
        18
    leiuu  
    OP
       2022-06-06 18:03:46 +08:00
    @sky857412 Nope !

    @hakr CriteriaQuery 可以实现👏


    @PopRain 场景很相似!
    duyaofei
        19
    duyaofei  
       2022-06-07 07:38:54 +08:00
    感觉 jdbctemplate 更适合啊,这场景
    zed1018
        20
    zed1018  
       2022-06-07 08:56:04 +08:00
    我觉得你这个其实更应该是 graphQL ?
    KingOfUSA
        21
    KingOfUSA  
       2022-06-07 09:34:33 +08:00
    @leiuu 用下我的这个库吧 ( https://github.com/ksprider/Surgical ),可以解决一个 entity 对应 n 多种组合的 dto 的场景。
    KevinBlandy
        22
    KevinBlandy  
       2022-06-07 14:53:27 +08:00   ❤️ 1
    Spring Data JPA + QueryDsl 。强烈推荐。投影查询,对象封装,JOIN 检索,UPDATE 部分列。要多舒服有多舒服。

    我写过一个 QueryDsl 的案例,你可以看看。

    [https://springboot.io/t/topic/4424]( https://springboot.io/t/topic/4424)
    leiuu
        23
    leiuu  
    OP
       2022-06-07 16:27:11 +08:00
    @KevinBlandy 手动赞一下 querydsl 很强大 用了 querydsl 是不是一般都不需要 dao 层了 service 层似乎直接就完成工作了
    KevinBlandy
        24
    KevinBlandy  
       2022-06-07 17:45:06 +08:00
    @leiuu 是的。抽象一下,直接在 Controller 就可以通过 Lambda 操作数据库。很方便,不需要写 Service ,Mapper ,xml 以及一堆堆的映射。
    RookieRicardo
        25
    RookieRicardo  
       2022-06-07 17:50:11 +08:00
    @KevinBlandy

    @leiuu

    dao 层是为了分层才设计出来的,真正的业务,哪有一条 CRUD 就结束的。
    KevinBlandy
        26
    KevinBlandy  
       2022-06-07 17:55:05 +08:00
    @RookieRicardo 看情况呗,我的业务只是为了更新一个字段。我没必要写过 Service ,写个 Dao 。

    ```java
    public void handler (){
    this.service.apply(query -> {
    return query.update(user).set(user.enabled, false).where(user.id.eq(1)).exec();
    })
    }
    ```
    RookieRicardo
        27
    RookieRicardo  
       2022-06-07 17:58:11 +08:00
    @KevinBlandy 如果没有编程规范的话 怎么样都行
    leiuu
        28
    leiuu  
    OP
       2022-06-07 18:59:29 +08:00
    @RookieRicardo 同意 需要依据团队规范,但这里不知道有没有好的实践方式。

    QueryDsl 很灵活,service 涉及多个 entiny 也可以支持 。可能很多需求在 service 层写 dsl 就实现了。所以会纠结是否需要独立抽象 dao 层。

    ```java
    QPerson person = QPerson.person;
    List<Person> persons = query.from(person)
    .where(person.firstname.eq(firstname))
    .orderBy(person.surname.desc())
    .list(person);

    ...
    ```
    kongkongye
        29
    kongkongye  
       2022-07-22 19:15:48 +08:00
    @nothingistrue 有个问题,如果返回 map 的话是不对 swagger 之类的不太友好,前端单看 swagger 接口说明不知道会返回啥字段,不过好像也没啥好办法
    nothingistrue
        30
    nothingistrue  
       2022-07-22 21:19:41 +08:00
    严格意义上来说,swagger 的接口定义是要自己写 yaml ,跟你后端定义的对象无关的。能够自动根据 Java 类生成 Swagger 接口定义的,是 Springfox-swagger 插件。而这个插件,是允许你通过注解的方式用自己的定义覆盖默认定于一的,所以及时使用 Map 仍然能够不影响 swagger 这类的接口定义,只是需要多做一些注解 /注释性的工作。

    当然,用 Map 不影响,不是鼓励 Map ,首选仍然是特定的类。楼主这里推荐用 Map ,是因为它返回内容当中的字段是不固定的,有可能还是前端要啥后端返回啥,这时候没法定义特定的类了(或者说定义的类就用一次,太浪费),用 Map + 注释 /注解会更省事。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3152 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 14:00 · PVG 22:00 · LAX 06:00 · JFK 09:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.