次のコード・サンプルを使用して、異なるトランザクション・シナリオで異なるタイプのデータ・アクセス・クライアントの不整合な接続例外をプログラマチックに処理する方法について説明します。
アプリケーションがデータベース操作で不整合な接続例外を受け取る場合、 これは現在保留されている接続が有効ではなくなったことを示しています。 どのデータベース操作においても不整合な接続例外を入手する可能性はあ りますが、発行された不整合な接続例外を確かめるのに最も一般的なのは、 接続を検索した直後に、その接続を初めて使用するときです。 接続がプールされているため、プールからの検索直後の操作、 すなわちデータベースとの最初の通信が試行されるまで、データベースの障害は検出されません。 接続の失効がマークされるのは、障害が検出されたときだけです。 データベースにアクセスする各メソッドがプールから新しい接続を取得するときは、不整合な接続例外の発生頻度は低くなります。
不整合な接続例外の原因の多くは、 データベース・サーバーのネットワークにかかわる偶発的な問題です。 新規の接続を取得して操作を再試行すれば、エンド・ユーザーに例外が表示されることなく、 操作は正常に終了します。 場合によっては、再試行と再試行の間にわずかな待機時間を追加して、 データベース・サーバーのリカバリーの時間を長くすると有益な場合もあります。 ただし、データベースが延長時間の間にダウンする場合は、 アプリケーションが操作を無制限に再試行することがないようにしてください。
アプリケーションが操作を再試行するために新規接続を取得する場合は、 その前に元の接続が関係していたトランザクションをロールバックして、 新しいトランザクションを開始してください。 このアクションに関する詳細は、次の 2 つのカテゴリーに分類できます。
Bean 管理トランザクションを明示 的に開始したオブジェクトが、データベース操作で不整合な接続例外を受け取る場合は、 接続を閉じ、トランザクションをロールバックしてください。この時点で、アプリケーション開発者は、 新規のトランザクションを開始して、新規の接続を取得し、操作を再試行することを決定することができます。
//get a userTransaction javax.transaction.UserTransaction tran = getSessionContext().getUserTransaction(); //retry indicates whether to retry or not //numOfRetries states how many retries have // been attempted boolean retry = false; int numOfRetries = 0; java.sql.Connection conn = null; java.sql.Statement stmt = null; do { try { //begin a transaction tran.begin(); //Assumes that a datasource has already been obtained //from JNDI conn = ds.getConnection(); conn.setAutoCommit(false); stmt = conn.createStatement(); stmt.execute("INSERT INTO EMPLOYEES VALUES (0101, 'Bill', 'R', 'Smith')"); tran.commit(); retry = false; } catch(com.ibm.websphere.ce.cm.StaleConnectionException sce) { //if a StaleConnectionException is caught // rollback and retry the action try { tran.rollback(); } catch (java.lang.Exception e) { //deal with exception //in most cases, this can be ignored } if (numOfRetries < 2) { retry = true; numOfRetries++; } else { retry = false; } } catch (java.sql.SQLException sqle) { //deal with other database exception retry = false } finally { //always cleanup JDBC resources try { if(stmt != null) stmt.close(); } catch (java.sql.SQLException sqle) { //usually can ignore } try { if(conn != null) conn.close(); } catch (java.sql.SQLException sqle) { //usually can ignore } } } while (retry) ;
public class MyEJBClient { //... other methods here ... public void myEJBClientMethod() { MyEJB myEJB = myEJBHome.findByPrimaryKey("myEJB"); boolean retry = false; do { try { retry = false; myEJB.insertValue(); } catch(RetryableConnectionException retryable) { retry = true; } catch(Exception e) { /* handle some other problem */ } } while (retry); } } //end MyEJBClient public class MyEJB implements javax.ejb.EntityBean { //... other methods here ... public void insertValue() throws RetryableConnectionException, java.rmi.EJBException { try { conn = ds.getConnection(); stmt = conn.createStatement(); stmt.execute("INSERT INTO my_table VALUES (1)"); } catch(com.ibm.websphere.ce.cm.StaleConnectionException sce) { getSessionContext().setRollbackOnly(); throw new RetryableConnectionException(); } catch(java.sql.SQLException sqle) { //handle other database problem } finally { 21 //always cleanup JDBC resources try { if(stmt != null) stmt.close(); } catch (java.sql.SQLException sqle) { //usually can ignore } try { if(conn != null) conn.close(); } catch (java.sql.SQLException sqle) { //usually can ignore } } } } //end MyEJB
MyEJBClient は最初に MyEJB Bean をホーム・インターフェースから取得します。MyEJB Bean はあらかじめ Java Naming and Directory Interface (JNDI) から取得されているものとします。 次に MyEJB Bean は、Bean 上の insertValue() を呼び出します。Bean 上のこのメソッドは、接続を取得し、値を表に挿入しようとします。 これらのメソッドのいずれかが失敗して不整合な接続例外を発生した場合、 そのメソッドはトランザクションに rollbackOnly のマークを付けて (呼び出し元に、 このトランザクションのロールバックを強制します)、新たに 再試行可能接続例外を作成し、 例外がスローされる前にリソースをクリーンアップします。 再試行可能接続例外は、アプリケーション定義の例外にすぎず、 メソッドの再試行を呼び出し元に知らせます。 呼び出し元は、再試行接続例外をモニターして、 これをキャッチした場合にはメソッドを再試行します。 この例では、コンテナーがトランザクションの開始および終了を行うため、 クライアントまたはサーバーでのトランザクション管理は必要ありません。 もちろん、クライアントがトランザクションのコミットあるいはロールバックも実行していれば、 クライアントによって Bean 管理トランザクションが開始され、動作はこの後も同じとなります。
public class MySessionBean extends javax.ejb.SessionBean { ... other methods here ... public void mySessionBMTMethod() throws java.rmi.EJBException { javax.transaction.UserTransaction tran = getSessionContext().getUserTransaction(); boolean retry = false; do { try { retry = false; tran.begin(); // causes ejbLoad() to be invoked myBMPBean.myMethod(); // causes ejbStore() to be invoked tran.commit(); } catch(java.rmi.EJBException re) { try { tran.rollback(); } catch(Exception e) { //can ignore } if (causedByStaleConnection(re)) retry = true; else throw re; } catch(Exception e) { // handle some other problem } finally { //always cleanup JDBC resources try { if(stmt != null) stmt.close(); } catch (java.sql.SQLException sqle) { //usually can ignore } try { if(conn != null) conn.close(); } catch (java.sql.SQLException sqle) { //usually can ignore } } } while (retry); } public boolean causedByStaleConnection(java.rmi.EJBException EJBException) { java.rmi.EJBException re = EJBException; Throwable t = null; while (true) { t = re.getCause(); try { re = (java.rmi.EJBException)t; } catch(ClassCastException cce) { return (t instanceof com.ibm.websphere.ce.cm.StaleConnectionException); } } } } public class MyEntityBean extends javax.ejb.EntityBean { ... other methods here ... public void ejbStore() throws java.rmi.EJBException { try { conn = ds.getConnection(); stmt = conn.createStatement(); stmt.execute("UPDATE my_table SET value=1 WHERE primaryKey=" + myPrimaryKey); } catch(com.ibm.websphere.ce.cm.StaleConnectionException sce) { //always cleanup JDBC resources try { if(stmt != null) stmt.close(); } catch (java.sql.SQLException sqle) { //usually can ignore } try { if(conn != null) conn.close(); } catch (java.sql.SQLException sqle) { //usually can ignore } // rollback the tran when method returns getEntityContext().setRollbackOnly(); throw new java.rmi.EJBException("Exception occurred in ejbStore", sce); } catch(java.sql.SQLException sqle) { // handle some other problem } } }
ローカル・トランザクションが未指定のトランザクション・コンテキスト内で稼働するエンタープライズ Bean で発生すると、エンタープライズ Bean クライアント・オブジェクトは、ローカル・トランザクション内包の外側で、直前の黒丸で説明したメソッドを使用して、トランザクションを再試行します。ただし、ローカル・トランザクショ ン内包がサーブレットまたは JSP ファイルの一部として実行される場合に、操作を再試行できるクライアント・オブジェクトはありません。 このため、ユーザー・トランザクションに含まれる場合を除き、 サーブレットや JSP ファイルではデータベースの操作を実行しないほうがよいでしょう。