V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
abcbuzhiming
V2EX  ›  Java

Spring Data Jpa 有更好的映射 SQL 语句的方法吗?

  •  
  •   abcbuzhiming · Aug 20, 2018 · 5371 views
    This topic created in 2807 days ago, the information mentioned may be changed or developed.
    最近研究了一下 Spring Data Jpa 的文档。发现它的 ORM 方式似乎只有两种:
    1 Dao 层的接口,接口方法名以驼峰的方式分割成关键词,映射为 SQL 的语句
    2 通过 @query 注解手动编写类 sql 语句

    我觉得这两种方式,灵活度都很一般。假设一个表里有 3 个字段,我分别有三个地方用到了这个表,但是是分别的查询 3 个字段。这在 dao 层里得写 3 个接口。这实在是有点累赘,很多 orm 工具都是直接提供条件字段映射类的,不需要从方法名上去映射,灵活的多,一个方法能搞定任何字段的查询。

    是我打开 spring data jpa 的方式不对还是就只能这样?
    22 replies    2018-08-21 09:56:29 +08:00
    qinxi
        2
    qinxi  
       Aug 20, 2018
    试试 JpaSpecificationExecutor 或者 QueryDSL
    lhx2008
        3
    lhx2008  
       Aug 20, 2018 via Android
    java 写接口语义化不是挺好,如果想不用接口的 orm,可以用 jooq
    InfiniteMirage
        4
    InfiniteMirage  
       Aug 20, 2018
    我同事用 Freemark 类似 MyBatis 的方式去做映射,一般需要很复杂的 SQL 才那样写
    linbiaye
        5
    linbiaye  
       Aug 20, 2018
    老哥,你的这个头像没穿 bra?
    letitbesqzr
        7
    letitbesqzr  
       Aug 20, 2018
    QueryDSL +1 .. 我们引入了 spring data jpa ..但是 所有查询 基础接口 都封装自 QueryDSL,只是某些需要反射来排序 是用的 jpa
    CtrlSpace
        8
    CtrlSpace  
       Aug 20, 2018
    写几个接口而已,不算累赘吧。
    abcbuzhiming
        9
    abcbuzhiming  
    OP
       Aug 20, 2018
    @richard1122 首先谢谢,其次这个实现的还是过于简单。它只能附加字段条件,对注入 limit,group,order 就无能为力,而且无法嵌套,不过你这个提醒了我,我发现我文档看的不仔细,这部分文档上有,但是我看漏了

    @qinxi 感谢,我在文档上找到这两个了,QueryDSL 比较理想。但是 JpaSpecificationExecutor。。。怎么会有搞的如此复杂的实现?它有没有什么特长?比如 QueryDSL 做不了它能做的事情?光看功能他做的事情和 QueryDSL 差不多,但是 QueryDSL 比它直观和简单的多了


    @letitbesqzr 请教有啥是 QueryDSL 实现不了的?现在选型比较关注框架的弱点方面
    abcbuzhiming
        10
    abcbuzhiming  
    OP
       Aug 20, 2018   ❤️ 1
    @CtrlSpace 人的追求是无止境的,你用过 mybatis-plus 就明白了,如果你用过一些动态语言的 ORM 工具比如 ruby 的,你更会明白 java 的这些 ORM 工具局限在哪里
    skypyb
        11
    skypyb  
       Aug 20, 2018
    注入 entitymanager 自个写 SQL 啊...
    我遇到自带的注解解决不了的一般就自己手动写了。写 HQL 还是原生 SQL 都可以自己定
    rim99
        12
    rim99  
       Aug 20, 2018 via Android
    建议看下文档吧,记得有工厂方法可以传入类对象,定制返回类型的
    passerbytiny
        13
    passerbytiny  
       Aug 20, 2018   ❤️ 1
    首先纠正楼主两点:
    一、Hibernate、Jpa 和基于二者的 Spring Data Jpa,都没有“ Dao ”这个概念。
    二、既然用了 Spring Data Jpa,那么除非是是否旧的数据库,不应该考虑表、字段等任何数据库方便的概念。

    其次,楼主第二段的需求,我没看看明白。

    Spring Data Jpa 只是 Hibernate 的一个上层封装,通过约定加自动生成的方式,简化 Jpa/Hibernate 的编码。它本身并不是 ORM,也没有对使用方式做任何强制限制(你可以自由的添加自定义实现)。
    abcbuzhiming
        14
    abcbuzhiming  
    OP
       Aug 20, 2018
    @passerbytiny
    好吧,严谨一点说,spring data jpa 用 repository 来承担了 dao(或者叫 mapper)层的功能

    我没看懂“不应该考虑表、字段等任何数据库方便的概念”这句话,你不从数据出发如何建模呢?或者说你觉得正确的思考方式是什么样的呢?

    我第二段的需求,是希望能追求灵活性,举个例子,User 对象(表),在场景 1 下需要搜索 userName 字段,到场景 B 需要检索 nickName 字段,按照 spring data jpa 的文档,正统的做法你得在 UserRepository 准备两个 find 方法
    findByUserName(String userName)
    findByNickName(String nickName)

    这在我看来是比较麻烦的,因为很多其他 Orm 工具提交这样的做法,以下是伪代码:
    DYSQL dySQL = new DYSQL() //动态 sql 条件查询类生成
    dySQL.eq("列名",列值)..eq("列名",列值).groupy("XXXX").Order("XXX");
    select(dySQL);
    嗯,其实就是上面提到的 QueryDSL
    WispZhan
        15
    WispZhan  
       Aug 20, 2018 via Android
    @abcbuzhiming 看看 ddd。这些概念是 ddd 的
    passerbytiny
        16
    passerbytiny  
       Aug 20, 2018   ❤️ 1
    @abcbuzhiming Repository 不是 Dao,贫血领域模型用 Dao,常规领域模型用 Repository,它俩永远不会一起使用。

    领域驱动设计中的“存储库”的含义是:对象集合以及该集合的读写行为,形象一店的说,就是对象的仓库和仓库管理员。Spring 核心给的 Repository 的定义是:表示“存储库”,若是传统 Java EE 模式(这是好听的说法,说不好停的就是老旧的贫血模型模式),也可以表示 Dao。

    下面是 spring core 中 @Repository 的 javadoc 和谷歌翻译
    https://docs.spring.io/spring/docs/5.0.8.RELEASE/javadoc-api/org/springframework/stereotype/Repository.html

    表示带注释的类是“存储库”,最初由域驱动设计( Evans,2003 )定义为“用于封装模拟对象集合的存储,检索和搜索行为的机制”。
    实现传统 Java EE 模式(如“数据访问对象”)的团队也可以将此构造型应用于 DAO 类,但在此之前应注意理解数据访问对象和 DDD 样式存储库之间的区别。这个注释是一个通用的刻板印象,个别团队可能会缩小其语义并在适当时使用。
    如此注释的类 DataAccessException 在与 a 一起使用时有资格进行 Spring 翻译 PersistenceExceptionTranslationPostProcessor。带注释的类还阐明了它在整个应用程序体系结构中的作用,以用于工具,方面等。
    从 Spring 2.5 开始,这个注释也可以作为一个特化 @Component,允许通过类路径扫描自动检测实现类。
    passerbytiny
        17
    passerbytiny  
       Aug 20, 2018   ❤️ 1
    @abcbuzhiming “不应该考虑表、字段等任何数据库方便的概念”,是领域驱动设计的要求。领域驱动设计中,要求完全按照面向对象的方式去建模,数据库的部分由 Jpa 自动去处理。形象一点的说是:引入了 JPA 的实现( Hibernate 等)后,开发人员只需要设计 Java 类,数据库部分(包括结构和读写 SQL )由 JPA 自动处理。
    passerbytiny
        18
    passerbytiny  
       Aug 20, 2018   ❤️ 1
    @abcbuzhiming 你的这个需求,“场景 1 按照方式一搜索、场景 2 按照方式二搜索”是业务逻辑,不是 Dao/Repository 该干的事。

    在传统模式中,Dao 层提供 findByUserName(String userName) findByNickName(String nickName)两个方法,Service 层决定调用哪个方法。

    在 DDD 模式中,Repository 提供两个检索方法,领域服务或者应用服务决定调用哪个方法。
    passerbytiny
        19
    passerbytiny  
       Aug 20, 2018   ❤️ 4
    补一下 DDD 的软件架构。
    简单分层架构:由下到上依次是:模型层(含 Entity/Aggregation、Value Object、Repository )、Domain Service 层、Application Service 层。Domain Service 层是可选的,并且它仅仅是在调用关系上处于上层,业务关系上它跟模型层是平级的。

    依赖倒置架构:由下到上依次是:领域核心层( Entity/Aggregation、Value Object、Domain Service、Repository 接口部分)、Application Service 层、基础设施层( JPA、Hibernate、数据库、Domain Service 技术实现部分……)。此时的分层只表示业务依赖关系,与调用关系无关了。

    还有更复杂的架构。所有架构中都没有“ Dao ”,数据库也从来不会参与建模。
    em998
        20
    em998  
       Aug 20, 2018
    用 grails gorm
    WispZhan
        21
    WispZhan  
       Aug 20, 2018 via Android
    @passerbytiny 看来老哥也是个 ddd 爱好者
    abcbuzhiming
        22
    abcbuzhiming  
    OP
       Aug 21, 2018
    @passerbytiny
    “在 DDD 模式中,Repository 提供两个检索方法,领域服务或者应用服务决定调用哪个方法。”
    我看完了你的描述,但是我这里的需求是:领域服务希望 Repository 的方法能更灵活一点,因为在我的应用场景里 Repository 对应的是持久层关系数据库的访问,对一张表(对象)只提供一个方法就能完成大部分查询,是件很有用的特性。如果 Repository 必须用多个方法才能应对领域服务的需求,这会增加额外的开发成本
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1305 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 51ms · UTC 23:40 · PVG 07:40 · LAX 16:40 · JFK 19:40
    ♥ Do have faith in what you're doing.