文档章节

为什么很多人不愿意用hibernate了?

素小暖OSC
 素小暖OSC
发布于 01/13 16:11
字数 3129
阅读 7351
收藏 29

关于SQL和ORM的争论,永远都不会终止,我也一直在思考这个问题。最近温习了一遍SSH框架,发了动弹,和广大猿友进行了深刻的探讨,被喷的五体投地,感慨万千,于是就有了今天这篇文章。

声明:本文只是小编的一点拙见,不喜勿喷。

欲速则不达,欲达则欲速!

一、hibernate优势

hibernate让你不用写sql了,这不单可以让你的应用更好移植其它数据库,更主要的是让程序员更专注业务逻辑、数据关系、对象关系等。hibernate对一对多,多对多关系实现是非常好的。很关键一点,它支持lazy,可以让你的数据只在需要的时候被加载,听起来很完美。hibernate还有一个更牛的就是HQL,这是完全可以把查询映射到你OO模型的查询语言,和mybatis的映射比起来,还是更方便和更强大的。

1、@Lazy注解是什么?

@Lazy注解用于标识bean是否需要延迟加载,源码如下:

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lazy {
    /**
     * Whether lazy initialization should occur.
     */
    boolean value() default true;
}

只有一个参数,默认是true,也就是说只要加了这个注解就会延迟加载。

2、@Lazy注解怎么使用

没加注解之前主要容器启动就会实例化bean,如下:

AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig.class);

创建user实例

而加上@Lazy注解则必须在第一次调用的时候才会加载如下:

@Scope
@Lazy
@Bean(value="user0",name="user0",initMethod="initUser",destroyMethod="destroyUser")
public User getUser(){
    System.out.println("创建user实例");
    return new User("张三",26);
}
AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig.class);
User bean2 = applicationContext2.getBean(User.class);
创建user实例
实例1 === User [userName=张三, age=26]

@Lazy注解注解的作用主要是减少springIOC容器启动的加载时间

二、hibernate劣势

看完优势之后,感觉hibernate无所不能了,无敌是多么的寂寞。处理大量数据或者大并发情况的网络服务感觉不是很好用,那么现在开始说说hibernate的问题。

1、难以使用数据库的一些功能

hibernate将数据库与开发者隔离了,开发者不需要关注数据库是Oracle还是MySQL,hibernate来帮你生成查询的sql语句,但问题来了,如果你想用某种数据库特有的功能,或者要让查询的sql完全符合你的心意,这就难了。如果使用hibernate,虽然它能对生成的查询进行一定程序的定制,但就有点隔靴挠痒的感觉了,而且你开发起来付出的代价更大。至于hibernate对native sql的支持,还是挺完善的,这种native sql还能返回non-managed entity,不走hibernate的cache,优化是搞定了,但如果整个项目都这么整,那还是用mybatis吧。

很多时候,我们都有一个需求:得到数据库服务器的当前时间。这是因为本机时间和服务器时间是有差别的。各种数据库都提供了函数来获得,比如,mysql,可以用“select now()”。hibernate也提供了一个函数current_timestamp(说起timestamp,个人认为数据库的timestamp做的很差,它居然和datetime是一个数量级的(精确度),这怎么可以用来表示真正的stamp啊!)。可是,你却无法用直接使用“select current_timestamp()”来获得服务器的当前时间,你还必须加上一个查询的表!比如,“select current_timestamp() from tbl_Good”。个人十分郁闷,我只是想用这个简单功能而已,为什么我一定要知道数据库里面的表格呢????更何况还必须建立映射。。。。。。

不是我不明白,这世界太复杂了 。每样产品都是拼命的复杂化,其实,它们实在是忽略了一般的用户只需要一小部分功能而已。默认的功能应该是能够满足普通用户的常见需求的,那样才算是一个好的产品。我不认为hibernate做到了这点。

2、满足不了程序对cache的需求

