ストアードとの格闘
先週末からPL/SQLで記述されたロジックをJavaに移植する作業を行っています。
PL/SQLの実行環境がない。PL/SQLをほとんど知らないという状態で、IN/OUTのデータのみを手がかりに作業を進めています。これって結構つらいです。実行結果が違った場合、デバッグできないし。。なんとか本日作業が終了したので、言語間の仕様の違い等気づいたというか泣かされたことを残しておきます。
PL/SQLのSUBSTRと=(代入)には、本当に泣かされました。
まずSUBSTRについてですが、
対応するJavaのAPIとして適当だと思われるのはStringクラスのsubstringになり、どちらも文字列抽出の機能をもつAPIになります。
1.引数の違い
substringの引数は1:対象文字列,2:startIndex,3:endIndexで0オリジンですが、
PL/SQLのSUBSTRの引数は、1:startIndex,2:抽出文字列長で1オリジンになります。
例)「ABCDEFG」に対して「CDE」を抽出する
Javaでは
new String("ABCDEFG").substring(2,5);となり
PL/SQLでは
SUBSTR('ABCDEFG',3,3);となります。
2.対象文字列範囲外の抽出についての動作の違い
対象文字列の範囲外のIndexを抽出条件として指定した場合、Javaでは
IndexOutOfBoundsExceptionが発生しますが、PL/SQLでは、抽出が可能な範囲内のみ
抽出を行い、正常終了します。
例)「ABCDEFG」に対して「Index5から10」の範囲にて文字列を抽出する Javaでは new String("ABCDEFG").substring(5,10);となりIndexOutOfBoundsExceptionが 発生します。 しかしPL/SQLでは SUBSTR('ABCDEFG',5,5);となりますが、戻り値として「EFG」が返ってきます。
あとPL/SQLのイコールによる代入の仕様について、こちらも記載しておきます。
これは例を見ていただいた方が早いです。
「ABCDEF」XYZを代入すると Javaでは String buffer = "ABCDEF"; buffer = "XYZ"とすると、bufferは「XYZ」となります。 しかしPL/SQLでは Char buffer = char(6); buffer = " "; buffer := 'XYZ'とすると、bufferは「XYZ 」となります。
今回は、上記2種類が混じってため、かなり時間がかかってしました。
あと、今回ストアードの処理仕様関して、仕様の溝を埋めるために、StoredWrapperクラスを実装しましたので、下記に記載します。
StoredWrapper.java
/* * Created on 2005/02/19 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ package test; /** * @author DancingSummper * * TODO To change the template for this generated type comment go to * Window - Preferences - Java - Code Style - Code Templates */ public class StoredWrapper { /** * ストアードのイコール代入は、代入元文字列長が代入先文字列長より * 小さい場合は、その差分は代入元の情報が残る。 * その処理をカバーするためのラッパーメソッド。 * @param src * @param dst * @return */ public static String equalInsert(String src, String dst) { if(dst == null || dst.length()==0){ return src; }else if(src == null ){ return dst; } if(src.length() > dst.length()){ return dst + src.substring(dst.length()); } return dst; } /** * ストアードのSUBSTRはIndexを越えた値にアクセスする際は、Exceptionを発生させず * Indexで指定された範囲内で有効な情報のみ返すという仕様のようです。 * プラスαで今回のプロジェクトでは、先にスペースでフォーマットされているので、 * Index範囲外がスペースのままであるという仕様になっています。 * 初期フォーマットがイコールの文字列挿入後も残っている仕様については、 * このクラスのequalInsertメソッドにて対応します。 * * @param data * @param beginIndex * @param len * @return */ public static String substr(String data, int beginIndex, int len){ // ストアードは1オリジンのため、0オリジンに変換します。 beginIndex = beginIndex - 1; // ストアードの第2引数は取得する文字列数のため、ここでendIndexに変換します。 int endIndex = beginIndex + len; try { return data.substring(beginIndex, endIndex); } catch (IndexOutOfBoundsException e) { // ここでExceptionを発生させず、Indexで指定された範囲の文字列のみ返すようにします。 StringBuffer defaultString=new StringBuffer(""); for(int i=0;i<endIndex-beginIndex;i++){ defaultString.append(singleModeSubstring(data, beginIndex+i, beginIndex+i+1)); } return defaultString.toString(); } } private static String singleModeSubstring(String data, int beginIndex, int endIndex) { try { return data.substring(beginIndex, endIndex); } catch (IndexOutOfBoundsException e) { return ""; } } }
お待たせしてすいませんでした。