程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Hibernate批量處置海量數據的辦法

Hibernate批量處置海量數據的辦法

編輯:關於JAVA

Hibernate批量處置海量數據的辦法。本站提示廣大學習愛好者:(Hibernate批量處置海量數據的辦法)文章只能為提供參考,不一定能成為您想要的結果。以下是Hibernate批量處置海量數據的辦法正文


本文實例講述了Hibernate批量處置海量數據的辦法。分享給年夜家供年夜家參考,詳細以下:

Hibernate批量處置海量其實從機能上斟酌,它是很弗成取的,糟蹋了很年夜的內存。從它的機制上講,Hibernate它是先把相符前提的數據查出來,放到內存傍邊,然後再停止操作。現實應用上去機能異常不睬想,在筆者的現實應用中采取上面的第三種優化計劃的數據是:100000條數據拔出數據庫, 須要約30分鐘,呵呵,暈倒。(自己10分鐘拔出1000000條數據(字段比擬小))

總結上去有三種來處置以處理機能成績:

1:繞過Hibernate API ,直接經由過程 JDBC API 來做,這個辦法機能上是比擬好的。也是最快的。

2:應用存儲進程。

3:照樣用Hibernate API 來停止慣例的批量處置,可以也有變,變就變在,我們可以在查找出必定的量的時刻,實時的將這些數據做完操作就 刪失落,session.flush();session.evict(XX對象集); 如許也能夠搶救一點機能喪失。這個"必定的量"要就要依據現實情形做定量參考了。普通為30-60閣下,但後果依然不睬想。

1:繞過Hibernate API ,直接經由過程 JDBC API 來做,這個辦法機能上是比擬好的,也是最快的。(實例為 更新操作)

Transaction tx=session.beginTransaction(); //留意用的是hibernate事務處置界限
Connection conn=session.connection();
PreparedStatement stmt=conn.preparedStatement("update CUSTOMER as C set C.sarlary=c.sarlary+1 where c.sarlary>1000");
stmt.excuteUpdate();
tx.commit(); //留意用的是hibernate事務處置界限

這小法式中,采取的是直接挪用JDBC 的API 來拜訪數據庫,效力很高。防止了Hibernate 先查詢出來加載到內存,再停止操作激發的機能成績

2:應用存儲進程。但這類方法斟酌到易植和法式安排的便利性,不建議應用。(實例為 更新操作)

假如底層數據庫(如Oracle)支撐存儲進程,也能夠經由過程存儲進程來履行批量更新。存儲進程直接在數據庫中運轉,速度加倍快。在Oracle數據庫中可以界說一個名為batchUpdateCustomer()的存儲進程,代碼以下:
create or replace procedure batchUpdateCustomer(p_age in number) as begin update CUSTOMERS set AGE=AGE+1 where AGE>p_age;end;
以上存儲進程有一個參數p_age,代表客戶的年紀,運用法式可依照以下方法挪用存儲進程:

tx = session.beginTransaction();
Connection con=session.connection();
String procedure = "{call batchUpdateCustomer(?) }";
CallableStatement cstmt = con.prepareCall(procedure);
cstmt.setInt(1,0); //把年紀參數設為0
cstmt.executeUpdate();
tx.commit();

從下面法式看出,運用法式也必需繞過Hibernate API,直接經由過程JDBC API來挪用存儲進程。

3:照樣用Hibernate API 來停止慣例的批量處置,可以也有變,變就變在,我們可以在查找出必定的量的時刻,實時的將這些數據做完操作就刪失落,session.flush();session.evict(XX對象集); 如許也能夠搶救一點機能喪失。這個"必定的量"要就要依據現實情形做定量參考了……
(實例為 保留操作)

營業邏輯為:我們要想數據庫拔出10 0000 條數據

tx=session.beginTransaction();
for(int i=0;i<100000;i++)
{
Customer custom=new Customer();
custom.setName("user"+i);
session.save(custom);
if(i%50==0) // 以每50個數據作為一個處置單位,也就是我下面說的"必定的量",這個量是要酌情斟酌的
{
session.flush();
session.clear();
}
}

如許可以把體系保持在一個穩固的規模……

在項目標開辟進程當中,因為項目需求,我們經常須要把年夜批量的數據拔出到數據庫。數目級有萬級、十萬級、百萬級、乃至萬萬級其余。如斯數目級其余數據用Hibernate做拔出操作,便可能會產生異常,罕見的異常是OutOfMemoryError(內存溢出異常)。

起首,我們簡略往返顧一下Hibernate拔出操作的機制。Hibernate要對它外部緩存停止保護,當我們履行拔出操作時,就會把要操作的對象全體放到本身的外部緩存來停止治理。

談到Hibernate的緩存,Hibernate有外部緩存與二級緩存之說。因為Hibernate對這兩種緩存有著分歧的治理機制,關於二級緩存,我們可以對它的年夜小停止相干設置裝備擺設,而關於外部緩存,Hibernate就采用了"任其自然"的立場了,對它的容量並沒無限制。如今關鍵找到了,我們做海量數據拔出的時刻,生成這麼多的對象就會被歸入外部緩存(外部緩存是在內存中做緩存的),如許你的體系內存就會一點一點的被蠶食,假如最初體系被擠"炸"了,也就在道理當中了。

我們想一想若何較好的處置這個成績呢?有的開辟前提又必需應用Hibernate來處置,固然有的項目比擬靈巧,可以去追求其他的辦法。

筆者在這裡推舉兩種辦法:

(1):優化Hibernate,法式上采取分段拔出實時消除緩存的辦法。
(2):繞過Hibernate API ,直接經由過程 JDBC API 來做批量拔出,這個辦法機能上是最 好的,也是最快的。

