Hibernate批量處理其實從性能上考慮它是很不可取的浪費了很大的內存從它的機制上講Hibernate它是先把符合條件的數據查出來放到內存當中然後再進行操作實際使用下來性能非常不理想在筆者的實際使用中采用下面的第三種優化方案的數據是條數據插入數據庫主流台式機的配置需要約分鐘呵呵暈倒
總結下來有三種來處理以解決性能問題
繞過Hibernate API 直接通過 JDBC API 來做這個方法性能上是比較好的也是最快的
運用存儲過程
還是用Hibernate API 來進行常規的批量處理可以也有變變就變在我們可以在查找出一定的量的時候及時的將這些數據做完操作就
刪掉sessionflush()sessionevict(XX對象集) 這樣也可以挽救一點性能損失這個一定的量要就要根據實際情況做定量參考了一般為左右但效果仍然不理想
繞過Hibernate API 直接通過 JDBC API 來做這個方法性能上是比較好的也是最快的(實例為 更新操作)
Transaction tx=sessionbeginTransaction() //注意用的是hibernate事務處理邊界
Connection conn=nnection()
PreparedStatement stmt=connpreparedStatement(update CUSTOMER as C set Csarlary=csarlary+ where csarlary>)
stmtexcuteUpdate()
mit() //注意用的是hibernate事務處理邊界
這小程序中采用的是直接調用JDBC 的API 來訪問數據庫效率很高避免了Hibernate 先查詢出來加載到內存再進行操作引發的性能問題
運用存儲過程但這種方式考慮到易植和程序部署的方便性不建議使用(實例為 更新操作)
如果底層數據庫(如Oracle)支持存儲過程也可以通過存儲過程來執行批量更新存儲過程直接在數據庫中運行速度更加快在Oracle數
據庫中可以定義一個名為batchUpdateCustomer()的存儲過程代碼如下
代碼內容create or replace procedure batchUpdateCustomer(p_age in number) as begin update CUSTOMERS set AGE=AGE+ where AGE>p_ageend
以上存儲過程有一個參數p_age代表客戶的年齡應用程序可按照以下方式調用存儲過程
代碼內容
tx = sessionbeginTransaction()
Connection con=nnection()
String procedure = {call batchUpdateCustomer(?) }
CallableStatement cstmt = conprepareCall(procedure)
cstmtsetInt() //把年齡參數設為
cstmtexecuteUpdate()
mit()
從上面程序看出應用程序也必須繞過Hibernate API直接通過JDBC API來調用存儲過程
還是用Hibernate API 來進行常規的批量處理可以也有變變就變在我們可以在查找出一定的量的時候及時的將這些數據做完操作就
刪掉sessionflush()sessionevict(XX對象集) 這樣也可以挽救一點性能損失這個一定的量要就要根據實際情況做定量參考了……
(實例為 保存操作)
業務邏輯為我們要想數據庫插入 條數據
tx=sessionbeginTransaction()
for(int i=i<i++)
{
Customer custom=new Customer()
customsetName(user+i)
sessionsave(custom)
if(i%==) // 以每個數據作為一個處理單元也就是我上面說的一定的量這個量是要酌情考慮的
{
sessionflush()
sessionclear()
}
}
這樣可以把系統維持在一個穩定的范圍……
在項目的開發過程之中由於項目需求我們常常需要把大批量的數據插入到數據庫數量級有萬級十萬級百萬級甚至千萬級別的如此數量級別的數據用Hibernate做插入操作就可能會發生異常常見的異常是OutOfMemoryError(內存溢出異常)
首先我們簡單來回顧一下Hibernate插入操作的機制Hibernate要對它內部緩存進行維護當我們執行插入操作時就會把要操作的對象全部放到自身的內部緩存來進行管理
談到Hibernate的緩存Hibernate有內部緩存與二級緩存之說由於Hibernate對這兩種緩存有著不同的管理機制對於二級緩存我們可以對它的大小進行相關配置而對於內部緩存Hibernate就采取了放任自流的態度了對它的容量並沒有限制現在症結找到了我們做海量數據插入的時候生成這麼多的對象就會被納入內部緩存(內部緩存是在內存中做緩存的)這樣你的系統內存就會一點一點的被蠶食如果最後系統被擠炸了也就在情理之中了
我們想想如何較好的處理這個問題呢?有的開發條件又必須使用Hibernate來處理當然有的項目比較靈活可以去尋求其他的方法
筆者在這裡推薦兩種方法()優化Hibernate程序上采用分段插入及時清除緩存的方法
()繞過Hibernate API 直接通過 JDBC API 來做批量插入這個方法性能上是最 好的也是最快的
對於上述中的方法其基本是思路為優化Hibernate在配置文件中設置hibernatejdbcbatch_size參數來指定每次提交SQL的數量程序上采用分段插入及時清除緩存的方法(Session實現了異步writebehind它允許Hibernate顯式地寫操作的批處理)也就是每插入一定量的數據後及時的把它們從內部緩存中清除掉釋放占用的內存
設置hibernatejdbcbatch_size參數可參考如下配置
<hibernateconfiguration> <sessionfactory>……
<property name= hibernatejdbcbatch_size></property>……
<sessionfactory> <hibernateconfiguration>
配置hibernatejdbcbatch_size參數的原因就是盡量少讀數據庫hibernatejdbcbatch_size參數值越大讀數據庫的次數越少速度越快從上面的配置可以看出Hibernate是等到程序積累到了個SQL之後再批量提交
筆者也在想hibernatejdbcbatch_size參數值也可能不是設置得越大越好從性能角度上講還有待商榷這要考慮實際情況酌情設置一般情形設置就可以滿足需求了
程序實現方面筆者以插入條數據為例子如
Session session=HibernateUtilcurrentSession()
Transatcion tx=sessionbeginTransaction()
for(int i=i<i++)
{
Student st=new Student()
stsetName(feifei)
sessionsave(st)
if(i%==) //以每個數據作為一個處理單元
{
sessionflush() //保持與數據庫數據的同步
sessionclear() //清除內部緩存的全部數據及時釋放出占用的內存
}
}
mit()
……
在一定的數據規模下這種做法可以把系統內存資源維持在一個相對穩定的范圍
注意前面提到二級緩存筆者在這裡有必要再提一下如果啟用了二級緩存從機制上講Hibernate為了維護二級緩存我們在做插入更新刪除操作時Hibernate都會往二級緩存充入相應的數據性能上就會有很大損失所以筆者建議在批處理情況下禁用二級緩存
對於方法采用傳統的JDBC的批處理使用JDBC API來處理
些方法請參照java 批處理自執行SQL
看看上面的代碼是不是總覺得有不妥的地方?對沒發現麼!這還是JDBC的傳統編程沒有一點Hibernate味道
可以對以上的代碼修改成下面這樣
Transaction tx=sessionbeginTransaction() //使用Hibernate事務處理
邊界Connection conn=nnection()
PrepareStatement stmt=connprepareStatement(insert into T_STUDENT(name) values(?))
for(int j=j++j<){
for(int i=i++j<)
{
stmtsetString(feifei)
}
}
stmtexecuteUpdate()
mit() //使用 Hibernate事務處理邊界
……
這樣改動就很有Hibernate的味道了筆者經過測試采用JDBC API來做批量處理性能上比使用Hibernate API要高將近倍性能上JDBC 占優這是無疑的
批量更新與刪除
Hibernate中對於批量更新操作Hibernate是將符合要求的數據查出來然後再做更新操作批量刪除也是這樣先把符合條件的數據查出來然後再做刪除操作
這樣有兩個大缺點()占用大量的內存
()處理海量數據的時候執行update/delete語句就是海量了而且一條update/delete語句只能操作一個對象這樣頻繁的操作數據庫性能低下應該是可想而知的了
Hibernate 發布後對批量更新/刪除操作引入了bulk update/delete其原理就是通過一條HQL語句完成批量更新/刪除操作很類似JDBC的批量更新/刪除操作在性能上比Hibernate的批量更新/刪除有很大的提升
Transaction tx=sessionbeginSession()
String HQL=delete STUDENT
Query query=sessioncreateQuery(HQL)
int size=queryexecuteUpdate()
mit()
……
控制台輸出了也就一條刪除語句Hibernatedelete from T_STUDENT語句執行少了性能上也與使用JDBC相差無幾是一個提升性能很好的方法當然為了有更好的性能筆者建議批量更新與刪除操作還是使用JDBC方法以及基本的知識點與上面的批量插入方法基本相同這裡就不在冗述
筆者這裡再提供一個方法就是從數據庫端來考慮提升性能在Hibernate程序端調用存儲過程存儲過程在數據庫端運行速度更快以批量更新為例給出參考代碼
首先在數據庫端建立名為batchUpdateStudent存儲過程
create or replace produre batchUpdateStudent(a in number) as
begin
update STUDENT set AGE=AGE+ where AGE>a
end
調用代碼如下
Transaction tx=sessionbeginSession()
Connection conn=nnection()
String pd=……{call batchUpdateStudent(?)}
CallableStatement cstmt=connPrepareCall(pd)
cstmtsetInt() //把年齡這個參數設為
mit()
觀察上面的代碼也是繞過Hibernate API使用 JDBC API來調用存儲過程使用的還是Hibernate的事務邊界存儲過程無疑是提高批量處理性能的一個好方法直接運行與數據庫端某種程度上講把批處理的壓力轉接給了數據庫
三編後語
本文探討了Hibernate的批處理操作出發點都是在提高性能上考慮了也只是提供了提升性能的一個小方面
不管采取什麼樣的方法來提升性能都要根據實際的情況來考慮為用戶提供一個滿足需求的而且高效穩定的系統才是重中之中
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28885.html