いろいろ備忘録日記

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

iBatis奮闘記-0012 (selectKeyによる採番)

iBatisでは、insert時にシーケンスなどから採番し、insertしてから、その値を取得する
機能があります。その際に使用するのがselectKey要素です。

以下サンプルです。

[DDL]

 -- vim: set ts=4 sw=4 et ws is nowrap ft=sql:
CREATE TABLE SAMPLE009_TEST_TABLE(
     id int
    ,value1 varchar(100)
    ,primary key(id)
);

CREATE SEQUENCE SAMPLE009_SEQ;

 --
 -- サンプルデータ
 --
insert into SAMPLE009_TEST_TABLE values(SAMPLE009_SEQ.nextval, 'test value-001');

[データクラス]

// vim:set ts=4 sw=4 et ws is nowrap ft=java:
package gsf.samples.ibatis.sample009;

import java.io.*;

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

public class Sample009TestTable implements Serializable{

    /** ID */
    private Integer id;

    /** VALUE1 */
    private String  value1;

    /**
     *  コンストラクタ.<br/>
     *
     */
    public Sample009TestTable(){
        // nop;
    }
    
    /**
     * Get id.
     *
     * @return id as Integer.
     */
    public Integer getId(){
        return this.id;
    }
    
    /**
     * Set id.
     *
     * @param id the value to set.
     */
    protected void setId(Integer id){
        this.id = id;
    }
    
    /**
     * Get value1.
     *
     * @return value1 as String.
     */
    public String getValue1(){
        return this.value1;
    }
    
    /**
     * Set value1.
     *
     * @param value1 the value to set.
     */
    public void setValue1(String value1){
        this.value1 = value1;
    }

    @Override
    public String toString(){
        return new ReflectionToStringBuilder(this).toString();
    }

}

[SQLマッピング設定ファイル]

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

