SQLによる例外処理
個人的には例外はキャッチしない方針なのですが、SQL関連の例外についてはそうも行きません。重複やNULL制約などは例外ではなくエラー扱いにしたいところです。
try { int cnt = IUserDao.Insert(user); } catch (SQLRuntimeException ex) { // SQLRuntimeExceptionはSeasar.NETの型なので局所化したい // OracleExceptionはODP.NETの型なので局所化したい OracleException oex = ex.InnerException; Trace.WriteLine (oex.Message); Trace.WriteLine (oex.Number); return false; }
たとえば、上記のような処理が入ります。例ではS2Dao.NETを使っているので、SQLRuntimeExceptionが発生します。更にOracleのエラーコードを取得しようとするとOracleExceptionにアクセスしなければなりません。もちろん、ラッパーを用意すればいいのですが、SQL毎にイチイチ書くのも面倒です。
折角、Seasar.NETを使うのだからAOPを利用してみます。
namespace My.Dao { public class SQLExceptionInterceptor : IMethodInterceptor { public object Invoke(IMethodInvocation invocation) { object ret = null; try { ret = invocation.Proceed(); } catch (SQLRuntimeException ex) { // SQLエラーの場合は、例外ではなく戻り値にする // 独自の例外クラスでOracleExceptionを隠蔽 ret = new SQLException(ex.Message, ex.InnerException); } return ret; } } }
インターセプタークラスを作成し、メソッド呼び出しに割り込んでSQL関連の例外を戻り値に変更しています。これをdiconファイルに登録します。
<component class="My.Dao.IUserDao"> <aspect>SQLExceptionInterceptor</aspect> <aspect>Ex.DaoInterceptor</aspect> </component>
登録順序はSQLExceptionInterceptor、DaoInterceptorの順。
こうすると、
object ret = IUserDao.Insert(user); if (ret is SQLException) { // SQLに関する例外は回復可能 SQLException sqlErr = ret as SQLException; // 以下、エラー処理 } // 成功した場合は、挿入した行数が戻る int cnt = (int)ret;
例外は飛ばないので、普通にエラー処理が行えます。
例外関連の扱いは苦手なので、もう少し良いやり方がありましたら教えてください。