いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

iBatis奮闘記-0002 (1テーブルのCRUD)

てことで、まずは基本中の基本。1テーブルに対してCRUD処理をしてみます。
iBatisで処理を行う際に、設定ファイルのほかに各SQLを収めたSQL設定ファイルが必要になります。
これも名前はなんでもいいみたいですが、取得クラス名にあわせたりするほうがいいでしょう。
今回、テストテーブルとして以下ものを作成。
(ちなみにiBatisには、Hibernateのようにテーブルを自動作成してくれる機能はありません。
自分でテーブルをDB上に作成します。)

create table test( id int primary key, name varchar(100));

おもしろくもなんともないテーブルです。すみませんw
次は、データクラスの作成。これは、普通のPOJOクラスです。

// vim:set ts=4 sw=4 et ws is nowrap ft=java fenc=cp932 ff=dos:
package gsf.samples.ibatis.sample001;

import java.io.*;

import org.apache.commons.lang.builder.*;

/**
 * TESTテーブルに対応するドメインオブジェクト.<br/>
 *
 * @author gsf_zero1
 *
 */
public class Test implements Serializable{

    /** ID */
    private Integer id;

    /** NAME */
    private String  name;

    /**
     * コンストラクタ.<br/>
     *
     */
    public Test(){
    }
    
    /**
     * Get id.
     *
     * @return id as Integer.
     */
    public Integer getId(){
        return this.id;
    }
    
    /**
     * Set id.
     *
     * @param id the value to set.
     */
    public void setId(Integer id){
        this.id = id;
    }
    
    /**
     * Get name.
     *
     * @return name as String.
     */
    public String getName(){
        return this.name;
    }
    
    /**
     * Set name.
     *
     * @param name the value to set.
     */
    public void setName(String name){
        this.name = name;
    }

    /**
     * オブジェクトの文字列表現を返す.<br/>
     *
     * @return 文字列表現
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString(){
        return new ReflectionToStringBuilder(this).toString();
    }
}

これも普通のクラスです。あとは、設定ファイルを書いてSQLとクラスを紐付けます。
今回、ファイル名をTest.ibatis.xmlとしました。なお、設定ファイル中に<や&rt;
があると、XMLがエラーを返しますので、SQL内で使う場合はCDATAセクションで
囲みましょう。

<?xml version="1.0" encoding="Windows-31J"?>
<!-- vim:set ts=4 sw=4 et ws is nowrap ft=xml fenc=cp932 ff=dos: -->
<!DOCTYPE sqlMap
    PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
    "http://www.ibatis.com/dtd/sql-map-2.dtd">

<!--
    SQL Map設定ファイルのルート要素。

    属性として、idとnamespaceが存在する。
    namespace属性は、テーブルジョインを用いて
    複雑な結果オブジェクトにマッピングする際に
    使用するので、指定しておくほうがよい。
    • >
<sqlMap namespace="Test"> <!-- 別名を付ける。 設定ファイル内で繰り返し、出現するクラス名などに対して、 別名を付けて、簡略名称で指定できるようにできる。 --> <typeAlias alias="Test" type="gsf.samples.ibatis.sample001.Test"/> <!-- SELECT文を指定できる。 オブジェクトに適切にマッピングするためには、 SELECT文の結果の列の名称をプロパティ名称と 一致させておくか、resultMap要素で定義しておく 必要がある。 --> <select id="getTest" parameterClass="int" resultClass="Test"> <![CDATA[ select id, name from test where id=#value# ] ]> </select> <select id="getAllTests" resultClass="Test"> <![CDATA[ select id, name from test order by id ] ]> </select> <!-- INSERT文を指定できる。 --> <insert id="insertTest" parameterClass="Test"> <![CDATA[ insert into test values(#id#, #name#) ] ]> </insert> <!-- UPDATE文を指定できる。 --> <update id="updateTest" parameterClass="Test"> <![CDATA[ update test set name = #name# where id = #id# ] ]> </update> <!-- DELETE文を指定できる。 --> <delete id="deleteAllTests"> <![CDATA[ delete from test ] ]> </delete> </sqlMap>

みたら、わかるとおもいますがそのままSQLを書きます。

#value#

と書かれているところには、後で登場するqueryForObjectメソッドの第二引数の値が入ります。
(parameterClassには、iBatis側で予めいくつか別名が定義されています。intもそのひとつ。)

#id#, #name#

とかかれているところは、予想がつくと思われますがparameterClassで指定された
クラスの該当プロパティが入ります。
次に、よく忘れそうですが、ルート設定ファイル(SqlMapConfig.xml)に
今作成したSQL設定ファイルを定義する必要があります。

    <sqlMap resource="gsf/samples/ibatis/sample001/Test.ibatis.xml"/>

ここまでで、ORマッピングの部分は終わりました。
あとは、これを利用する部分を作成。

// vim:set ts=4 sw=4 et ws is nowrap ft=java fenc=cp932 ff=dos:
package gsf.samples.ibatis.sample001;

import java.io.*;
import java.util.*;

import com.ibatis.common.resources.*;
import com.ibatis.sqlmap.client.*;

/**
 * Sample001の動作を確認するクラス.<br/>
 *
 * @author gsf_zero1
 *
 */
public class IBatisSample001{

