spring在管理hibernate上有独到的地方能够顺手拿来用,我也是想在能不抛弃hibernate的基础上尽量多挖掘一下它的一些性能提高上的作法,总结你们的见解,基本得出一致结论:复杂查询依靠jdbc的sql或者hibernate提供的本地化sql封装,或者使用spring的管理,均可以提高性能和效率.我认为对于hibernate的性能优化应该是无止境的.spring
在项目中使用Hibernate进行大数据量的性能测试,有一些总结,
1) 在处理大数据量时,会有大量的数据缓冲保存在Session的一级缓存中,这缓存大太时会严重显示性能,因此在使用Hibernate处理大数据量的,可使用session.clear()或者session. Evict(Object) 在处理过程当中,清除所有的缓存或者清除某个对象。
2) 对大数据量查询时,慎用list()或者iterator()返回查询结果,
1. 使用List()返回结果时,Hibernate会全部查询结果初始化为持久化对象,结果集较大时,会占用不少的处理时间。
2. 而使用iterator()返回结果时,在每次调用iterator.next()返回对象并使用对象时,Hibernate才调用查询将对应的对象初始化,对于大数据量时,每调用一次查询都会花费较多的时间。当结果集较大,可是含有较大量相同的数据,或者结果集不是所有都会使用时,使用iterator()才有优点。
3. 对于大数据量,使用qry.scroll()能够获得较好的处理速度以及性能。并且直接对结果集向前向后滚动。
3) 对于关联操做,Hibernate虽然能够表达复杂的数据关系,但请慎用,使数据关系较为简单时会获得较好的效率,特别是较深层次的关联时,性能会不好。
4) 对含有关联的PO(持久化对象)时,若default-cascade="all"或者 “save-update”,新增PO时,请注意对PO中的集合的赋值操做,由于有可能使得多执行一次update操做。
5) 在一对多、多对一的关系中,使用延迟加载机制,会使很多的对象在使用时方会初始化,这样可以使得节省内存空间以及减小的负荷,并且若PO中的集合没有被使用时,就可减小互数据库的交互从而减小处理时间。 数据库什么叫n+1次select查询问题?
在Session的缓存中存放的是相互关联的对象图。默认状况下,当Hibernate从数据库中加载Customer对象时,会同时加载全部关联的Order对象。以Customer和Order类为例,假定ORDERS表的CUSTOMER_ID外键容许为null,图1列出了CUSTOMERS表和ORDERS表中的记录。
如下Session的find()方法用于到数据库中检索全部的Customer对象:
List customerLists=session.find("from Customer as c");
运行以上find()方法时,Hibernate将先查询CUSTOMERS表中全部的记录,而后根据每条记录的ID,到ORDERS表中查询有参照关系的记录,Hibernate将依次执行如下select语句:sql
select * from CUSTOMERS; select * from ORDERS where CUSTOMER_ID=1; select * from ORDERS where CUSTOMER_ID=2; select * from ORDERS where CUSTOMER_ID=3; select * from ORDERS where CUSTOMER_ID=4;
经过以上5条select语句,Hibernate最后加载了4个Customer对象和5个Order对象,在内存中造成了一幅关联的对象图,参见图2。数据库
Hibernate在检索与Customer关联的Order对象时,使用了默认的当即检索策略。这种检索策略存在两大不足:
(a) select语句的数目太多,须要频繁的访问数据库,会影响检索性能。若是须要查询n个Customer对象,那么必须执行n+1次select查询语句。这就是经典的n+1次select查询问题。这种检索策略没有利用SQL的链接查询功能,例如以上5条select语句彻底能够经过如下1条select语句来完成:缓存
select * from CUSTOMERS left outer join ORDERS on CUSTOMERS.ID=ORDERS.CUSTOMER_ID
以上select语句使用了SQL的左外链接查询功能,可以在一条select语句中查询出CUSTOMERS表的全部记录,以及匹配的ORDERS表的记录。
(b)在应用逻辑只须要访问Customer对象,而不须要访问Order对象的场合,加载Order对象彻底是多余的操做,这些多余的Order对象白白浪费了许多内存空间。
为了解决以上问题,Hibernate提供了其余两种检索策略:延迟检索策略和迫切左外链接检索策略。延迟检索策略能避免多余加载应用程序不须要访问的关联对象,迫切左外链接检索策略则充分利用了SQL的外链接查询功能,可以减小select语句的数目。
刚查阅了hibernate3的文档:
查询抓取(默认的)在N+1查询的状况下是极其脆弱的,所以咱们可能会要求在映射文档中定义使用链接抓取: 性能优化
<set name="permissions" fetch="join"> <key column="userId"/> <one-to-many class="Permission"/> </set <many-to-one name="mother" class="Cat" fetch="join"/>
在映射文档中定义的抓取策略将会有产生如下影响: session
经过get()或load()方法取得数据。
只有在关联之间进行导航时,才会隐式的取得数据(延迟抓取)。
条件查询
在映射文档中显式的声明 链接抓取作为抓取策略并不会影响到随后的HQL查询。
一般状况下,咱们并不使用映射文档进行抓取策略的定制。更多的是,保持其默认值,而后在特定的事务中, 使用HQL的左链接抓取(left join fetch) 对其进行重载。这将通知 Hibernate在第一次查询中使用外部关联(outer join),直接获得其关联数据。 在条件查询 API中,应该调用 setFetchMode(FetchMode.JOIN)语句。
6) 对于大数据量新增、修改、删除操做或者是对大数据量的查询,与数据库的交互次数是决定处理时间的最重要因素,减小交互的次数是提高效率的最好途径,因此在开发过程当中,请将show_sql设置为true,深刻了解Hibernate的处理过程,尝试不一样的方式,可使得效率提高。
7) Hibernate是以JDBC为基础,可是Hibernate是对JDBC的优化,其中使用Hibernate的缓冲机制会使性能提高,如使用二级缓存以及查询缓存,若命中率较高明,性能会是到大幅提高。
8) Hibernate能够经过设置hibernate.jdbc.fetch_size,hibernate.jdbc.batch_size等属性,对Hibernate进行优化。
hibernate.jdbc.fetch_size 50
hibernate.jdbc.batch_size 25
这两个选项很是很是很是重要!!!将严重影响Hibernate的CRUD性能!
C = create, R = read, U = update, D = delete
Fetch Size 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数。
例如一次查询1万条记录,对于Oracle的JDBC驱动来讲,是不会1次性把1万条取出来的,而只会取出Fetch Size条数,当纪录集遍历完了这些记录之后,再去数据库取Fetch Size条数据。
所以大大节省了无谓的内存消耗。固然Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch Size越小,读数据库的次数越多,速度越慢。
这有点像平时咱们写程序写硬盘文件同样,设立一个Buffer,每次写入Buffer,等Buffer满了之后,一次写入硬盘,道理相同。
Oracle数据库的JDBC驱动默认的Fetch Size=10,是一个很是保守的设定,根据个人测试,当Fetch Size=50的时候,性能会提高1倍之多,当Fetch Size=100,性能还能继续提高20%,Fetch Size继续增大,性能提高的就不显著了。
所以我建议使用Oracle的必定要将Fetch Size设到50。
不过并非全部的数据库都支持Fetch Size特性,例如MySQL就不支持。
MySQL就像我上面说的那种最坏的状况,他老是一下就把1万条记录彻底取出来,内存消耗会很是很是惊人!这个状况就没有什么好办法了 :(
Batch Size是设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,有点至关于设置Buffer缓冲区大小的意思。
Batch Size越大,批量操做的向数据库发送sql的次数越少,速度就越快。我作的一个测试结果是当Batch Size=0的时候,使用Hibernate对Oracle数据库删除1万条记录须要25秒,Batch Size = 50的时候,删除仅仅须要5秒!!!
//咱们一般不会直接操做一个对象的标识符(identifier),所以标识符的setter方法应该被声明为私有的(private)。这样当一个对象被保存的时候,只有Hibernate能够为它分配标识符。你会发现Hibernate能够直接访问被声明为public,private和protected等不一样级别访问控制的方法(accessor method)和字段(field)。 因此选择哪一种方式来访问属性是彻底取决于你,你可使你的选择与你的程序设计相吻合。
全部的持久类(persistent classes)都要求有无参的构造器(no-argument constructor);由于Hibernate必需要使用Java反射机制(Reflection)来实例化对象。构造器(constructor)的访问控制能够是私有的(private),然而当生成运行时代理(runtime proxy)的时候将要求使用至少是package级别的访问控制,这样在没有字节码编入(bytecode instrumentation)的状况下,从持久化类里获取数据会更有效率一些。
而hibernate.max_fetch_depth 设置外链接抓取树的最大深度
取值. 建议设置为0到3之间
就是每次你在查询时,会级联查询的深度,譬如你对关联vo设置了eager的话,若是fetch_depth值过小的话,会发多不少条sql
Hibernate的Reference以后,能够采用批量处理的方法,当插入的数据超过10000时,就flush session而且clear。
下面是一个测试method。
View Codeide
这只是简单的测试,实际项目中遇到的问题,要比这个复杂得多。 性能
这时候,咱们可让Spring来控制Transaction,本身来控制Hibernate的Session,随时更新数据。
首先,利用HibernateDaoSupport类来自定义个方法打开Session;
View Code测试
而后,用打开的Session处理你的数据;fetch
View Code
每作一次数据操做,就更新一次Session,这样能够保证每次数据操做都成功,不然就让Spring去控制它roll back吧。
最后,记得关闭Session。1 Session session = openSession(); 2 doBusiness(session); 3 session.close(); // 关闭session