Hibernateアプリケーションでの一括操作


Hibernateアプリケーションでの一括操作
最近のプロジェクトでは、クエリーを作成するときに大量にデータを更新し、クエリーを行う必要があります.
Hiberanteアプリケーションでは、この更新操作は
一、session.update(object)
1つの方法は、条件に基づいてlistをロードすることです.条件に合致するオブジェクトが1万以上ある場合、sessinキャッシュに複数のオブジェクトがロードされます.
次に、各オブジェクトを1つずつ更新します.トランザクションコミットがキャッシュをクリーンアップし、1万個以上のupdate文を実行する場合
tx = session.beginTransaction();
Iterator objects =session.find("from Project where p.id>0").iterator();
while(objects .hasNext()){
Object object=(Object)objects .next();
object.setXXX();
} 
tx.commit();
session.close();

以上の一括更新方式には2つの欠点があります.
(1)大量のメモリを使用する場合、条件を満たすすべてのオブジェクトをメモリにロードしてから、一つ一つ更新する必要があります.
(2)実行されるupdate文の数が多すぎて、update文ごとに1つのCustomerオブジェクトしか更新できず、頻繁にデータベースにアクセスすると、アプリケーションのパフォーマンスが低下します.
üオブジェクトが使用するメモリを迅速に解放するために、各オブジェクトを更新した後、Sessionのevict()メソッドを呼び出して、すぐにそのメモリを解放することができる.

tx = session.beginTransaction();
Iterator objects=session.find(“hql").iterator();
while(objects.hasNext()){
Object  object =(Object) objects.next();
object.setXXX();
session.flush();
session.evict(customer);
} 
tx.commit();
session.close();

flush()メソッドは、Hibernateが直ちにこのobjectオブジェクトの状態変化に従ってデータベースを同期的に更新し、関連するupdate文を直ちに実行する.evict()メソッドは、このobjectオブジェクトをキャッシュから消去し、使用したメモリをタイムリーに解放するために使用されます.
パフォーマンスを少し向上させることができますが、バッチ操作に影響を与える重要な要素として、数万個のupdate文を生成します.
二、直接sqlを実行する
hibernateがupdate文を実行できる場合、条件に合致するオブジェクトは一度に更新されます.
しかし、Hibernateはこのようなupdate文を実行するインタフェースを直接提供していない.アプリケーションはHibernate APIを迂回し、JDBC APIを介してSQL文を直接実行する必要があります.

tx = session.beginTransaction();
Connection con=session.connection();
PreparedStatement stmt=con.prepareStatement(sql");
stmt.executeUpdate();
tx.commit();

以上のプログラムでは,Hibernate APIを迂回してJDBC APIを介して直接データベースにアクセスする手順を示した.アプリケーションは、Sessionのconnection()メソッドで使用するデータベース接続を取得し、PreparedStatementオブジェクトを作成してSQL文を実行します.なお、アプリケーションはHibernateのTransactionインタフェースを介してトランザクション境界を宣言しています.
三、ストレージプロセスの使用
Oracleなどの最下位データベースがストレージ・プロシージャをサポートしている場合は、ストレージ・プロシージャを使用して一括更新を実行することもできます.ストレージ・プロシージャはデータベースで直接実行され、高速化されます.

create or replace procedure updateProject is
begin
  update project p set p.total_intend_gather = 
  (select sum(ig.gather_sum) from intend_gather ig where ig.project_number=p.project_number);
  
  update project p set p.total_actual_gather = 
  (select sum(ag.gahter_sum) from actual_gather ag where ag.project_number=p.project_number);
  
  update project p set p.total_invoice=
(select sum(invoice.invoice_sum) from invoice invoice
 where invoice.intend_id in
 (select ig.intend_id  from intend_gather ig where ig.project_number=p.project_number));
 
end updateProject;

コールコード

        Session session = this.getSession();
        Transaction tx =null;
        try {
            tx = session.beginTransaction();
            Connection con = session.connection();
            String procedure = "{call updateproject() }";
            CallableStatement cstmt = con.prepareCall(procedure);
            cstmt.executeUpdate();
            tx.commit();

        } catch (Exception e) {
           tx.rollback();
        }

上記のプログラムから、アプリケーションもHibernate APIを迂回して、JDBC APIを直接介してストレージ・プロシージャを呼び出す必要があることがわかります.
四、delete操作
Sessionの様々なリロード形式のupdate()メソッドは、一度に1つのオブジェクトしか更新できませんが、delete()メソッドの一部のリロード形式では、HQL文をパラメータとして使用できます.たとえば、次のようになります.
session.delete(from Project where p.id>0);

一括削除は可能ですが、Sessionのdelete()メソッドは次のdelete文を実行していません.
delete from PROJECT where ID>0;

ではなく
Sessionのdelete()メソッドは、次のselect文を使用して、一致するすべてのオブジェクトをメモリにロードします.
select * from Project where ID>0;

次に、N複数のdelete文を実行し、プロジェクトオブジェクトを1つずつ削除します.
delete from PROJECT where ID=i;
delete from PROJECT where ID=j;
delete from PROJECT where ID=k;

したがって,直接Hibernate APIによる一括更新も一括削除も不快である.一方、JDBC APIを直接介して関連するSQL文を実行したり、関連するストレージ・プロシージャを呼び出したりすることは、一括更新と一括削除の最適な方法であり、この2つの方法には以下の利点があります.
(1)データベース内の大量のデータを先にメモリにロードしてから、1つずつ更新したり修正したりする必要がないため、大量のメモリを消費することはありません.
(2)1つのSQL文で大量のデータを更新または削除することができる.