    /**
     * アプリケーションエントリポイント.<br/>
     *
     * @param  args      起動時引数
     * @throws Exception エラーが発生した場合
     *
     */
    public static void main(String[] args) throws Exception{

        String       sqlMapConfigFile = "SqlMapConfig.xml";
        Reader       reader           = Resources.getResourceAsReader(sqlMapConfigFile);
        SqlMapClient sqlMap           = SqlMapClientBuilder.buildSqlMapClient(reader);

        try{

            //
            // トランザクションの開始
            //
            sqlMap.startTransaction();

            //
            // データを全削除
            //
            sqlMap.delete("deleteAllTests", null);

            //
            // テストデータを追加
            //
            for(int i = 0; i < 1000; i++){
                Test aTest = new Test();

                aTest.setId( (i + 1) );
                aTest.setName("test_value_" + (i + 1));

                sqlMap.insert("insertTest", aTest);
            }

            //
            // コミットを行う
            //
            sqlMap.commitTransaction();

        }finally{
            //
            // トランザクションを終了
            //
            // このメソッドは、コミットがコールされていない場合
            // 自動的にロックバックを行い、トランザクションを
            // 終了させる。コミットがコールされている場合は
            // そのままトランザクションを終了させる。
            //
            sqlMap.endTransaction();
        }

        //
        // データを全取得
        //
        for(Test aTest : (List<Test>) sqlMap.queryForList("getAllTests", null)){
            System.out.println(aTest);
        }

        System.out.printf("\n\n");

        //
        // 単一データの取得
        //
        // IDが111のデータを取得
        Test aTest = (Test) sqlMap.queryForObject("getTest", 111);
        System.out.println(aTest);

        //
        // データを更新
        //
        try{
            sqlMap.startTransaction();

            aTest.setName("modify_" + aTest.getName());

            sqlMap.update("updateTest", aTest);

            sqlMap.commitTransaction();
        }finally{
            sqlMap.endTransaction();
        }

        aTest = (Test) sqlMap.queryForObject("getTest", 111);
        System.out.println(aTest);

    }
}

利用方法は、こんな感じです。

        String       resource = "SqlMapConfig.xml";
        Reader       reader   = Resources.getResourceAsReader(resource);
        SqlMapClient sqlMap   = SqlMapClientBuilder.buildSqlMapClient(reader);

の部分は、お決まり部分です。SqlMapClientを作成することがiBatisにおいて最初の処理となります。
ちなみにこのクラスはスレッドセーフとなっています。アプリケーションでひとつあればいいです。
実際は、チュートリアルに書いてあるようにヘルパークラスを作成するか、DIコンテナなどから
インジェクトするのが多いでしょう。

実行している部分は特に説明がいらないでしょう。
sqlMap.queryForObjectやsqlMap.queryForListなどの第一引数が設定ファイルにあるidで指定した
名前に対応します。第二引数がparameterClassの指定に対応します。
あと、トランザクション処理で、ロールバックみたいなものが記述されていませんが
endTransactionメソッドがその処理を担当しています。このメソッドは、コールされた際に
すでにコミットがされている場合は、そのままトランザクションを終了し、コミットされていない場合は
ロールバックを行った後、トランザクションを終了します。つまり、finallyに書いておけば、エラーが
出た場合は、ロールバックがかかり、コミットが行われた場合はそのままトランザクションが終了することに
なります。

単一テーブルの処理はこんな感じです。次は、ジョイン関連の処理をやってみます。