Batch Processing


 下面例子展現批量插入一個反模式(不成熟使用Hibernate插入100000行記錄)
 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();
 此操做在大多數系統執行約50000行時獲得OutOfMemoryException而失敗,由於Hibernate緩存全部新插入Customer在會話級緩存實例。
 有幾種方法能夠避免這個問題。
 
 在Batch Processing前啓用JDBC batching,要啓用JDBCbatching,設置屬性hibernate.jdbc.batch_size爲一個整數10至50。
 注:若是你使用一個標識符生成器,在JDBC級別透明Hibernate禁用insert batching。
 
 若是上面的方法是不合適的,你能夠禁用二級緩存:hibernate.cache.use_second_level_cache = false
 
 一、Batch inserts
  當使新的對象持久化時,按期採用Session的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
    //flush a batch of inserts and release memory:
    session.flush();
    session.clear();
   }
  }
  tx.commit();
  session.close();
  
 二、Batch updates
  當遍歷和更新數據,按期使用flush和clear方法,另外,使用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();
 
 三、StatelessSession
  StatelessSession是Hibernate提供一個面向命令API。用它來流化數據且從數據庫detached對象的形式。
  StatelessSession沒有與之關聯的持久化上下文且不提供許多更高級的生命週期的語義,
  StatelessSession不提供的特性和行爲:
   a first-level cache
   interaction with any second-level or query cache
   transactional write-behind or automatic dirty checking
   
  StatelessSession的限制: 
   Operations performed using a stateless session never cascade to associated instances
   Collections are ignored by a stateless session
   Operations performed via a stateless session bypass Hibernate's event model and interceptors
   Due to the lack of a first-level cache, Stateless sessions are vulnerable to data aliasing effects
   A stateless session is a lower-level abstraction that is much closer to the underlying JDBC
  
  使用StatelessSession例子:
  StatelessSession session = sessionFactory.openStatelessSession();
  Transaction tx = session.beginTransaction();
  
  ScrollableResults customers = session.getNamedQuery("GetCustomers")
         .scroll(ScrollMode.FORWARD_ONLY);
  while ( customers.next() ) {
   Customer customer = (Customer) customers.get(0);
   customer.updateStuff(...);
   session.update(customer);
  }
  
  tx.commit();
  session.close();
  
  Customer 的實例經過查詢立刻detached狀態返回,沒有與任何持久上下關聯。
  StatelessSession接口中的insert、update、delete直接操做數據庫的行,引發相應的sql立刻執行。
  與Session接口中的save、saveOrUpdate、delete語意不一樣。
  
 四、Hibernate Query Language for DML
  DML指的是SQL語句如INSERT, UPDATE, 和 DELETE。Hibernate 提供大部分SQL語句執行的方法(HQL)
  
  HQL for UPDATE and DELETE
   ( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?
   ?後綴:可選參數,上面FROM和WHERE從句是可選的。
   
   FROM從句只能引用一個單一的實體(能夠別名),若是實體名稱的別名,任何屬性引用也限制使用別名
   隱式or顯式鏈接在HQL大部分是禁止的。你能夠在WHERE從句中使用子查詢,且子查詢自己能夠包含鏈接。
   Session session = sessionFactory.openSession();
   Transaction tx = session.beginTransaction();
   String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
   // or String hqlUpdate = "update Customer set name = :newName where name = :oldName";
   int updatedEntities = session.createQuery( hqlUpdate )
        .setString( "newName", newName )
        .setString( "oldName", oldName )
        .executeUpdate();
   
   tx.commit();
   session.close();
   
   爲符合EJB3的規範,默認的,HQL UPDATE語句,不影響版本或時間戳屬性值對受影響的實體。
   您可使用一個版本更新迫使Hibernate重置版本或時間戳屬性值,經過在UPDATE關鍵字後添加VERSIONED關鍵字:
   Session session = sessionFactory.openSession();
   Transaction tx = session.beginTransaction();
   String hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
   int updatedEntities = session.createQuery( hqlVersionedUpdate )
        .setString( "newName", newName )
        .setString( "oldName", oldName )
        .executeUpdate();
   
   tx.commit();
   session.close();
   
   注:若使用VERSIONED語句,不能使用自定義的版本類型,而使用 org.hibernate.usertype.UserVersionType類型
   HQL delete statement:
   Session session = sessionFactory.openSession();
   Transaction tx = session.beginTransaction();
   String hqlDelete = "delete Customer c where c.name = :oldName";
   // or String hqlDelete = "delete Customer where name = :oldName";
   int updatedEntities = session.createQuery( hqlDelete )
         .setString( "oldName", oldName )
        .executeUpdate();
   
   tx.commit();
   session.close();   
   
   Query.executeUpdate()方法返回int值,表示此操做影響實體的數量。這可能會或可能不會與數據庫中的影響的行數。
   HQL bulk操做可能致使多個SQL語句被執行,如joined-subclass
  
  HQL syntax for INSERT
   INSERT INTO EntityName properties_list select_statement
   僅僅支持INSERT INTO ... SELECT ... 格式,不能指定明確值insert。
   properties_list :相似於SQL INSERT語句中的列規範,關於實體涉及的繼承映射,只能直接使用直接類級別上的屬性,
       父類的屬性不被容許且子類屬性不相關。換言之,INSERT語句原本是非多態的。
       id不須要再properties_list中指定,Hibernate自動生成一個值。
       自動生成只有你使用ID生成器操做數據庫,不然,在解析Hibernate拋出一個異常。
       可用的數據庫生成器:org.hibernate.id.SequenceGenerator、其子類和實現org.hibernate.id.PostInsertIdentifierGenerator類
       
   select_statement:任何有效的HQL查詢,但返回類型必需與INSERT指望的匹配。Hibernate驗證返回類型在編譯查詢,而不是期待數據庫檢查。
       問題可能來自Hibernate類型是等價的,而不是平等的。如:一個屬性類型org.hibernate.type.DateType和一個org.hibernate.type.TimestampType類型,
       儘管數據庫可能沒有區別,或者可以處理的轉換。
  
   屬性映射爲版本或時間戳,insert語句給你兩個選擇。properties_list您能夠指定屬性,在這種狀況下,它的價值來自相應的選擇表達式,或從properties_list省略它,
   在這種狀況下,值由org.hibernate.type.VersionType使用
   Session session = sessionFactory.openSession();
   Transaction tx = session.beginTransaction();
   String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
   int createdEntities = session.createQuery( hqlInsert )
        .executeUpdate();
   tx.commit();
   session.close();
sql

相關文章
相關標籤/搜索