いろいろ備忘録日記

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

iBatis奮闘記-0004 (1:1の関連)

次は、1:1の関連です。

こちらは、1:Nよりも楽です。
ポイントは、1:Nの時と同じく適切にresultMapを定義することだけです。

[テーブル定義]

create table one_to_one_test_table1 (id int primary key, val varchar(50), table2_id int);
create table one_to_one_test_table2 (id int primary key, val varchar(50), table1_id int);
create sequence test_table1_seq;
create sequence test_table2_seq;

[ドメインオブジェクト]

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

import java.io.*;

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

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

    /** ID */
    private Integer id;

    /** VAL */
    private String  value;

    /** ONE_TO_ONE_TEST_TABLE2オブジェクト */
    private Table2  table2;

    /**
     * コンストラクタ.<br/>
     *
     */
    public Table1(){
    }

    /**
     * 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 value.
     *
     * @return value as String.
     */
    public String getValue(){
        return this.value;
    }
    
    /**
     * Set value.
     *
     * @param value the value to set.
     */
    public void setValue(String value){
        this.value = value;
    }
    
    /**
     * Get table2.
     *
     * @return table2 as Table2.
     */
    public Table2 getTable2(){
        return this.table2;
    }
    
    /**
     * Set table2.
     *
     * @param table2 the value to set.
     */
    public void setTable2(Table2 table2){
        this.table2 = table2;
    }

    /**
     * オブジェクトの文字列表現を返す.<br/>
     *
     * @return 文字列表現
     *
     * @see java.lang.Object#toString()
     *
     */
    @Override
    public String toString(){
        return new ToStringBuilder(this)
                        .append("id",  this.getId())
                        .append("val", this.getValue())
                        .toString();
    }
}
// vim:set ts=4 sw=4 et ws is nowrap ft=java fenc=cp932 ff=dos:
package gsf.samples.ibatis.sample003;

import java.io.*;

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

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

    /** ID */
    private Integer id;

    /** VAL */
    private String  value;

    /** ONE_TO_ONE_TEST_TABLE1オブジェクト */
    private Table1  table1;

    /**
     * コンストラクタ.<br/>
     *
     */
    public Table2(){
    }

    /**
     * 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 value.
     *
     * @return value as String.
     */
    public String getValue(){
        return this.value;
    }
    
    /**
     * Set value.
     *
     * @param value the value to set.
     */
    public void setValue(String value){
        this.value = value;
    }
    
    /**
     * Get table1.
     *
     * @return table1 as Table1.
     */
    public Table1 getTable1(){
        return this.table1;
    }
    
    /**
     * Set table1.
     *
     * @param table1 the value to set.
     */
    public void setTable1(Table1 table1){
        this.table1 = table1;
    }

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

[SQL設定ファイル]

<?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">

<!--
    1:1の関連を行うSQL設定ファイル
    • >
<sqlMap namespace="OneToOne"> <typeAlias alias="Table1" type="gsf.samples.ibatis.sample003.Table1"/> <typeAlias alias="Table2" type="gsf.samples.ibatis.sample003.Table2"/> <!-- 取得した値をオブジェクトにマッピング property属性には、table2.idのようにOGNL表記が使えるので そのまま関連オブジェクトに値を格納できる。 双方向関連が一発で実現できるかどうかは、まだ分かりません・・・。 でも、ほとんどの場合1方向の関連でうまくいくのではないでしょうか。 どうしても、必要な場合は逆順のマッピングを設定すればいいことですし。 --> <resultMap id="table1ResultMap" class="Table1"> <result property="id" column="table1_id"/> <result property="value" column="table1_val"/> <result property="table2.id" column="table2_id"/> <result property="table2.value" column="table2_val"/> </resultMap> <select id="getTable1Data" parameterClass="int" resultMap="table1ResultMap"> <![CDATA[ select t1.id as table1_id ,t1.val as table1_val ,t2.id as table2_id ,t2.val as table2_val from one_to_one_test_table1 t1 ,one_to_one_test_table2 t2 where t1.id = #value# and t1.table2_id = t2.id ] ]> </select> <select id="getNextTable1Id" resultClass="int"> <![CDATA[ select test_table1_seq.nextval from dual ] ]> </select> <select id="getNextTable2Id" resultClass="int"> <![CDATA[ select test_table2_seq.nextval from dual ] ]> </select> <select id="getMostRecentTable1Id" resultClass="int"> <![CDATA[ select max(id) from one_to_one_test_table1 ] ]> </select> <!-- 本当は、selectKey要素でシーケンスから値を取得して、 insertを行いたかったが、H2ではエラーが出てうまくいかなかった。 できるかどうかは、後で調査. --> <insert id="insertTable1" parameterClass="Table1"> <![CDATA[ insert into one_to_one_test_table1 (id, val, table2_id) values(#id#, #value#, #table2.id#) ] ]> </insert> <insert id="insertTable2" parameterClass="Table2"> <![CDATA[ insert into one_to_one_test_table2 (id, val, table1_id) values(#id#, #value#, #table1.id#) ] ]> </insert> </sqlMap>

[動作確認クラス]

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

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

/**
 * IBatisSample003の動作確認を行うクラス.<br/>
 *
 * @author gsf_zero1
 *
 */
public class IBatisSample003{

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

        SqlMapClient sqlMap 
                        = SqlMapClientBuilder.buildSqlMapClient(
                                Resources.getResourceAsReader("SqlMapConfig.xml"));

        try{
            sqlMap.startTransaction();

            Table1 t1 = new Table1();
            t1.setId( (Integer) sqlMap.queryForObject("getNextTable1Id", null) );
            t1.setValue("table1_value");
            
            Table2 t2 = new Table2();
            t2.setId( (Integer) sqlMap.queryForObject("getNextTable2Id", null) );
            t2.setValue("table2_value");

            t1.setTable2(t2);
            t2.setTable1(t1);

            sqlMap.insert("insertTable1", t1);
            sqlMap.insert("insertTable2", t2);

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

        Table1 aTable1 
                = (Table1) sqlMap.queryForObject(
                                    "getTable1Data", 
                                    (Integer) sqlMap.queryForObject("getMostRecentTable1Id", null));

        Table2 aTable2 = aTable1.getTable2();

        System.out.println(aTable1);
        System.out.println(aTable2);
    }
}

実行すると、Table1からTable2オブジェクトが参照できることが確認できます。

N:Nは、1:Nの応用になるので、特にとりあげるのはやめます。
iBatisのDeveloper'sガイドを見ていると複合キーとかいろいろ対応できるみたいです。

次は、Dynamic Mapped Statementsに挑戦。