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

为何不选 Hibernate,非要 MyBatis?

  •  2
     
  •   dc2002007 · 329 天前 · 15082 次点击
    这是一个创建于 329 天前的主题,其中的信息可能已经有所发展或是发生改变。

    ###一直不能理解 Hibernate 这么优秀的 orm 思想框架,为什么这么多项目还选择了在 xml 里写 sql 的 MyBatis ,求解惑?

    当然我首先是不能接受在 xml 里写 sql 这种行为的!
    orm 不够优秀吗?
    • 当然我知道,这个帖子一旦发出去,必定是会遭到嘲讽了,这个我已经做好了心理预期了
    第 1 条附言  ·  328 天前
    综合评论来看,我总结一下:国内 java 程序员普遍对面对对象思想理解不深刻,导致 orm 思想很难灵活运用,故行业为了降低开发门槛,刻意选择了 ibatis 方向,这个也是社会培训等多方面造成的问题,jpa 还是主流,这是母庸置疑的,我们需要一个新陈代谢的过程,35 岁可能是这个代谢过程的重要节点。
    第 2 条附言  ·  328 天前
    这个矛盾足够激烈,我今天赚了 3 个银币,目标达成,mybatis 是最好,我支持 mybatis
    第 3 条附言  ·  326 天前
    各位宝宝们,这个帖子我下沉了,你们的讨论意见很鲜明,都是久经实战的好程序员,作为你们口中的 op ,我深感欣慰,v 站需要活力,共同努力~
    130 条回复    2024-06-13 10:46:44 +08:00
    1  2  
    nimble
        101
    nimble  
       328 天前
    国产的 mybatis-plus 使用的方式跟 jpa 和 hibernate 一样,如果你的项目使用的是 mybatis-plus ,jpa 完全没问题,并且支持的功能更多,除了它放弃了恶心的 xml 写 sql 。
    wlm201219
        102
    wlm201219  
       328 天前
    @BiChengfei 我觉得,从这个帖子的讨论和现在 35 岁就业危机来看,国内并不会从 mybatis 转向 jpa
    ZeroDu
        103
    ZeroDu  
       328 天前
    @ZeroDu #100 甚至可以说:你用 Mybatis 这些还需要写 xml 的情况,放在 Hibernate 里面必然也是需要自定义 sql/hql 的
    aristotll
        104
    aristotll  
       328 天前
    用 jooq 逃
    pigspy
        105
    pigspy  
       328 天前
    作为一个 hibernate + jpa 的使用者本来想认真回答下的,不过这 lz 看上去是钓鱼的,真是建议 nuke 了
    izzy27
        106
    izzy27  
       328 天前
    Hibernate 不好念,不利于传播
    jason31415926
        107
    jason31415926  
       328 天前
    @wlm201219 可以聊聊国内计算机教育脱节的问题吗,谢谢
    jeesk
        108
    jeesk  
       328 天前 via Android
    这么说吧, 一个报表连表 5-10 张, 你还会用 orm 吗? 谁不想偷懒直接用 orm ?
    jeesk
        109
    jeesk  
       328 天前 via Android
    现在开源的项目多大多都是 mybatis plus , 各种泛型,还不如 jdbc template 呢,
    jeesk
        110
    jeesk  
       328 天前 via Android
    国外的屎都是香的, 真的是。 国外 orm 用得多,国内用的少, 那么国内就是程序员不行。
    grpc web 就不遵守 rest 规范,grpc web 全是 post. 一大群人说香,google 就是正义, 这风气真是太舔了。
    morgan1freeman
        111
    morgan1freeman  
       328 天前   ❤️ 3
    国情所致吧,国内的项目大多短平快,半年就要出成绩,甚至 2 个月就要出成绩,而且码农本身真的很便宜(相对国内的公司收入而言),一个项目再烂,再狗屎都能请到人来维护,像 JPA 这种 Model First 的框架当然不合适,远没有 mybatis 面向 db 表驱动的开发模式搞的快。

    要是美国 2000 美金就能雇佣工程师去吃屎,资本家怕是笑得合不拢嘴,做梦都能笑醒,软件工程?啥是软件工程? 2000 美金雇个大学生,让你在屎山里面 996 遨游不香么?这比 GPT 高级不知道多少的真人工智能,才 2000 美金在就中国能雇佣到一个入门的。

    讲一个真实的案例,我们系统里面有一个车辆模型,光是 status 状态就好几个, 审核状态,车辆可租售状态,催审状态 等一大堆的状态变量,而这些状态变量本身相互之间还有依赖。

    结果这些变量全部都在各个接口跟 job 里面来维护,本来这种状态变化最好的方式当然是维护一个状态机模式,在模型内部维护车辆状态,不至于代码逻辑失控。

    但是我们的现状就是类似 mybatis 这种,天然就是面向 db 的(虽然 mybatis 有提供一个表针对多个对象的转换服务,但是真的很少看到有人用,毕竟是个半残的 ORM ),几乎不存在逻辑内聚的需要,这个开发在 job 服务里面 query 出来 update 一下,那个开发在接口里面 query 出来 update 一下,代码多了,服务多了,基本上就失控了,第一版写的人很爽,因为一开始状态很少,变量也少,逻辑还没有分散在各个代码仓库(草台班子还用上了微服务,逻辑没有内聚,全靠人肉对代码进行跨仓库静态分析),基本上不会有什么思维负担,对于三版第四版维护的人来讲,简直比吃屎还难受,时间评估的多,还容易改错。

    说到底用 JPA 跟用 Mybatis 是两种截然不同的开发模式跟思维方式,一个系统里面 OLTP 的部分出错了,需要花费巨大的成本去定位问题,以及修复数据,并评估对生产数据带来的影响,成本极高。对于业务逻辑相对复杂的项目,建议使用 JPA 去好好建模分析的,减少业务变动对于后续维护者带来的心智负担。

    对于需求变化极快且复杂的 OLAP 需求,这种功能大多都是面向内部,用 SQL 怎么快怎么来,就算出错也没事,改改就好,而且我经常跟人讲,OLAP 就是要打破业务逻辑里面的 Model ,甚至用 es 来重新聚合 Model 的数据都没有问题,因为 OLAP 就算出问题,who care ?分析数据?根本不重要的东西。

    以我之前那个案例来讲,有一个哥们因为车辆状态太复杂,帮业务下线一匹数据,job 维护车辆的逻辑写错了,导致生产上几千辆车 没法进行租售,直接就是生产重大事故,要是做个报表联几个表出一个后台页面,就算是错了又能怎样?糊弄一下就过去了。
    GoRoad
        112
    GoRoad  
       328 天前
    很奇怪,翻了一下帖子 大家都是在友好讨论,反而 op 的 append 有些破防了,我认为 op 这是在恶意引战不成破防了,@Livid 恶意引战
    chuck1in
        113
    chuck1in  
       327 天前
    hibernate 除了本身的 ORM 概念以外,其实还有一些其他的概念,会有一些心智负担,有些可能会不太喜欢这个概念。
    mybatis 我想胜在简单吧,在东亚比较流行。

    最后我建议也可以试试 JPA 和 Mybatis 之外的第三种选择:JOOQ
    https://www.v2ex.com/t/1007609#reply12
    chuck1in
        114
    chuck1in  
       327 天前
    @aristotll 同样支持 jooq + 1
    chuck1in
        115
    chuck1in  
       327 天前
    @morgan1freeman 2000 美金恐怕雇佣的还不止入门的哦,雇个接近高级的,运气好雇个高级也不是不可能的。
    Dragonphy
        116
    Dragonphy  
       327 天前
    jpa 能不能做到 mybatis-plus 那样清晰的日志,一堆 alias t.x,t.x 找半天才知道是哪张表
    totoro52
        117
    totoro52  
       327 天前   ❤️ 1
    看了你的 append 好了 黑名单见, 流量赚了马跑了
    yoloMiss
        118
    yoloMiss  
       327 天前
    我曾经见过把 hibernate 又封了一层的项目,用起来跟一坨大便一样。真的是会的人简单,不会的人瞎几把搞啊。
    dyv9
        119
    dyv9  
       327 天前 via Android
    @yooomu 最近特意把我接手的 3 个项目换到 Java 21 编译级别,开发中观察我们的项目对 Java 21 的兼容性,我相信我啥也不需要改就可以跑起来。
    gowk
        120
    gowk  
       327 天前
    @morgan1freeman #111 感谢分享,太 tm 真实了!
    jinsongzhao
        121
    jinsongzhao  
       326 天前
    说点有用的吧,hibernate 一套代码兼容多种数据库,适合做产品。多表关联和本地 SQL 等等,复杂,但是也能做,并且多了一些 HQL 的特有功能,这些功能是为了方便某些特殊情况的,绕那么大一卷坚持 ORM ,总要有某个重要优点。最后,JPA 是借鉴 Hibernate 的,或者是说 Hibernate 是 JPA 的试验基地,设计者本身也是 Java 标准的制定者,所以更有权势,技能寿命会更长久。
    pocketz
        122
    pocketz  
       326 天前
    darrenfang
        123
    darrenfang  
       326 天前 via iPhone
    @leohuangsulei 公司项目用 mybatis plus ,2 个条件以内的查询我是支持将查询条件用作方法名。
    darrenfang
        124
    darrenfang  
       326 天前 via iPhone
    公司项目用的 mybatis plus ,有很多人直接用 QueryWrapper 写查询条件,更让人头疼🤕️
    shitshit666
        125
    shitshit666  
       326 天前
    Java 那群老顽固多的很,你让他改变点什么,比杀了他还难受。
    byte10
        126
    byte10  
       326 天前
    @Umenezumi
    @x66
    这一点其实我挺同意的,本身面向过程开发 就是比较舒服,快速,符合人理解的方式。面向对象确实比较难一些,这个真没说错,大多数都还是 mvc 模式,而不是 struts2 。
    28Sv0ngQfIE7Yloe
        127
    28Sv0ngQfIE7Yloe  
       326 天前
    @shitshit666
    你也是挺能扯的
    ForMrFang
        128
    ForMrFang  
       305 天前
    两者各有优缺点.取决于业务或数据结构. 不过我推荐可以尝试一下 mybatis flex.
    SkyLine7
        129
    SkyLine7  
       303 天前
    现在我们都用 mybatis 注解写了,不用 xml
    vishun
        130
    vishun  
       190 天前
    原先也很倾向纯 ORM ,但是现在反而越来越觉得复杂 sql 还是原生的更直观,可以参考这个[根据条件分页查询用户列表]( https://gitee.com/mybatis-flex/mybatis-flex/issues/I90V7G#note_25272825_link),我贴过来:
    ```
    /**
    * 根据条件分页查询用户列表
    *
    * @param searchReq 用户信息
    *
    * @return 用户信息集合信息
    */
    @Override
    @DataScope(deptAlias = SysDept.TABLE_ALIAS, userAlias = SysUser.TABLE_ALIAS)
    public Page<SysUser> selectUserList(PageAdapter<SysUser> pageAdapter, final SysUser searchReq) {
    final QueryWrapper queryWrapper = queryChain()
    .select(SYS_USER.DEFAULT_COLUMNS, SYS_DEPT.DEPT_NAME, SYS_DEPT.LEADER)
    .from(SYS_USER.as(SysUser.TABLE_ALIAS))
    .leftJoin(SYS_DEPT).as(SysDept.TABLE_ALIAS).on(SYS_USER.DEPT_ID.eq(SYS_DEPT.DEPT_ID))

    .eq(SysUser::getDelFlag, UserConstants.NORMAL) // 是否删除: 否
    .eq(SysUser::getUserId, searchReq.getUserId(), IdUtils.isIdValid(searchReq.getUserId())) // 用户 ID
    .like(SysUser::getUserName, searchReq.getUserName(), StrUtil.isNotBlank(searchReq.getUserName())) // 用户名称
    .eq(SysUser::getStatus, searchReq.getStatus(), StrUtil.isNotBlank(searchReq.getStatus())) // 用户状态
    .like(SysUser::getPhonenumber, searchReq.getPhonenumber(), StrUtil.isNotBlank(searchReq.getPhonenumber())) // 手机号码
    .ge(SysUser::getCreateTime, searchReq.getParams().getBeginTime(), Objects.nonNull(searchReq.getParams().getBeginTime())) // 创建时间开始
    .le(SysUser::getCreateTime, searchReq.getParams().getEndTime(), Objects.nonNull(searchReq.getParams().getEndTime())) // 创建时间结束
    .and(qw -> qw.eq(SysUser::getDeptId, searchReq.getDeptId()) // 部门过滤
    .or(SYS_USER.DEPT_ID.in(QueryWrapper.create().select(SYS_DEPT.DEPT_ID)
    .from(SYS_DEPT)
    .where(QueryMethods.findInSet(QueryMethods.number(searchReq.getDeptId()), SYS_DEPT.ANCESTORS).gt(0)))),
    IdUtils.isIdValid(searchReq.getDeptId()))
    // 数据权限
    .and(qw -> qw.where(searchReq.getParams().getDataScope()), searchReq.getParams().hasDataScopeFilter());

    return getMapper().paginate(pageAdapter.getPage(), queryWrapper);
    }
    ```
    如果用 xml 差不多是这样:
    ```
    <select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
    select u.user_id, u.dept_id, u.nick_name, u.user_name, u.email, u.avatar, u.phonenumber, u.password,
    u.sex,u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time,
    u.remark,
    u.show_tag,
    d.dept_name, d.leader
    from sys_user u
    left join sys_dept d on u.dept_id = d.dept_id
    where u.del_flag = '0'
    <if test="searchValue != null and searchValue != ''">
    AND (
    u.user_name like concat('%', #{searchValue}, '%')
    OR u.nick_name like concat('%', #{searchValue}, '%')
    )
    </if>
    <if test="userId != null ">
    AND u.user_id = #{userId}
    </if>
    <if test="userName != null and userName != ''">
    AND u.user_name like concat('%', #{userName}, '%')
    </if>
    <if test="status != null and status != ''">
    AND u.status = #{status}
    </if>
    <if test="phonenumber != null and phonenumber != ''">
    AND u.phonenumber like concat('%', #{phonenumber}, '%')
    </if>
    <if test="beginTime != null and beginTime != ''"><!-- 开始时间检索 -->
    AND date_format(u.create_time,'%y%m%d') &gt;= date_format(#{beginTime},'%y%m%d')
    </if>
    <if test="endTime != null and endTime != ''"><!-- 结束时间检索 -->
    AND date_format(u.create_time,'%y%m%d') &lt;= date_format(#{endTime},'%y%m%d')
    </if>
    <if test="deptId != null and deptId != 0">
    AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE FIND_IN_SET
    (#{deptId},ancestors) ))
    </if>
    <!-- 数据范围过滤 -->
    ${params.dataScope}
    </select>
    ```
    个人感觉 xml 方式反而更加直观看出查询的字段和检索条件。
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4962 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 03:50 · PVG 11:50 · LAX 19:50 · JFK 22:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.