|
|
使用Hibernate將 100 000 條記錄插入到數據庫的一個很天然的作法多是這樣的 html
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
若是要將不少對象持久化,你必須經過常常的調用 flush() 以及稍後調用 clear() 來控制第一級緩存的大小。 session
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();
此方法一樣適用於檢索和更新數據。此外,在進行會返回不少行數據的查詢時, 你須要使用 scroll() 方法以便充分利用服務器端遊標所帶來的好處。 app
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();
就像已經討論的那樣,自動和透明的 對象/關係 映射(object/relational mapping)關注於管理對象的狀態。 這就意味着對象的狀態存在於內存,所以直接更新或者刪除 (使用 SQL 語句UPDATE 和 DELETE) 數據庫中的數據將不會影響內存中的對象狀態和對象數據。 不過,Hibernate提供經過Hibernate查詢語言(第 14 章 HQL: Hibernate查詢語言)來執行大批 量SQL風格的(UPDATE)和(DELETE) 語句的方法。 性能
UPDATE 和 DELETE語句的語法爲: ( UPDATE | DELETE ) FROM? ClassName (WHERE WHERE_CONDITIONS)?。 有幾點說明: spa
在FROM子句(from-clause)中,FROM關鍵字是可選的 hibernate
在FROM子句(from-clause)中只能有一個類名,而且它不能有別名 code
不能在大批量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() 方法 (此方法是爲 那些熟悉JDBC PreparedStatement.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映射方式的子類的表。
注意,上述大批量HQL操做的少數限制會在新版本中獲得改進;進一步詳細信息請參考JIRA裏的路線圖(roadmap)。