iBATIS.NETからSQLを取得してDataAdapterで利用する方法

iBATIS.NETはiBATIS.NETのまま使うのが一番だと思うのですが、政治的な都合やら諸般の事情やらで表題のようなことを行ってみたので、覚え書き程度に書いておきます。以下は、iBATIS.NETを使う上であくまでイレギュラーなことですので、それをご承知の上でご覧下さい。

やりたいことは、SQLとDbCommandだけiBATIS.NETから取得して、SQLの発行はADO.NETのDataAdapterで行い、DataTableを取得するというものです。というわけで、以下のような段取りで行います。

  1. 動的なSQLについて、iBATIS.NETのSQLMapで作成したものから取得する。
  2. iBATIS.NETから取得したSQLと、パラメータとで、DbCommandを作成する。
  3. 作成したDbCommandをDataAdapterのSelectCommandに代入して、DataSetにFill()する。

これを実装したのが以下のコードです。

iBATIS.NETのソースコードに添付されているACCESSのMDBファイルに対してアクセスするようなコードを、NUnitで書いて動かしてみました。

[csharp]
// SQLMapの初期化
Assembly assembly = Assembly.GetExecutingAssembly();
Stream stream = assembly.GetManifestResourceStream(
"iBATISLabo.Config.SQLMap.config");
DomSqlMapBuilder builder = new DomSqlMapBuilder();
mapper = builder.Configure(stream);

// パラメータの作成
Hashtable entity = new Hashtable();
entity.Add("LastName", "Dalton");

// コネクション無しでSqlMapを作成する
ISqlMapSession session = new SqlMapSession(mapper);
// SQLをXMLから取得
IMappedStatement statement = mapper.GetMappedStatement("SelectAccountList");
// リクエストを作成する
RequestScope request = statement.Statement.Sql.GetRequestScope(statement, entity, session);
// DBCommandを作成する
statement.PreparedCommand.Create(request, session, statement.Statement, entity);

Debug.WriteLine("★ SQL取得 : " + request.PreparedStatement.PreparedSql);

// DataAdapterの作成とFillメソッドでのデータ取得

DataSet ds = new DataSet();
OleDbDataAdapter adpt =
new OleDbDataAdapter(request.IDbCommand.CommandText, mapper.DataSource.ConnectionString);
OleDbParameter orgParam = (OleDbParameter)request.IDbCommand.Parameters[0];
// DbParameterのコピー(そのままインスタンスをAddするだけだと例外がスローされる)
OleDbParameter newParam =
new OleDbParameter(orgParam.ParameterName, orgParam.OleDbType, orgParam.Size, orgParam.Direction, orgParam.IsNullable, orgParam.Precision, orgParam.Scale, orgParam.SourceColumn, orgParam.SourceVersion, orgParam.Value);
// パラメータの設定
adpt.SelectCommand.Parameters.Add(newParam);
// SELECT文の発行
adpt.Fill(ds);

Debug.WriteLine("★ DataTable 取得 : " + ds.Tables[0].Rows.Count + " 件");

// 取得したデータの出力
foreach (DataRow row in ds.Tables[0].Rows) {
StringBuilder stb = new StringBuilder();
foreach (object value in row.ItemArray) {
if (stb.Length > 0) stb.Append(" | ");
if (value != null) {
stb.Append(value.ToString());
}
}
Debug.WriteLine("★ 取得レコード : " + stb.ToString());
}
[/csharp]

まず13行目にあるように、SqlMapperSessionを作成します。ここでは特にコネクションをオープンする必要はなく、単純にSqlMapperSessionのインスタンスだけあれば大丈夫です。

次に15行目で、XMLファイルに記述されたSQLのMappedStatementを取得します。

さらに17行目でRequestScopというクラスのインスタンスを取得します。このクラスのメンバに、PreparedStatementや、DbCommandのプロパティがあり、これをパラメータとしてiBATIS内部では最終的なSQLが実行される仕組みになっています。

そして19行目でDBCommandを作成します。ここで、Dynamicタグの記述が展開されて、最終的なSQLやパラメータが作成されます。

