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); }
トラック番号のような付加的なデータを持たせるために、中間テーブルのエンティティを明示的に作成するようにしているらしい。