很多web服务,对cache的依赖是非常大的,hibernate自带的cache按理说也是很强大的,但还是满足不了很多需求。

3、耦合度高

hibernate的确是在你项目开发的时候节约了很多时间,但是它对你的业务逻辑模型和数据库模型互相依赖的程序太高了。短期没啥问题,但随着项目的变迁,这些都会改变,在维持这种仅仅耦合的关系的时候,你会发信你的代码特别脆弱,随便改一处数据库的schema,整个java项目可能要改几十次。而且现在mybatis的自动mapping做的也很好,开发起来也没花多长时间,等项目进入中后期,你需要大量定制和优化查询的时候,mybatis的开发效率就更明显了。

4、debug难

作为一个后端程序员,我比较喜欢每一行代码我都精确知道它到底在干什么。尤其是数据库访问的代码,往往系统的瓶颈就在这些地方产生,每一行都不能小看。我看过hibernate早期版本的部分代码,比我想象的复杂和强大很多。的确很多地方Hibernate可以强大的只用一行代码解决很多问题,但比如说一个update()或者save()到底做了什么,这里既有hibernate本身的逻辑,也有你应用的逻辑,如果这一行产生了问题,你该如何去做?我个人觉得这是很难搞的,还不如一开始费点事,用mybatis这种。

作为一个程序员,我始终坚持认为改代码比改配置文件容易。

5、hibernate更新大批量数据

(1)hibernate批量更新customers表中大于零的所有记录的age字段:

Transaction transaction = session.beginTransaction();     
Iterator customers=session.find("from Customer c where c.age>0").iterator();     
while(customers.hasNext()){     
    Customer customer=(Customer)customers.next();     
    customer.setAge(customer.getAge()+1);     
}      
transaction.commit();     
session.close();  

如果customers表中有一万条年龄大于零的记录,那么session的find()方法会一下子加载一万个customer对象到内存中。当执行tx.commit()方法时,会清理缓存,hibernate执行一万条更新customers表的update语句:

update CUSTOMERS set AGE=? …. where ID=i;     

(2)以上hibernate批量更新方式有两个缺点

  • 占用大量内存空间,必须把一万个customer对象先加载到内存,然后一一更新他们。
  • 执行的update语句的数目太多,每个update语句只能更新一个Customer对象,必须通过1万条update语句才能更新一万个Customer对象,频繁的访问数据库,会大大降低应用的性能。

(3)为了迅速释放1万个Customer对象占用的内存,可以在更新每个Customer对象后,就调用Session的evict()方法立即释放它的内存:

Transaction transaction = session.beginTransaction();     
Iterator customers=session.find("from Customer c where c.age>0").iterator();     
while(customers.hasNext()){     
    Customer customer=(Customer)customers.next();     
    customer.setAge(customer.getAge()+1);     
    session.flush();     
    session.evict(customer);     
}      
transaction.commit();     
session.close();  

在以上程序中,修改了一个Customer对象的age属性后,就立即调用Session的flush()方法和evict()方法,flush()方法使hibernate立刻根据这个Customer对象的状态变化同步更新数据库,从而立即执行相关的update()语句;evict()方法用于把这个Customer对象从缓存中清除出去,从而及时释放它占用的内存。

但evict()方法只能稍微提高批量操作的性能,因为不管有没有使用evict()方法,Hibernate都必须执行1万条update语句,才能更新1万个Customer对象,这是影响批量操作性能的重要因素。假如Hibernate能直接执行如下SQL语句:

update CUSTOMERS set AGEAGE=AGE+1 where AGE>0;   

那么以上一条update语句就能更新CUSTOMERS表中的1万条记录。但是Hibernate并没有直接提供执行这种update语句的接口。应用程序必须绕过Hibernate API,直接通过JDBC API来执行该SQL语句:

Transaction transaction = session.beginTransaction();     
Connection con=session.connection();     
PreparedStatement stmt=con.prepareStatement("update CUSTOMERS set AGEAGE=AGE+1 where AGE>0 ");     
stmt.executeUpdate();     
transaction.commit();  