<sqlMap namespace="SelectKey">

    <typeAlias alias="Sample009TestTable" type="gsf.samples.ibatis.sample009.Sample009TestTable"/>

    <sql id="insertSample009TestTable">
        insert into SAMPLE009_TEST_TABLE
            (id, value1)
            values
            (#id#, #value1#)
    </sql>

    <select id="getMaxNumberSample009TestTable" resultClass="int">
        select max(id) from SAMPLE009_TEST_TABLE
    </select>

    <insert id="insertSample009TestTableNoAutoKey" parameterClass="Sample009TestTable">
        <include refid="insertSample009TestTable"/>
    </insert>

    <insert id="insertSample009TestTableWithAutoKey" parameterClass="Sample009TestTable">
        <selectKey resultClass="int" type="pre" keyProperty="id">
            select SAMPLE009_SEQ.nextval as id from dual
        </selectKey>

        <include refid="insertSample009TestTable"/>
    </insert>

</sqlMap>

[動作確認クラス]

// vim:set ts=4 sw=4 et ws is nowrap ft=java:
package gsf.samples.ibatis.sample009;

import java.sql.*;

import gsf.interfaces.sqlmap.*;
import gsf.utils.sqlmap.*;

import com.ibatis.sqlmap.client.*;

public class IBatisSample009{

    private static class MaxIdNumberExecutor implements SqlMapClientExecutor{

        public Object execute(SqlMapClient sqlMap) throws SQLException{
            return sqlMap.queryForObject("getMaxNumberSample009TestTable");
        }

    }

    private static class InsertSampleWithAutoKeyExecutor implements SqlMapClientExecutor{

        private Sample009TestTable data;

        public void setData(Sample009TestTable data){
            this.data = data;
        }

        public Object execute(SqlMapClient sqlMap) throws SQLException{
            return sqlMap.insert("insertSample009TestTableWithAutoKey", data);
        }

    }

    private static class InsertSampleNoAutoKeyExecutor implements SqlMapClientExecutor{

        private Sample009TestTable data;

        public void setData(Sample009TestTable data){
            this.data = data;
        }

        public Object execute(SqlMapClient sqlMap) throws SQLException{
            return sqlMap.insert("insertSample009TestTableNoAutoKey", data);
        }

    }

    public static void main(String[] args){

        Sample009TestTable data = new Sample009TestTable();

        //
        // 自動採番を利用した例
        // 
        System.out.println("=========== 自動採番の例 Start ===============");

        data.setValue1("自動採番を利用。");

        InsertSampleWithAutoKeyExecutor executor = new InsertSampleWithAutoKeyExecutor();

        executor.setData(data);

        System.out.printf("実行する前のデータオブジェクト:%s\n", data);

        //////////////////////////////////////////////////////////////////////////
        // insertにて、内部でselectKeyを使用した場合、sqlMap.insert()の戻り値は
        // 採番されたキーの値が戻り値として返却される.
        //
        Integer newKey = (Integer) SqlMapUtils.executeWithTransaction(executor);

        System.out.printf("採番されたキー:%s\n",                 newKey);
        System.out.printf("実行した後のデータオブジェクト:%s\n", data);

        System.out.println("=========== 自動採番の例 end ===============");

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


        //
        // 自動採番を利用しない例
        //
        System.out.println("=========== 手動採番の例 Start ===============");

        Integer nextNumber = (Integer) SqlMapUtils.executeNoTransaction(new MaxIdNumberExecutor());

        data.setId(++nextNumber + 100000);
        data.setValue1("手動採番を利用。");

        InsertSampleNoAutoKeyExecutor executor2 = new InsertSampleNoAutoKeyExecutor();

        executor2.setData(data);

        System.out.printf("実行する前のデータオブジェクト:%s\n", data);

        //////////////////////////////////////////////////////////////////////////
        // insertにて、内部でselectKeyを使用していない場合、sqlMap.insert()の戻り値は
        // nullとなる.
        //
        Object result = SqlMapUtils.executeWithTransaction(executor2);

        System.out.printf("insertメソッドの戻り値:%s\n",         result);
        System.out.printf("実行した後のデータオブジェクト:%s\n", data);
        System.out.println("=========== 手動採番の例 end ===============");
    }
}

実行すると、selectKeyを使用して、insertを行っている場合は、採番された値が
帰ってきていることがわかります。selectKeyを使用していない場合の戻り値はnullです。

selectKey要素は、以下の属性を持っています。

  • resultClass
  • type
  • keyProperty

resultClassはそのまま、いつもどおりです。
typeは、preとpostが指定できます。preはinsertを行う前に実行され、
postは、insertを行った後に実行されます。SQL Serverなどは、postに
しないとうまく動きません。通常は、preを指定するのが多いでしょう。
最後のkeyPropertyですが、これは、採番して取得した値を
parameterClassで指定されたオブジェクトのどのプロパティにセットするかを
指定するものです。上記の場合ですと、Sample009TestTableのidプロパティに
採番された値がセットされます。ちなみに、keyPropertyを指定しない場合は、
iBatisの方で勝手に判断して、採番するのに実行したSQLの結果カラムから
適合するプロパティを探し出しセットしてくれます。

つまり、insertメソッドの結果でも取得キーが帰ってきますが、
パラメータで渡したオブジェクトにもセットされるということになります。

ちなみに、上記のサンプルを実行した結果は、たとえば以下のようになります。

exec:
     [java] =========== 自動採番の例 Start ===============
     [java] 実行する前のデータオブジェクト:gsf.samples.ibatis.sample009.Sample009TestTable@de6f34[id=<null>,value1=自動採番を利用。]
     [java] 採番されたキー:11
     [java] 実行した後のデータオブジェクト:gsf.samples.ibatis.sample009.Sample009TestTable@de6f34[id=11,value1=自動採番を利用。]
     [java] =========== 自動採番の例 end ===============


     [java] =========== 手動採番の例 Start ===============
     [java] 実行する前のデータオブジェクト:gsf.samples.ibatis.sample009.Sample009TestTable@de6f34[id=300012,value1=手動採番を利用。]
     [java] insertメソッドの戻り値:null
     [java] 実行した後のデータオブジェクト:gsf.samples.ibatis.sample009.Sample009TestTable@de6f34[id=300012,value1=手動採番を利用。]
     [java] =========== 手動採番の例 end ===============