25行目以降は、作成したSQLとDBCommandを使って、DataAdapterでSQLを発行しています。30、31行目でOleDbParameterを再度作り直しているのは、既にOleDbParameterCollectionにAddされているOleDbParameterを別のDbParameterCollectionにAddすると例外になるためです。
http://msdn.microsoft.com/ja-jp/library/50xtbfet(VS.80).aspx

.NETの場合は、DataSet/DataTable取り扱った方が、画面のコントロールなどに対してデータバインドするのにいろいろと都合がよいので、アーキテクチャによってはこの方法が役に立つかもしれません。

Zaurusハマり道

まあなんというか、仕事ではWindowsっ子なので、昔のささやかなUNIX上での開発経験と、プログラマとしての基礎教養程度でしかLinuxを理解していないわけですが、想像以上にZaurusでのRubyにはハマっておりまして・・・とっても楽しい通勤時間です(笑)。

勉強がてら、Ruby/Qteで、自分用のついったクライアントを作ろうとして、Rubyの勉強からはじめているのですが、

まずRubyGemsを導入
→ 様々なサイトの情報を頂き、なんとかおk

次にRubyGemsで、Twitter4rを導入
→ なんかおかしなエラー。

どうやらGemsはメモリ食いなので、–localで入れた方がいいらしいことを知り、それで導入
→ すると、「requires json >= 0.4.3」と言われる。

なので、それを落としてきて、jsonをGemsで導入
→ make コマンドがないよと言われる。

いろいろ調べて、swapファイルをつくり、dev_img-1.3を導入してmountして、HelloWorldのCのソースをザウルス上でコンパイルできることを確認して、さらにjsonをGemsでインストール
→ 今度は、/opt/Embedix/tools/bin/arm-linux-gcc: Command not found と言われる。

これってつまり、クロスコンパイル環境じゃないとダメ?よく分からないまま、あきらめてjsonはtgzからインストール。動作を確認するために、require (“json”)を実行してみました。
すると・・・

/opt/QtPalmtop/lib/ruby/site_ruby/1.8/json/pure.rb:50: iconv doesm't seem to support UTF-8/UTF-16 conversions (JSON:MissingUnicodeSuport)
from /opt/QtPalmtop/lib/ruby/site_ruby/1.8/json.rb:230:in 'require'
from /opt/QtPalmtop/lib/ruby/site_ruby/1.8/json.rb:230
from jsontest.rb:1:in 'require'
from jsontest.rb:1

と言われました・・・orz

つまり、iconvがインストールされていないせい?と思って導入してみましたが、iconv自体は正常にインストールできた(iconv -l で一覧が出力されている)ものの、このメッセージは変わりません。

まあ・・・これは、要するに、他人の作ったものに頼ってはダメだという啓示だと思って、あきらめてイチから作ろうと思います。 それはそれで楽しいわけですし、RESTについては、Rubyのメタプログラミングの機能でやってみたいこともありますし(笑)。


iBATIS.NETにおけるネストしたタグのprepend出力について

基本的にSQLが好きで、そのせいというわけではないのだが、仕事で開発しているフレームワークでは、O/RマッパーのベースとしてiBATISを使用している。いくつかのO/Rマッパーを試してみて、一番覚えるのが容易で、また柔軟性に富んでいて、Java、.NETの両方の開発者に対して広く薦められるプロダクトだと判断したためである。

若干の問題点として、IBATISは、Javaと.NETの間にバージョンの差異があることが上げられるのだが、先日、後発に当たるiBATIS.NETにおいて、以下のようにネストした条件判定タグを記述した場合にprepend属性がフラットに判定されるという仕様があることを発見した。

たとえば、少し強引な例だが、WHERE句に以下のようなタグの記述があるとする。

このSQLMapにおいて、UserJigyoubuCode == “y” で、かつ JigyoybyCodeとSubCodeが IsNotEmpty == true の場合、WHERE句の出力結果は以下の通りとなり、実行時にSQLシンタックスの誤りで例外がスローされる。

.NET版のiBATISでは、このように、タグのprepend属性の出力はフラットに判定され、タグがネストしていた場合でも一つ前のタグ(この例で言うと、5行目のisEqualタグ)の状態によって判断される。

また、Java版のiBATISについては、以下のように、removeFirstPrepend=”true”を親のタグに記述することによって、ネストしたタグが先頭の場合にprepend属性の出力を抑制することが可能である。