關於上述中的辦法1,其根本是思緒為:優化Hibernate,在設置裝備擺設文件中設置hibernate.jdbc.batch_size參數,來指定每次提交SQL的數目;法式上采取分段拔出實時消除緩存的辦法(Session完成了異步write-behind,它許可Hibernate顯式地寫操作的批處置),也就是每拔出必定量的數據後實時的把它們從外部緩存中消除失落,釋放占用的內存。

設置hibernate.jdbc.batch_size參數,可參考以下設置裝備擺設。

<hibernate-configuration> <session-factory>……
<property name=" hibernate.jdbc.batch_size">50</property>……
<session-factory> <hibernate-configuration>

設置裝備擺設hibernate.jdbc.batch_size參數的緣由就是盡可能少讀數據庫,hibernate.jdbc.batch_size參數值越年夜,讀數據庫的次數越少,速度越快。從下面的設置裝備擺設可以看出,Hibernate是比及法式積聚到了50個SQL以後再批量提交。

筆者也在想,hibernate.jdbc.batch_size參數值也能夠不是設置得越年夜越好,從機能角度上講還有待商議。這要斟酌現實情形,酌情設置,普通情況設置30、50便可以知足需求了。

法式完成方面,筆者以拔出10000條數據為例子,如

Session session=HibernateUtil.currentSession();
Transatcion tx=session.beginTransaction();
for(int i=0;i<10000;i++)
{
Student st=new Student();
st.setName("feifei");
session.save(st);
if(i%50==0) //以每50個數據作為一個處置單位
{
session.flush(); //堅持與數據庫數據的同步
session.clear(); //消除外部緩存的全體數據,實時釋放出占用的內存
}
}
tx.commit();
……

在必定的數據范圍下,這類做法可以把體系內存資本保持在一個絕對穩固的規模。

留意:後面提到二級緩存,筆者在這裡有需要再提一下。假如啟用了二級緩存,從機制上講Hibernate為了保護二級緩存,我們在做拔出、更新、刪除操作時,Hibernate都邑往二級緩存充入響應的數據。機能上就會有很年夜喪失,所以筆者建議在批處置情形下禁用二級緩存。

關於辦法2,采取傳統的JDBC的批處置,應用JDBC API來處置。

些辦法請參照java 批處置自履行SQL

看看下面的代碼,是否是總認為有不當的處所?對,沒發明麼!這照樣JDBC的傳統編程,沒有一點Hibernate滋味。

可以對以上的代碼修正成上面如許:

Transaction tx=session.beginTransaction(); //應用Hibernate事務處置
Connection conn=session.connection();
PrepareStatement stmt=conn.prepareStatement("insert into T_STUDENT(name) values(?)");
for(int j=0;j++;j<200){
for(int i=0;i++;j<50)
{
stmt.setString(1,"feifei");
}
}
stmt.executeUpdate();
tx.commit(); //應用 Hibernate事務處置界限
……

如許修改就很有Hibernate的滋味了。筆者經由測試,采取JDBC API來做批量處置,機能上比應用Hibernate API要高快要10倍,機能上JDBC 占優這是無疑的。

批量更新與刪除Hibernate2中,關於批量更新操作,Hibernate是將相符請求的數據查出來,然後再做更新操作。批量刪除也是如許,先把相符前提的數據查出來,然後再做刪除操作。

如許有兩個年夜缺陷:

(1):占用年夜量的內存。
(2):處置海量數據的時刻,履行update/delete語句就是海量了,並且一條update/delete語句只能操作一個對象,如許頻仍的操作數據庫,機能低下應當是可想而知的了。

Hibernate3 宣布後,對批量更新/刪除操作引入了bulk update/delete,其道理就是經由過程一條HQL語句完成批量更新/刪除操作,很相似JDBC的批量更新/刪除操作。在機能上,比Hibernate2的批量更新/刪除有很年夜的晉升。

Transaction tx=session.beginSession();
String HQL="delete STUDENT";
Query query=session.createQuery(HQL);
int size=query.executeUpdate();
tx.commit();
……

掌握台輸入了也就一條刪除語句Hibernate:delete from T_STUDENT,語句履行少了,機能上也與應用JDBC相差無幾,是一個晉升機能很好的辦法。固然為了有更好的機能,筆者建議批量更新與刪除操作照樣應用JDBC,辦法和根本的常識點與下面的批量拔出辦法2根本雷同,這裡就不在冗述。

筆者這裡再供給一個辦法,就是從數據庫端來斟酌晉升機能,在Hibernate法式端挪用存儲進程。存儲進程在數據庫端運轉,速度更快。以批量更新為例,給出參考代碼。

起首在數據庫端樹立名為batchUpdateStudent存儲進程:

create or replace produre batchUpdateStudent(a in number) as
begin
update STUDENT set AGE=AGE+1 where AGE>a;
end;

挪用代碼以下:

Transaction tx=session.beginSession();
Connection conn=session.connection();
String pd="……{call batchUpdateStudent(?)}";
CallableStatement cstmt=conn.PrepareCall(pd);
cstmt.setInt(1,20); //把年紀這個參數設為20
tx.commit();

不雅察下面的代碼,也是繞過Hibernate API,應用 JDBC API來挪用存儲進程,應用的照樣Hibernate的事務界限。存儲進程無疑是進步批量處置機能的一個好辦法,直接運轉與數據庫端,某種水平上講把批處置的壓力轉接給了數據庫。

編後語

本文商量了Hibernate的批處置操作,動身點都是在進步機能上斟酌了,也只是供給了晉升機能的一個小方面。

不論采用甚麼樣的辦法,來晉升機能都要依據現實的情形來斟酌,為用戶供給一個知足需求的並且高效穩固的體系才是重中當中。

願望本文所述對年夜家Hibernate法式設計有所贊助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved