1. 程式人生 > >spring+hibernate 大批量處理資料

spring+hibernate 大批量處理資料

  關於使用spring+hibernate進行大批量資料的插入和更新,它的效能和使用JDBC PreparedStatement的batch批量操作以及資料庫的儲存過程操作幾乎可以一樣高。在Hibernate的官方文件裡說到了BatchprocessingSpring+Hibernate大批量處理資料想要說明如何使用Hibernate大批量處理資料獲得高效能。

使用Hibernate將 100 000 條記錄插入到資料庫的一個很自然的做法可能是這樣的

Session session = sessionFactory.openSession();  
Transaction tx = session.beginTransaction();  
for ( int i=0; i<100000; i++ ) {      
  Customer customer = new Customer(.....);      
  session.save(customer);  
}  
tx.commit();  
session.close();

這段程式大概執行到 50 000條記錄左右會失敗並丟擲 記憶體溢位異常(OutOfMemoryException) 。這是因為 Hibernate 把所有新插入的 客戶(Customer)例項在session級別的快取區進行了快取的緣故。

我們會在本章告訴你如何避免此類問題。首先,如果你要執行批量處理並且想要達到一個理想的效能,那麼使用JDBC的批量(batching)功能是至關重要。將JDBC的批量抓取數量(batch size)引數設定到一個合適值(比如,10-50之間):

hibernate.jdbc.batch_size 20

你也可能想在執行批量處理時關閉二級快取:

hibernate.cache.use_second_level_cache false

14.1. 批量插入(Batch inserts)

如果要將很多物件持久化,你必須通過經常的呼叫 flush() 以及稍後呼叫 clear() 來控制第一級快取的大小。

Session session = sessionFactory.openSession();  
Transaction tx = session.beginTransaction();
    for ( int i=0; i<100000; i++ ) {
       Customer customer = new Customer(.....);
         session.save(customer);
         if ( i % 20 == 0 ) {
   //20, same as the JDBC batch size
   //20,與JDBC批量設定相同
          //flush a batch of inserts and release memory:
          //將本批插入的物件立即寫入資料庫並釋放記憶體
          session.flush();
          session.clear();
        }
   }
tx.commit();
session.close();

14.2. 批量更新(Batch updates)

此方法同樣適用於檢索和更新資料。此外,在進行會返回很多行資料的查詢時,你需要使用 scroll() 方法以便充分利用伺服器端遊標所帶來的好處。

Session session = sessionFactory.openSession();  
Transaction tx = session.beginTransaction();       
ScrollableResults customers = session.getNamedQuery("GetCustomers").setCacheMode(CacheMode.IGNORE).scroll(ScrollMode.FORWARD_ONLY);  int count=0;  
while ( customers.next() ) {
      Customer customer = (Customer) customers.get(0);
      customer.updateStuff(...);
      if ( ++count % 20 == 0 ) {
 //flush a batch of updates and release memory:
          session.flush();
          session.clear();
      }
  }
tx.commit();  
session.close();

14.3. 大批量更新/刪除(Bulk update/delete)

就像已經討論的那樣,自動和透明的 物件/關係 對映(object/relational mapping)關注於管理物件的狀態。這就意味著物件的狀態存在於記憶體,因此直接更新或者刪除 (使用 SQL語句 UPDATE 和 DELETE)資料庫中的資料將不會影響記憶體中的物件狀態和物件資料。 不過,Hibernate提供通過Hibernate查詢語言(第 15 章 HQL:Hibernate查詢語言)來執行大批量SQL風格的(UPDATE)和(DELETE) 語句的方法。

UPDATE 和 DELETE語句的語法為: (UPDATE | DELETE ) FROM? ClassName (WHERE WHERE_CONDITIONS)?。有幾點說明:

  • 在FROM子句(from-clause)中,FROM關鍵字是可選的

  • 在FROM子句(from-clause)中只能有一個類名,並且它不能有別名

  • 不能在大批量HQL語句中使用連線(顯式或者隱式的都不行)。不過在WHERE子句中可以使用子查詢。

  • 整個WHERE子句是可選的。

舉個例子,使用Query.executeUpdate()方法執行一個HQL UPDATE語句:

Session session = sessionFactory.openSession();          
Transaction tx = session.beginTransaction();            
String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate ).setString( "newName", newName ).setString( "oldName", oldName ).executeUpdate();
tx.commit();
session.close();

執行一個HQL DELETE,同樣使用 Query.executeUpdate() 方法(此方法是為那些熟悉JDBCPreparedStatement.executeUpdate() 的人們而設定的)

Session session = sessionFactory.openSession();          
Transaction tx = session.beginTransaction();            
String hqlDelete = "delete Customer where name = :oldName";          
int deletedEntities = s.createQuery( hqlDelete ).setString( "oldName", oldName ) .executeUpdate();          
tx.commit();          
session.close();

Query.executeUpdate()方法返回的整型值表明了受此操作影響的記錄數量。注意這個數值可能與資料庫中被(最後一條SQL語句)影響了的“行”數有關,也可能沒有。一個大批量HQL操作可能導致多條實際的SQL語句被執行,舉個例子,對joined-subclass對映方式的類進行的此類操作。這個返回值代表了實際被語句影響了的記錄數量。在那個joined-subclass的例子中,對一個子類的刪除實際上可能不僅僅會刪除子類對映到的表而且會影響“根”表,還有可能影響與之有繼承關係的joined-subclass對映方式的子類的表。