为何不少人不肯意用hibernate了?

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

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

欲速则不达,欲达则欲速!程序员

1、hibernate优点web

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

一、@Lazy注解是什么?sql

@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,也就是说只要加了这个注解就会延迟加载。后端

二、@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容器启动的加载时间

2、hibernate劣势

看完优点以后,感受hibernate无所不能了,无敌是多么的寂寞。处理大量数据或者大并发状况的网络服务感受不是很好用,那么如今开始说说hibernate的问题。

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

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作到了这点。

二、知足不了程序对cache的需求

不少web服务,对cache的依赖是很是大的,hibernate自带的cache按理说也是很强大的,但仍是知足不了不少需求。

三、耦合度高

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

四、debug难

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

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

五、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来调用存储过程。 

六、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

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

 

4、被喷以后的一些感悟

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

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

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

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

 

相关博文

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

相关文章
相关标签/搜索