以上程序演示了绕过Hibernate API,直接通过JDBC API访问数据库的过程。应用程序通过Session的connection()方法获得该Session使用的数据库连接,然后通过它创建 PreparedStatement对象并执行SQL语句。值得注意的是,应用程序仍然通过Hibernate的Transaction接口来声明事务边 界。 
如果底层数据库(如Oracle)支持存储过程,也可以通过存储过程来执行Hibernate批量更新。存储过程直接在数据库中运行,速度更加快。在Oracle数据库中可以定义一个名为batchUpdateCustomer()的存储过程,代码如下:

create or replace procedure batchUpdateCustomer(p_age in number) as     
begin     
update CUSTOMERS set AGEAGE=AGE+1 where AGE>p_age;     
end;  

以上存储过程有一个参数p_age,代表客户的年龄,应用程序可按照以下方式调用存储过程:

Transaction transaction = session.beginTransaction();     
Connection con=session.connection();     
String procedure = "{call batchUpdateCustomer(?) }";     
CallableStatement cstmt = con.prepareCall(procedure);     
cstmt.setInt(1,0); //把年龄参数设为0     
cstmt.executeUpdate();     
transaction.commit();  

从上面程序看出,应用程序也必须绕过Hibernate API,直接通过JDBC API来调用存储过程。 

6、hibernate删除大批量数据

Session的各种重载形式的update()方法都一次只能更新一个对象,而delete()方法的有些重载形式允许以HQL语句作为参数,例如:

session.delete("from Customer c where c.age>0");  

如果CUSTOMERS表中有1万条年龄大于零的记录,那么以上代码能删除一万条记录。但是Session的delete()方法并没有执行以下delete语句

delete from CUSTOMERS where AGE>0;  

Session的delete()方法先通过以下select语句把1万个Customer对象加载到内存中:

select * from CUSTOMERS where AGE>0;  

接下来执行一万条delete语句,逐个删除Customer对象:

delete from CUSTOMERS where ID=i;     
delete from CUSTOMERS where ID=j;     
delete from CUSTOMERS where ID=k;  

由 此可见,直接通过Hibernate API进行Hibernate批量更新和Hibernate批量删除都不值得推荐。而直接通过JDBC API执行相关的SQL语句或调用存储过程,是hibernate批量更新和批量删除的最佳方式。

素小暖讲Spring JdbcTemplate

三、群众的眼光的雪亮的,千万不要逆天而行

 

四、被喷之后的一些感悟

感觉就是一场批斗大会,我深深的感觉到才疏学浅的无奈,我真的只是想好好学习而已,希望若干年后,我还能初心不改。

作为一个初级程序员而言,没有必要花费过多的时间去证明技术的无用论,我并没有对hibernate这个框架进行深入的研究,只是在肤浅的层面的一些个人感悟。

框架本身并没有对错一说,只有适合不适合,任何框架都有其自身的能力范围,hibernate封装了很多有用的API给我们,降低了操作数据库的难度和复杂度,同时也减少了模板代码的数量,但hibernate留给开发者的可操作空间相对mybatis少了很多;mybatis框架使用起来更加灵活,开发者可以自定义查询语句,但增加了模板代码量,看起来并没有hibernate边界。两种框架在便捷与灵活两个指标上做出了取舍和妥协,这不能说是框架的错。对于一个框架而言,需要有自身专注的领域和设计愿景,不可能面面俱到,就如这位大哥所言:

使用任何一种技术框架,都需要贴合现实的业务需求以及自身的技术能力。当你还没有深入的去了解一门技术或者当前业务需求无法与框架契合时,不要盲目的批判框架的好坏。

 

相关博文

素小暖讲Java框架(SSH、SSM、Springboot)

© 著作权归作者所有

素小暖OSC
粉丝 141
博文 93
码字总数 218003
作品 0
大连
私信 提问
加载中

评论(122)

名字呢
名字呢
这水的什么玩意儿,我居然看完了,而且还上了首页。就算是 Hibernate 3 也没得作者说的这么不堪吧。文章中作者拿批量删除、批量更新来说事,简直是无稽之谈。
素小暖OSC
素小暖OSC 博主
哈哈,居然还有29个人收藏了,你说气不气
我叫金正恩
我叫金正恩
微服务下没必要搞这么重的组件.一个服务也不会操作那么多表。
谁说谁是谁的灰
谁说谁是谁的灰
我们公司用的JPA,底核hibernate。招人的时候没有特别注明,我们jpa那边又允许了他们用native代码,导致我们的hibernate被用的跟mybaties一样,JPA的dao层全是sql语句,至少比mybatis少了一个xml文件吧。
sakurawinds
sakurawinds
建议再再再复习下hibernate,我觉得国内mybatis 多主要是受阿里系等互联网公司影响。另外我觉得一个hibernate都搞不明白的人,你能相信他写的代码(可悲的是找个能用好mybatis 的人都已经很开心了😂)。
YANG_YAWEI
YANG_YAWEI
本来跟数据库打交道,怎么都需要写 sql 的,用 mybatis 或者 mybatis-plus 这种简单的框架更可控,出了问题排查和解决起来都比较容易,而且针对单表的操作,mybatis-plus 也封装好了很多方法可以方便的使用,能显著的提高开发效率,而像 hibernate 这种笨重的框架,很容易就掉坑里,在经历了无数的踩坑填坑之后,大家就都明白了,跟关系型数据库打交道,根本不需要 ORM,SQL Mapper 就完全足够了,虽然 hibernate 可以用 SQLQuery 手写 sql 来优化,但是代码里面拼 sql 实在是太艹了,不然大家都在用 spring 的 JdbcTemplate 了
素小暖OSC
素小暖OSC 博主
JdbcTemplate目前用的人多吗,我身边没几个用的
YANG_YAWEI
YANG_YAWEI
我的意思是因为在代码里面拼接 sql 太麻烦了,所以才不用 JdbcTemplate,不然只是把查询结果转换成对象 JdbcTemplate 的功能也是够用的,不过 JdbcTemplate 有需要优化批处理的时候也可以用下,之前同事发现用 jdbc 的批处理进行批量插入,效率比写 insert into table values () ,().... 要高
素小暖OSC
素小暖OSC 博主
略懂略懂
微笑兔
微笑兔
首先呢 mybatis-plus可以了解一下,简单的sql基本不用写一行sql,全代码拼装!非常好用,hibernate我们公司有封装的框架,我个人觉得使用起来并不舒服,尤其是在想定义sql实现复杂查询的时候,真的很难受!不过各有各的好处吧!
CHONGCHEN
CHONGCHEN
hibernate挺好用的呀,自动生成表,只需要写hql就行了,完全没有太大难度
傻根她弟
傻根她弟
我是坚定支持Hibernate的老程序员,但是hibernate和mybatis相比有一些缺点:
1.门槛高。不管是新手还是老手,用不好确实会有问题,尤其是select
2.懒加载在分布式下 显示不出优势了

但是相比较有很多人热衷于mybatis的面向对象查询车轮也说明 他们比较矛盾

综合比较,我对h和m都支持。所以,在我司开发框架中,orm层,完美的同时支持hibernate和mybaits,而且还扩展了对es的支持,就是同一套接口,可以在三者之间自由切换。我的初衷是 CUD操作使用h,R操作使用mybatis或者是es,真实环境中到底用什么,具体看项目各种情况的要求了。

从唯美到认同,从否定到接受,这是我从hibernate扩展到mybatis的过程,取二者之长,为开发人员服务,为项目服务

但是有个不争的事实,当我司框架支持mybatis后,很多人用m而不是h,我个人分析主要原因是m入门简单易上手
x
xytest01
jooq 了解一下
f
freezingsky
jooq只看过介绍, 没有正式使用过。 目前用得比较多的是Specification和 QueryDSL。
x
xytest01
竟然有库用 Specification 这么 general 的名字吗? 用 QueryDSL 或者 jOOQ 都比用基于字符串模板的 mybatis 好很多
f
freezingsky
JPA中提供一种动态查询的方式,specification
jang675953
jang675953
水货一个
ExtremeTalk
ExtremeTalk
这个感觉就是一篇十年前的文章,充满了十年前的认知
不要为了代码整洁,而放弃写出好代码

本文是从 Clean Code Versus Great Code 这篇文章翻译而来。 最 近我和一些程序员就编程问题进行了一次有趣的讨论。我通常会有这样的印象,感觉一些程序员过于强调代码的整洁。不要误会,我也...

红薯
2011/08/10
4.2K
19
hibernate flush 机制与调用 save 后不立即打印 sql 的问题

针对昨天同事遇到的hibernate的问题。算是hibernate最基本的东西。具了解,这个问题很多人遇到过,也很常见,却遇到了还经常会懵了。 为了加深印象,知其然,知其所以然。 之后单纯用原始的H...

唐海洋Sir
2014/02/28
1K
0
Hibernate 并非一无是处

最近挺长一段时间,有很多人会喷 Hibernate,特别是 12306 被曝用 Hibernate 以来,更是喷得厉害。尽管我对“喷”这件事已经完全免疫了,没有兴趣参与,也丝毫不会激发我的一丝斗志,顶多就是...

红薯
2012/10/16
5.4K
28
Zoo_Fun 一直有个问题想请教各位java web面向企业应用软件方面的前辈,能看一下吗,关于创业or上班工作

用架构1jsp fckeditor jquery struts2 spring security spring jpa hibernate 架构2或者直接用spring side架构一个平台然后 在其上 写几个 新闻发布 纯粹 jsp forword 形式的业务流程,不去设...

mauersu
2013/02/15
222
4
面向解决问题的java编程,spring boot,mybatis generator和坑-0废话篇

当一个立志成为程序员的人开始一门新技术的学习时,是很兴奋的,因为很多新技术的介绍总是有很多酷炫的新名称,酷炫的新功能和一些与旧技术的对比,在对比中,新技术总是用各种对比数据把旧数...

wphmoon
2019/05/16
130
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周日乱弹 —— 谨以此文怀念逝去的人

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 今天没歌曲 手机党少年们想听歌,请使劲儿戳(这里) 今天是周六, 刚想开始写乱弹的时候, 翻到了巴叔新建的话题上, @ 巴拉迪维:宏哥,一路...

小小编辑
47分钟前
15
1
判断php变量是否定义,是否为空,是否为真的一览表

使用 PHP 函数对变量 $x 进行比较 表达式 gettype() empty() is_null() isset() boolean : if($x) $x = ""; string TRUE FALSE TRUE FALSE $x = null; NULL TRUE TRUE FALSE FALSE var $x; N......

Grayk
今天
12
0
服务器监控

1. 服务器监控内容有哪些? 答: 主要有服务监控 和 流量监控 2. 监控重点是? 答: 是 磁盘 和 流量,cpu是浮动的,意义不大 3. 怎样 实现 监控? 答:将 服务器 分为:监控者 和 被监管协议...

杨凯123
昨天
242
0
Apollo开发指南

一、准备工作 二、本地启动 2.1 Apollo Config Service和Apollo Admin Service 2.2 Apollo-Portal 2.3 Java样例客户端启动 2.4 .Net样例客户端启动 三、开发 3.1 Portal 实现用户登录功能 3....

Ciet
昨天
307
0
英文词汇 计算机网络中的专业英语单词及其缩写

学习计算机网络时,会阅读相关的专业文献。对于文献中经常出现的缩写形式的专业名词,做了一些积累。现于此博文中做个简单的分享,希望能对后来人有所帮助,平稳地入门计算机网络。 注:博文...

志成就
昨天
178
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部