ユーザ用ツール

サイト用ツール


java:s2jdbc

S2JDBC

Seasarプロジェクトが提供する新しいタイプのオブジェクト・リレーショナル(OR)マッパー。メソッドチェイン(流れるようなインターフェイス)によってクエリを組み立てる。

使い方

セットアップ方法は公式マニュアル参照

リレーション

一対多、多対一、一対一リレーションをサポート。

artistテーブルはmusicテーブルに対して一対多リレーション、musicテーブルのartist_idが外部キーとする。

1側のエンティティ

@Entity
@Table(name="artist")
public class Artist {
    @Id
    @GeneratedValue
    public long id;
 
    public String name;
 
    // 一対多リレーション(外部キーは相手が持っている)
    @OneToMany(mappedBy="artist")        // 相手側がartistという名前でArtist型プロパティを持っている必要がある
    public List<Music> musicList;
}

多側のエンティティ

@Entity
@Table(name="music")    // テーブル名=クラス名なので省略可(ただしDBがUNIX系OS上のMySQLは大文字小文字を区別するので設定が必要)
public class Music {
 
    @Id
    @GeneratedValue
    @Column(name="id")      // カラム名=フィールド名なので省略可
    public long id;
 
    @Column(name="name")    // カラム名=フィールド名なので省略可
    public String name;
 
    @Column(name="artist_id")    // カラム名=フィールド名でないので省略不可
    public long artistId;
 
    // 多対一リレーション
    @ManyToOne
    @JoinColumn(name="artist_id", referencedColumnName="id")    // referencedColumnNameは相手テーブルのprimary keyなので省略可
    public Artist artist;
}

アノテーションはJPAのもの(javax.persistence)を使う。

呼び出し側

public class Main {
    public static void main(String[] arg) {
        // S2Container 作成
        SingletonS2ContainerFactory.init();
        JdbcManager db = SingletonS2Container.getComponent(JdbcManager.class);
 
        // 一対多
        Artist artist
            = db.from(Artist.class)
                .leftOuterJoin("musicList")     // テーブル名ではなく@OneToManyを設定したプロパティ名を指定する
                .where(new SimpleWhere().eq("name", "Perfume"))
                .getSingleResult();
 
        System.out.println(artist.name);
        for (Music music: artist.musicList) {
            System.out.println(music.name);
        }
 
        // 多対一
        List<Music> allMusic
            = db.from(Music.class)
                .innerJoin("artist")    // テーブル名ではなく@ManyToOneを設定したプロパティ名を指定する
                .getResultList();
 
        for (Music music: allMusic) {
            System.out.println(music.name + " : " + music.artist.name);
        }
    }
}

OR条件クエリの作成

ComplexWhereを使う

public class Main {
    public static void main(String[] arg) {
        // S2Container 作成
        SingletonS2ContainerFactory.init();
        JdbcManager db = SingletonS2Container.getComponent(JdbcManager.class);
 
        List<Music> favoriteMusic
            = db.from(Music.class)
                .innerJoin("artist")
                .where(new ComplexWhere()
                    .eq("artist.name", "Perfume")
                    .and(new ComplexWhere()
                        .eq("name", "ポリリズム")
                        .or()
                        .eq("name","チョコレイトディスコ")))
                .getResultList();
 
        for (Music music: favoriteMusic) {
            System.out.println(music.name + " : " + music.artist.name);
        }
    }
}

生成クエリのwhere部分

(T2_.NAME = 'Perfume' and ((T1_.name = 'ポリリズム') or (T1_.name = 'チョコレイトディスコ')))

ComplexWhereのネストがない場合

        List<Music> favoriteMusic
            = db.from(Music.class)
                .innerJoin("artist")
                .where(new ComplexWhere()
                    .eq("artist.name", "Perfume")
                    .eq("name", "ポリリズム")
                    .or()
                    .eq("name","チョコレイトディスコ"))
                .getResultList();

生成SQL

((T2_.NAME = 'Perfume' and T1_.name = 'ポリリズム') or (T1_.name = 'チョコレイトディスコ'))

外部SQLファイルを使う

準備中

MySQLのユーザ変数を使う

以下のようなSQLを発行すると、同一トランザクション中のSQLで@hogeという変数にアクセスできる。外部ファイルSQL呼び出しで検索条件DTO作るのめんどくさい時に使える。ただし当然MySQL依存。

jdbcManager.updateBySql("SET @hoge = ?", String.class).params("ほげ?").execute();

トランザクション境界は、SAStrutsの場合、Actionクラスのメソッド、またはServiceクラスのメソッド単位になる(もう少し調べる…)

多対多

JPAには多対多(ManyToMany)が定義されているが、S2JDBC(S2 2.4.25時点)には実装されていないので、@OneToManyと@ManyToOneを組み合わせて作成する

CDというテーブルとMusicというテーブルがTrackというテーブルでリレーションされている多対多関連を考える。エンティティは以下のようになる。

@Entity
@Table(name="cd")
public class Cd {
 
    @Id
    @GeneratedValue
    public long id;
 
    public String name;
 
    // 多対多用の中間テーブルとのリレーション
    @OneToMany(mappedBy="cd")
    public List<Track> trackList; 
}
 
@Entity
@Table(name="music")
public class Music {
 
    @Id
    @GeneratedValue
    public long id;
 
    public String name;
 
    @Column(name="artist_id")
    public long artistId;
 
    @ManyToOne
    @JoinColumn(name="artist_id", referencedColumnName="id")    // referencedColumnNameは相手テーブルのprimary keyなので省略可
    public Artist artist;
 
    // 多対多用の中間テーブルとのリレーション
    @OneToMany(mappedBy="music")
    public List<Track> trackList;
}
 
@Entity
@Table(name="track")
public class Track {
 
    @Id
    @GeneratedValue
    public long id;
 
    @Column(name="music_id")
    public long musicId;
 
    @Column(name="cd_id")
    public long cdId;
 
    @Column(name="number")
    public int number;
 
    @ManyToOne
    @JoinColumn(name="cd_id", referencedColumnName="id")
    public Cd cd;
 
    @ManyToOne
    @JoinColumn(name="music_id", referencedColumnName="id")
    public Music music;
}

呼び出しコードは以下のようになる

Cd cd
    = db.from(Cd.class)
        .leftOuterJoin("trackList")
        .innerJoin("trackList.music")    // trackListの要素TrackクラスがMusic型プロパティmusicを持っている事
        .where(new SimpleWhere().eq("name", "GAME"))
        .getSingleResult();
 
System.out.println(cd.name);
for (Track track : cd.trackList) {
    System.out.print(track.number);
    System.out.println(track.music.name);
}

トラック番号のような付加的なデータを持たせるために、中間テーブルのエンティティを明示的に作成するようにしているらしい。

java/s2jdbc.txt · 最終更新: 2008/07/02 16:48 by 127.0.0.1