###一直不能理解 Hibernate 这么优秀的 orm 思想框架,为什么这么多项目还选择了在 xml 里写 sql 的 MyBatis ,求解惑?
101
nimble 303 天前
国产的 mybatis-plus 使用的方式跟 jpa 和 hibernate 一样,如果你的项目使用的是 mybatis-plus ,jpa 完全没问题,并且支持的功能更多,除了它放弃了恶心的 xml 写 sql 。
|
102
wlm201219 303 天前
@BiChengfei 我觉得,从这个帖子的讨论和现在 35 岁就业危机来看,国内并不会从 mybatis 转向 jpa
|
104
aristotll 303 天前
用 jooq 逃
|
105
pigspy 303 天前
作为一个 hibernate + jpa 的使用者本来想认真回答下的,不过这 lz 看上去是钓鱼的,真是建议 nuke 了
|
106
izzy27 303 天前
Hibernate 不好念,不利于传播
|
107
jason31415926 303 天前
@wlm201219 可以聊聊国内计算机教育脱节的问题吗,谢谢
|
108
jeesk 302 天前 via Android
这么说吧, 一个报表连表 5-10 张, 你还会用 orm 吗? 谁不想偷懒直接用 orm ?
|
109
jeesk 302 天前 via Android
现在开源的项目多大多都是 mybatis plus , 各种泛型,还不如 jdbc template 呢,
|
110
jeesk 302 天前 via Android
国外的屎都是香的, 真的是。 国外 orm 用得多,国内用的少, 那么国内就是程序员不行。
grpc web 就不遵守 rest 规范,grpc web 全是 post. 一大群人说香,google 就是正义, 这风气真是太舔了。 |
111
morgan1freeman 302 天前 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 维护车辆的逻辑写错了,导致生产上几千辆车 没法进行租售,直接就是生产重大事故,要是做个报表联几个表出一个后台页面,就算是错了又能怎样?糊弄一下就过去了。 |
112
GoRoad 302 天前
很奇怪,翻了一下帖子 大家都是在友好讨论,反而 op 的 append 有些破防了,我认为 op 这是在恶意引战不成破防了,@Livid 恶意引战
|
113
chuck1in 302 天前
hibernate 除了本身的 ORM 概念以外,其实还有一些其他的概念,会有一些心智负担,有些可能会不太喜欢这个概念。
mybatis 我想胜在简单吧,在东亚比较流行。 最后我建议也可以试试 JPA 和 Mybatis 之外的第三种选择:JOOQ https://www.v2ex.com/t/1007609#reply12 |
115
chuck1in 302 天前
@morgan1freeman 2000 美金恐怕雇佣的还不止入门的哦,雇个接近高级的,运气好雇个高级也不是不可能的。
|
116
Dragonphy 302 天前
jpa 能不能做到 mybatis-plus 那样清晰的日志,一堆 alias t.x,t.x 找半天才知道是哪张表
|
117
totoro52 302 天前 1
看了你的 append 好了 黑名单见, 流量赚了马跑了
|
118
yoloMiss 302 天前
我曾经见过把 hibernate 又封了一层的项目,用起来跟一坨大便一样。真的是会的人简单,不会的人瞎几把搞啊。
|
119
dyv9 301 天前 via Android
@yooomu 最近特意把我接手的 3 个项目换到 Java 21 编译级别,开发中观察我们的项目对 Java 21 的兼容性,我相信我啥也不需要改就可以跑起来。
|
120
gowk 301 天前
@morgan1freeman #111 感谢分享,太 tm 真实了!
|
121
jinsongzhao 301 天前
说点有用的吧,hibernate 一套代码兼容多种数据库,适合做产品。多表关联和本地 SQL 等等,复杂,但是也能做,并且多了一些 HQL 的特有功能,这些功能是为了方便某些特殊情况的,绕那么大一卷坚持 ORM ,总要有某个重要优点。最后,JPA 是借鉴 Hibernate 的,或者是说 Hibernate 是 JPA 的试验基地,设计者本身也是 Java 标准的制定者,所以更有权势,技能寿命会更长久。
|
123
darrenfang 301 天前 via iPhone
@leohuangsulei 公司项目用 mybatis plus ,2 个条件以内的查询我是支持将查询条件用作方法名。
|
124
darrenfang 301 天前 via iPhone
公司项目用的 mybatis plus ,有很多人直接用 QueryWrapper 写查询条件,更让人头疼🤕️
|
125
shitshit666 301 天前
Java 那群老顽固多的很,你让他改变点什么,比杀了他还难受。
|
126
byte10 300 天前
|
127
28Sv0ngQfIE7Yloe 300 天前
@shitshit666
你也是挺能扯的 |
128
ForMrFang 280 天前
两者各有优缺点.取决于业务或数据结构. 不过我推荐可以尝试一下 mybatis flex.
|
129
SkyLine7 278 天前
现在我们都用 mybatis 注解写了,不用 xml
|
130
vishun 164 天前
原先也很倾向纯 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') >= date_format(#{beginTime},'%y%m%d') </if> <if test="endTime != null and endTime != ''"><!-- 结束时间检索 --> AND date_format(u.create_time,'%y%m%d') <= 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 方式反而更加直观看出查询的字段和检索条件。 |