N:1マッピング

S2DAOのN:1マッピングを利用すると、SQLをかかずにJOINできます。


まず実験用にテーブルを作ります。ここではDBはMySQLを使いました。
CDテーブルとARTISTテーブルはN:1の関連を持ちます(ARTIST1人が複数のCDを出してると)。

CREATE TABLE ARTIST(
  ID INTEGER NOT NULL,
  NAME VARCHAR(100),
  PRIMARY KEY(ID)
);

CREATE TABLE CD(
  ID INTEGER NOT NULL,
  TITLE VARCHAR(100),
  ARTIST_ID INTEGER,
  PRIMARY KEY(ID),
  FOREIGN KEY (ARTIST_ID) 
     REFERENCES ARTIST(ID)
);

INSERT INTO ARTIST VALUES(1, '人1');
INSERT INTO ARTIST VALUES(2, '人2');

INSERT INTO CD(ID, TITLE,ARTIST_ID) VALUES(1, 'タイトル1', 1);
INSERT INTO CD(ID, TITLE,ARTIST_ID) VALUES(2, 'タイトル2', 1);
INSERT INTO CD(ID, TITLE,ARTIST_ID) VALUES(3, 'タイトル3', 2);


それぞれのテーブルに対応したビーンを作ります。
まずARTISTテーブルに対応したArtistクラス。これは普通に作ります。

package dao;

public class Artist {

    private int id;
    private String name;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


次にCDテーブルに対応したCdクラスを作成します。

package dao;

public class Cd {

    private int id;
    private String title;
//    private int artistId;
    private Artist artist;
    
    public static final int artist_RELNO = 0;
    public static final String artist_RELKEYS = "ARTIST_ID:ID";
    

    public String toString(){
        return super.toString()
            + ",id=" + id
            + ",title=" + title
            + ",artistId=" + artist.getId()
            + ",artistName=" + artist.getName()
            ;
    }
    
    public Artist getArtist() {
        return artist;
    }

    public void setArtist(Artist artist) {
        this.artist = artist;
    }

//    public int getArtistId() {
//        return artistId;
//    }

//    public void setArtistId(int artistId) {
//        this.artistId = artistId;
//    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}


このCdクラスでN:1用の設定を行います。
やってることは、

ということです。


このN:1のアノテーションがちょっとハマりました。

    public static final int artist_RELNO = 0;
    public static final String artist_RELKEYS = "ARTIST_ID:ID";

RELNOはなんだかイマイチわかりませんが、とりあえず外部キーは1つなので0にするといいようです(2つ、3つ結合する場合、1、2と増やしていく)。

そして、RELKEYSにN:1のテーブルのそれぞれのカラム名を指定します。
左側がN側、つまりCDテーブルのカラムARTIST_IDです。
右側が1側、つまりARTISTテーブルのカラムIDです。


このあたりの情報って、JDBCのDBメタデータから取れそうな気もするので、もしかしたら、必須のアノテーションじゃないかも?でも書かないと動かない。




あとは普通にDaoを作って、diconファイルに登録すればよしです。

Daoクラス、CdDao.javaはこんな感じ。

package dao;

import java.util.List;

public interface CdDao {

    public final static Class BEAN = Cd.class;
    
    public List getAllCds();
    
}


diconファイルはこんな感じ

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components namespace="dao">
    <include path="j2ee.dicon"/>

    <component
        class="org.seasar.dao.impl.DaoMetaDataFactoryImpl"/>
    <component name="daointerceptor"
        class="org.seasar.dao.interceptors.S2DaoInterceptor"/>
        
    <component class="dao.CdDao">
        <aspect>daointerceptor</aspect>
    </component>

</components>


これを呼び出すクライアントを作成して、

package dao;

import java.util.List;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

public class Main {

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create("dao.dicon");
        CdDao cdDao = (CdDao)container.getComponent(CdDao.class);
        List cdList = cdDao.getAllCds();
        for(int i=0; i<cdList.size(); i++){
            Cd cd = (Cd)cdList.get(i);
            System.out.println(cd);
        }
    
    }
}


えい、実行!

DEBUG 2004-10-15 00:25:34,009 [main] 物理的なコネクションを取得しました
DEBUG 2004-10-15 00:25:34,009 [main] 論理的なコネクションを取得しました
DEBUG 2004-10-15 00:25:34,079 [main] 論理的なコネクションを閉じました
DEBUG 2004-10-15 00:25:34,099 [main] SELECT Cd.id, Cd.title, artist.name AS name_0, artist.id AS id_0 FROM Cd LEFT OUTER JOIN Artist artist ON Cd.ARTIST_ID = artist.ID
DEBUG 2004-10-15 00:25:34,099 [main] 論理的なコネクションを取得しました
DEBUG 2004-10-15 00:25:34,129 [main] 論理的なコネクションを閉じました
dao.Cd@142bece,id=1,title=タイトル1,artistId=1,artistName=人1
dao.Cd@fcfa52,id=2,title=タイトル2,artistId=1,artistName=人1
dao.Cd@961dff,id=3,title=タイトル3,artistId=2,artistName=人2

ちゃんとJOINするSQLが発効され、関連テーブル含めたCdクラスのインスタンスが取得できます。


SQLは書いてません


SQLの自動生成で全部こなせるとは思いませんが、大半の単調なコードは省略できそうです。


なお、上のCd.javaでartistIdプロパティーには自動生成のSQLでは、値が入ってこないようです(関連クラスから取得できるんで入らないのかな?)。



あと、N:1のサンプルを作るのに

WEB DB PRESS Vol.23
http://www.gihyo.co.jp/magazines/wdpress/archive/Vol22
S2DAOの記事を参考にしました。


最初2ページぶちぬきソースか!?なんじゃそりゃと思いましたが、
ソース大変役立ちました。
(グーグってもS2DAOのN:1のサンプル出てこないのよねー)