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

Java 都这么多年过去了,生产级别数据库操作库除了 JPA 和 Mybatis 还有什么? JPA 和 mybatis-plus 比优势在什么地方?

  •  
  •   pigbug · 130 天前 · 5396 次点击
    这是一个创建于 130 天前的主题,其中的信息可能已经有所发展或是发生改变。
    70 条回复    2021-08-05 11:35:04 +08:00
    Leviathann
        1
    Leviathann  
       130 天前
    太多了
    jooq
    jdbi
    querydsl

    kotlin 可以使用所有 java 生态然后还有官方的 exposed
    sagaxu
        2
    sagaxu  
       130 天前 via Android
    试试 jooq
    br00k
        3
    br00k  
       130 天前
    我挺喜欢 querydsl,SQL 、MongoDB 都可以支持。
    Cbdy
        5
    Cbdy  
       130 天前 via Android
    当然是 JDBCTemplate 啦,再精妙的 ORM,都不如直接写 SQL 清晰利落
    Leviathann
        6
    Leviathann  
       130 天前 via iPhone
    @Cbdy 根据查询条件拼接 sql,要修改重构的话这种纯字符串的方案巨恶心
    jones
        7
    jones  
       130 天前   ❤️ 2
    jdbctemplate, 其他都是浮云, 代码就应该是代码的样子, jpa 会陷入 Annotation 地狱, mybatis 会陷入 XML 地狱
    Cbdy
        8
    Cbdy  
       130 天前 via Android
    @Leviathann 不同的方案有不同的写法、劣势、优势,直接写字符串明了,虽然动态性差,但是在要动态性的场景可以通过多拆几个方法,或者换动态的 SQL Builder 解决

    你这是要求一个完美的方法解决所有问题吗?很可惜软件开发没有银弹
    chendy
        9
    chendy  
       130 天前
    jdbctemplate 遇到条件不定的 sql 就很头大了
    alamaya
        10
    alamaya  
       130 天前   ❤️ 14
    居然有吹直接写 sql 的,这是返祖现象吗?
    notejava
        11
    notejava  
       130 天前   ❤️ 1
    习惯了使用 mybatis-plus,兼顾了单表操作和手写 sql 。
    limbo0
        12
    limbo0  
       130 天前   ❤️ 1
    mybatis-plus + 1 尽量不手写 sql, 维护太麻烦
    bitmin
        13
    bitmin  
       130 天前 via Android
    jdbctemplate 动态 sql 我的解决方案是先写完整 sql,自己写一个工具类检查入参空参数并删除 sql 中相应的语句。

    在 idea 中 sql 都有代码提示,也可以格式化显示,代码中用多行字符串展示看起来很清晰。
    abcbuzhiming
        14
    abcbuzhiming  
       130 天前   ❤️ 48
    这本质是两种思想的碰撞,JPA 的思维是 ORM,mybatis 则来自“sqlUtils”(sql 帮助工具)。

    在关系数据库群雄争霸的那个时代,SQL 标准没有统一,在编程界的当时有一个迫切的需求:如果我半途要换关系数据库怎么办?不像现在这个 MySQL“称霸世界”(并没有)的时代,当时做到一半换数据库是真的常见的需求。而当时 SQL 作为一种 4GL 语言其实思想并没有广泛的被人接受,当时的业界还是面向对象的天下。于是就有人说:为什么不能用面向对象来映射关系数据库的表呢?这就是 ORM(Object Relational Mapping,对象关系映射)最初的思想来源。也是最主要的两个 ORM 库,Hibernate 和.Net Entity Framework 的指导思想。

    时间飞速前进,新世纪后发生了两个非常主要的变化:

    第 1 个变化是,关系数据库面对海量的互联网数据开始变的力不从心,为了解决海量数据的问题(当时大数据还没有像现在这么发达),不得不妥协,把单库拆分成多库,这么一拆之后,关系数据库之前以强约束,关联关系计算为卖点的特性,不再是优势(拆分之后的库无法使用这些特性,只能搞什么最终一致,Base 理论之类的聊以自慰),这也是为啥 MySQL 这种在经典关系数据库理论看上去简直就是个残废的关系数据库大行其道的原因。同时,因为经典关系数据库的特性不再重要,关系数据库开始沦为数据仓库,选哪家的数据库就变的不再重要了,更换数据库变成了伪需求,不再存在,ORM 理论的重要来源:屏蔽各个关系数据库之间的不同点,以让用户更换数据库无忧,这一最初的重要需求,不再存在。


    第 2 个变化是,进入新世纪后,SQL 作为一个 4GL 语言的价值终于被发现,大家纷纷表示这种“do not tell me how to do,tell me what you want(不要告诉我怎么干,告诉我你要什么)”的模式真是太香了;而且一众 NoSQL 的鼓吹者们高举大旗准备驱逐关系数据库,结果闹了很久后发现自己拿 SQL 居然毫无办法,只能捏着鼻子,纷纷又开始在自己的查询器上加上了 SQL 支持。传统关系数据库是没落了,但是 SQL 以及背后承载的关系运算的思维,在新时代反而是大行其道。复杂查询直接上 SQL 那感觉是真香,连新普的大数据从业者都自嘲自己说:后端是 CRUD Boy,我们就是 SQL Boy 。


    总之,新世纪的竞争里,目前看是 SQL 赢了,ORM 的思维只比较适合无连表的 CRUD,对单表或多个单表执行 OLTP 业务,此时的 ORM 是比你写多个 insert,select 啥的方便的,但是一旦涉及到复杂的联表,group,having 查询,大家否纷纷直接上 SQL 更方便。

    另外从业界的进化你也可以看得出来,纯正的 ORM,在 Hibernate 和.Net Entity Framework 之后几乎就没有再看到出名的了,JPA 则是从 Hibernate 发展来的,可以看做延续和强化,但本质不会变。但是各种"SQL 工具类"型的查询库,还在层出不穷的出,而且这些工具并非墨守成规,Ruby on Rails 开历史先河,吸收 ORM 对单表查询很友好这个优势,发展出了 Active Record 这种即可以从单表结构直接映射出对象,并可以在查询的时候以链式调用的方式注入查询条件;同时,都保留了能够方便自定义 SQL 的能力。其它 Sql 工具一看纷纷跟进,包括现在 Mybatis 的几个强化工具都是这个思路。

    所以我觉得接下来一段比较长的时间,除非出现新的变革,占优势的应该还是各种更亲和 Sql,但是同时具备表映射对象能力的各种 SQL 帮助工具。
    echo1937
        15
    echo1937  
       130 天前
    Spring Data 是 Spring 提供的一个操作数据的框架,旨在统一和简化对各类型持久化存储,
    而 Spring Data JPA 只是 Spring Data 框架下的一个基于 JPA 标准操作数据的模块。
    它的优势是 RDBMS 、Redis 、MongoDB 、ES 、Cassandra 、Solr 、GemFire 等等都可以用同一套的 API 去访问,这点是 mybatis-plus 做不到的。
    缺点是国内用的少(国外 99%都是这个),有一定学习门槛,注解比较多,没有 JDK text block 的话,写原生查询 SQL 看着不美观。

    如果你业务离不开 mybaits,mybatis-plus 确实非常好用。
    yema50
        16
    yema50  
       130 天前
    @limbo0 最近也在尝试用 mybatis-plus, 请问一般查数据的时候,假如有一两个条件参数, 你们一般是在 Mapper 里定义好方法然后 service 里直接调用, 还是直接在 service 里构造 QueryWrapper 查询呢
    abcbuzhiming
        17
    abcbuzhiming  
       130 天前
    @yema50 单表查询请一律不要自己定义 mapper 和 service,直接用表反向映射生成实体,mapper,service,然后你构建 QueryWrapper 注入查询条件,用 mapper 还是用 service 就随你了。

    这类工具的设计目的就是为了解决纯 SQL 工具在单表查询时不如 ORM 方便的。如果你单表查询还要去自己定义 mapper 和 service,那等于这个工具没有起作用
    yema50
        18
    yema50  
       130 天前
    @abcbuzhiming 感谢回复,可能我没有表述清楚, 我举个例子。现在有个 User 实体,对应的 UserMapper, UserService,现在有个需求要根据 username 查询,我想到的实现方式有 1. UserMapper 里定义一个方法 findUserByName(@Param("name") String name), 然后上面加上对应的注解和 SQL,再在 userService 里直接调用; 2. 在 userService 里需要查询的地方构造 QueryWrapper<User> query....., query.eq("name", "name"), 然后再查询。

    我的困惑是:1. 如果使用第二种方式,可能会有多处需要用到类似查询的地方, 那就是代码里会有多处构造相同 QueryWrapper 的代码; 2.如果使用第一种方式, 可能有时查询条件会增减,比如某处需要再加上 age 作为条件,这样又感觉不够动态

    请大家指教
    AlkTTT
        19
    AlkTTT  
       130 天前   ❤️ 2
    @alamaya 号是 14 年的,年龄不小了。难以接受新的技术,最后终究会被淘汰,可能几年后我们也会这样
    Kipp
        20
    Kipp  
       130 天前
    @yema50 #18 之前也思考过这个问题,觉得还是放在 mapper 里比较好,不然 QueryWrapper 到处飞,之前的 QueryWrapper 也没法复用
    BBCCBB
        21
    BBCCBB  
       130 天前
    @yema50 Wrapper 统一放到响应 Service 里. 不要用到的地方到处写..
    Rwing
        22
    Rwing  
       130 天前
    欢迎各位大佬来 C# 体验一下 entity framework,看看什么是真正的 ORM
    abcbuzhiming
        23
    abcbuzhiming  
       130 天前   ❤️ 3
    @yema50
    方式 1 就是传统的 mybatis 方法,你要用方式 1,没必要用 mybatis-plus

    方式 2 本身就是脱胎于 ActiveRecord 的链式调用,这个方式最初的来源就是我上面说的 Ruby on Rails,它提供了高度的灵活性,代价的就是代码看上去像事务脚本,但是我认为这个在开发效率,这是值得的。

    最后,后端的项目,在经历过几轮微服务拆分后,业务是高度内聚的,你几乎很难遇到多处复用你这个查询代码的情况,我认为初期就考虑复用,这个想法是有问题的
    Cbdy
        24
    Cbdy  
       130 天前
    abcbuzhiming
        25
    abcbuzhiming  
       130 天前   ❤️ 3
    @Kipp
    @BBCCBB
    我明确的反对这个想法,QueryWrapper 本质就是 ActiveRecord,把它直接放在最靠近业务的地方才是对的,硬要封装起来啦就又变成了 JPA 的思路,那还不如用 JPA 。

    你要追求灵活性,你就要失去一些结构上的严谨,以及代码感觉看上去像事务脚本

    你要追求严谨性,你的代码就要封装套层,失去灵活性。

    我选择灵活性,ActiveRecord 的诞生就是为了灵活而不是为了严谨性,要严谨的面向对象的话,选 JPA
    pigbug
        26
    pigbug  
    OP
       130 天前   ❤️ 1
    @abcbuzhiming #25 非常赞同
    ikas
        27
    ikas  
       130 天前   ❤️ 1
    1.ORM 到底是什么程度..根据业务.喜好选择,根本不是什么需要争论的问题
    2.如何更好的拼接 SQL[条件]才是最大的问题,不管你用 jpa,spring jdbc,jdbc,mybatis,dsl
    3.xml 只是 mybatis 的一种动态 sql 方式.他本身也支持代码,也支持你自己开发其他动态方式,也支持 sqlbuilder

    目前我自用的....
    @Select("""
    <script>
    select
    count(*)
    from
    pet
    <where>
    <if test='name != null' > and name like #{name}</if>
    </where>
    </script>
    """
    )
    List<Long> querySomeCount(@Param("name") String name);
    ColinZeb
        28
    ColinZeb  
       130 天前
    @abcbuzhiming 不考虑一下 EF Core 的 Linq
    micean
        29
    micean  
       130 天前
    @yema50

    为何不直接在 controller 里构造 QueryWrapper 呢?
    见过太多怪物 service 了
    yema50
        30
    yema50  
       130 天前
    @micean controller 里加业务逻辑感觉有点奇怪吧
    abcbuzhiming
        31
    abcbuzhiming  
       130 天前   ❤️ 1
    @ColinZeb 用过,linq 很好用,但是还是那句话,复杂查询最后你还是会回到 SQL 上来的


    @yema50 没啥怪的,看你选严谨还是选灵活,我自己的项目就经常在 Controller 层加业务
    potatowish
        32
    potatowish  
       130 天前 via iPhone
    jdbctemplate 就算了吧,最好的方案就是单表操作用 mbp 、tk,多表操作就在 mybatis xml 中写原生的 sql,无脑吹这个那个的都是老顽固
    ccppgo
        33
    ccppgo  
       130 天前   ❤️ 1
    @yema50 第一种方式没有任何意义
    wanguorui123
        34
    wanguorui123  
       130 天前 via iPhone
    C#的 EntityFramework 、FreeSql,才是真正的 OOP
    paouke
        35
    paouke  
       130 天前 via iPhone
    @yema50 我一般会吧 wrapper 查询的部分封到一个 localservice 里面
    yema50
        36
    yema50  
       130 天前
    @paouke 然后在需要使用的地方注入 localservice 并调用 localservice 封装的方法吗
    hutoer
        37
    hutoer  
       130 天前
    @abcbuzhiming Controller 层加业务?不考虑业务代码复用?
    EKkoGG
        38
    EKkoGG  
       130 天前
    @Rwing #22 真的!最近在学习 mybatis-plus,要被气哭了,EF Core+ LINQ 可以称为绝杀
    ColinLi
        39
    ColinLi  
       130 天前
    简单查询用 jpa,复杂查询用 jdbctemplate+sqlbuilder
    ebony0319
        40
    ebony0319  
       130 天前
    ORM 的方向是对的,但是 jpa 在稍微复杂一点的场景,关联表的时候还是得写原生 sql,革命尚未成功,仍需努力.
    abcbuzhiming
        41
    abcbuzhiming  
       130 天前   ❤️ 1
    @hutoer 为什么要在初期就考虑代码复用?为啥不能遇到能复用的时候,再去把代码抽成 service ?为啥一定在初期搞这种过度设计?
    abcbuzhiming
        42
    abcbuzhiming  
       130 天前   ❤️ 1
    @ebony0319 ORM 的方向如果是对的,它就不会遇到复杂场景必须回到 SQL 这种尴尬的问题。本质是对象模型并不能如当初的人们估算的那样取代关系模型,只要关系模型自身不倒,ORM 就始终处于尴尬的位置
    alamaya
        43
    alamaya  
       130 天前
    @abcbuzhiming 可怕,连考虑代码复用都成了过度设计,好同情接手你的屎山代码的同事
    abcbuzhiming
        44
    abcbuzhiming  
       130 天前   ❤️ 3
    @alamaya
    问题是你到底是怎么考虑代码复用的?

    哦,看到一段操作数据库的代码,就不能放在 Controller 里?就得写个 service 装起来,搞不好这个 service 还得先是一个接口,然后做出一个 Impl 实现类来?是这个意思吗?

    考虑复用难道不是先考虑一下有没有复用的可能性吗?当根本就没有复用的可能性时,就像我前面说的,业务代码几轮拆分后都是高内聚的,哪有让你复用的机会?也要“复用”吗?

    如果你一开始就已经想到了这段代码在业务上就是需要复用的,甚至这个需求就在眼前,那我万分支持你把立刻把这段代码抽出来变成一个 service,给人复用。如果你一开始根本想不出来要在哪里复用,那为什么要复用?这不是过度设计又是什么呢?就像很多项目写一堆直到项目死掉都不会改成实现的接口,美其名曰 [规范] 。。。

    退一万步讲,你到了遇到有需要复用的那一天,再把 Controller 里的代码抽出来变成一个复用的 service,这很费事吗?

    我见了太多的人,都是嘴上空谈说 [复用] ,我直接问他们关于眼前项目,你觉得这个位置的代码在哪里可能会复用?鲜有答上来的,他们大部分也估不到项目在发展中会遇到什么。只不过是看别人是这么干的,或者说按所谓的规范应该这么干的。。。

    除非你已经想到需要的地方,那么再动手,不要过早动手,优先完成业务。这是我的法则。就目前的实践经验看,和我配合的人都挺喜欢的,没觉得我的代码就是屎山。当然有人不认同我能理解,毕竟张口闭口设计规范的人非常之多,谢欢在项目里写一堆到死也不会发生变化的接口的人更不少
    paouke
        45
    paouke  
       130 天前 via iPhone
    @yema50 是的
    Kamiyu0087
        46
    Kamiyu0087  
       130 天前
    进来想说 Exposed
    仔细一想 Exposed 是 Kotlin 的
    testFor
        47
    testFor  
       130 天前
    有个疑问,jpa 这种的 orm,不是对于领域设计会更加友好么,不是更能解决复杂业务么
    fpure
        48
    fpure  
       129 天前
    上个项目用 JPA,想撞墙,因为 JPA 匮乏的表达能力做了很多骚操作,现在代码也已经腐烂的不成样子了
    SawyerGuo
        49
    SawyerGuo  
       129 天前
    哎,我也听说 mybatis-plus 好用,但就是懒得学。
    fpure
        50
    fpure  
       129 天前
    @abcbuzhiming 很多人就是这样,过度设计,滥用 DRY 原则和开闭原则
    witcherhope
        51
    witcherhope  
       129 天前 via iPhone
    @testFor 领域驱动设计跟 ORM 没有任何必然联系,统一语言做的就是屏蔽技术细节,抽象业务本身。
    potatowish
        52
    potatowish  
       129 天前 via iPhone
    @SawyerGuo 没有学习成本,照着文档撸就好了
    Lemeng
        53
    Lemeng  
       129 天前
    哎,感觉自己人变懒了
    KevinBlandy
        54
    KevinBlandy  
       129 天前
    spring-data-jpa + querydsl 永远滴神。
    shakoon
        55
    shakoon  
       129 天前 via Android
    脱离业务场景谈技术都是空谈。——一个写了十几年存储过程的程序员
    crclz
        56
    crclz  
       129 天前   ❤️ 4
    JPA for write/transaction
    SQL for query

    =================

    “来自”微软官方的答案。


    ——引号是因为微软在.net 世界给出了一个类似的解决方案。
    https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/cqrs-microservice-reads

    查询( read )用的是 micro orm Dapper,命令( write )用的是宇宙最强 orm Entity Framework 。

    虽然 ef 的表达能力几乎覆盖 sql,但是微软却告诉你:没必要全程用 ef 哦!

    关键词:cqrs
    fkdog
        57
    fkdog  
       129 天前
    我觉得 mybatis-PLUS 的 api 设计的一言难尽。一种查询方法愣是被弄出 N 个 api 。。
    其实大部分人对 orm 框架的需求很简单,支持泛型 crud 模板方法,支持 orm 就足以,一个简单的 DBUtils 工具类就能实现。

    现在国内的互联网项目都普遍喜欢在代码层面 join 而不是交给数据库。类似管理后台这种经常需要复杂条件组合查询和多表 join 的,一般都丢交给 es 或者数仓了。
    yizmaoaa
        58
    yizmaoaa  
       129 天前
    Ebean

    - -哎,主要还是多行字符串,不然谁用 Mybatis
    ameccc
        59
    ameccc  
       129 天前
    SQLDelight 能算是生产级别的吗?自己写玩具用这个写的很开心。
    GiantHard
        60
    GiantHard  
       129 天前
    我最近在从 mybatis-plus 切换到 MyBatis Dynamic SQL,好处如下:

    * 类型安全,通过官方提供的代码生成器,保证数据模型与数据库模型一致,避免字段写漏、类型写错
    * 表达力强,Dynamic 提供的 DSL 语法与 SQL 相比差异不大,完全可以复用自己写 SQL 的经验
    * 足够灵活,不管是简简单单的 INSERT 还是像一篇小作文一样的 WHERE,Dynamic 提供的 DSL 都可以 hold 住
    * 拓展性强,Dynamic SQL 并没有跟 MyBatis 绑定死,默认还支持 JDBCTemplate 跟 JDBC,除此之外,还可以自定义类型安全的数据库函数跟操作符

    最开始我很喜欢 EF Core 这样“正宗”的 ORM,但是现在用过这种类型安全且表达力强的 SQL DSL 之后,我的初心动摇了
    ikas
        61
    ikas  
       129 天前
    话说没人用过 doma 么...支持模板与 dsl
    val list = entityql
    .from(e)
    .innerJoin(d) { eq(e.departmentId, d.departmentId) }
    .where { eq(d.departmentName, "SALES") }
    .associate(e, d) { employee, department ->
    employee.department = department
    department.employeeList += employee
    }
    .fetch()


    @Dao
    public interface EmployeeDao {

    @Sql(
    """
    select * from EMPLOYEE where
    /*%if salary != null*/
    SALARY >= /*salary*/9999
    /*%end*/
    """)
    @Select
    List<Employee> selectBySalary(BigDecimal salary);
    }
    xuanbg
        62
    xuanbg  
       129 天前
    用 mybatis,但绝不用 plus 。简单的 sql 数据库管理工具都能自动生成,复制出来就行。复杂的 sql 除了手写没别的办法。。。
    charlie21
        63
    charlie21  
       129 天前 via iPhone
    靠写面条代码样子的烂代码并入库( merge into main branch in the repo ),
    堆屎山堆得谁也看不懂了 不好接手,
    让自己的不可替代性变得越来越强,
    说的是谁呢 @abcbuzhiming
    你所在的公司当然是对你的依赖性愈发变强的 这没问题 你的价值是被认可的,
    但在我司 如果谁敢在 controller 里作数据库操作,那么第二天他就会被从主程队伍里过滤掉,发配 “边疆” 去写一些边边角角的代码;如果狡辩则直接开除 so 恭喜你没在我司
    james122333
        64
    james122333  
       129 天前 via Android
    个人还是爱 php pdo 那种方式 好写 如果案子大了再封装也方便 兼顾灵活与直觉 参数判断写 xml 判断就神经了 也不可能全代码化 debug 又方便 step 调适很容易找到问题点 至于 service 还是会分的 因为 http 特性 但也就两层而已 剩下都是辅助工具让代码容易维护 orm 特烦没错
    tctc4869
        65
    tctc4869  
       128 天前
    jfinal 的 ar+enjoy
    chocotan
        66
    chocotan  
       128 天前
    公司项目用 mybatis,自己项目用 spring data jpa 和 querydsl
    ccppgo
        67
    ccppgo  
       128 天前
    @james122333 这种就是 jdbcTemplate 吧
    abcbuzhiming
        68
    abcbuzhiming  
       127 天前   ❤️ 1
    @charlie21 我已经非常明确的描述了我的观点,我并不抵触严谨代码结构。我只是强调应该根据不同的的情况选择不同的方式,我非常开心能够不用和你这种永远停留在自己的舒适区的人一起共事,之前也遇到过这样,号称“不按他的规范来就开除”,然后我就看着他有一天就撞到了他的规范无法处理,然后必须妥协的时候,最后此人的选择就是一走了之。。。

    在 Controller 里写数据库处理逻辑非常常见,很早就有技术文章描述过这种“事务脚本编程”方式的优劣,如果你看过别的语言的 mvc 框架,也能看到这样的实现,只是你自己不可接受而已,随便你怎么想,我来这里不是和人吵架的,而是和人交流,我不排斥任何处理方式,只要他能更好的解决问题就行。
    unbright
        69
    unbright  
       126 天前
    jpa 配合 ddd 强无敌
    CyberShadiao
        70
    CyberShadiao  
       125 天前
    @Leviathann 我现在就是你说的这种情况,前端传 sql 条件部分的语句 后端再拼接。代码里面到处都是 sql
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3586 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 09:52 · PVG 17:52 · LAX 01:52 · JFK 04:52
    ♥ Do have faith in what you're doing.