EJB 3.0(Public Draft)入門記 Java Persistence API Chapter2 その6
今回から2.1.8 Relationship Mapping Defaultに入ります。この節は長いのでとても1回では終われないです。ということで細切れになります。
2.1.8 Relationship Mapping Default
この節ではOneToOne、OneToMany、ManyToOne、ManyToManyアノテーションを使用する際に適用されるマッピングのDefaultについて説明されるそうです。XML記述子を使ったときも同じマッピングのDefaultが適用されるそうです。そういえば、Java Persistence APIはEJB専用ではないにもかかわらずXML記述子がでてきます。ということはJava SEでPersistence APIが使われるときはアノテーションに加えてXMLでO/Rマッピングが記述できるといういうことですね。
2.1.8.1 Bidirectional OneToOne Relationships
双方向のOneToOneリレーションシップについてです。ドキュメントに例が載っているんですが、この例をベースに話をすすめます。例はそのままでは使わず都合のいいように少し手を加えてます。例は次のようなものです。
Employee(従業員)とCubicle(従業員の作業スペース)のエンティティがあります。コードはこんなカンジ。
@Entity(access = AccessType.FIELD) public class Employee implements Serializable { @Id(generate = GeneratorType.AUTO) private int id; private String name; @OneToOne private Cubicle assignedCubicle; public String toString() { return "name=" + name + ", " + "assignedCubicle.name=" + assignedCubicle.getName(); } // getter, setter省略 }
@Entity(access = AccessType.FIELD) public class Cubicle implements Serializable { @Id(generate = GeneratorType.AUTO) private int id; private String name; @OneToOne(mappedBy = "assignedCubicle") private Employee residentEmployee; public String toString() { return "name=" + name + ", " + "residentEmployee.name=" + residentEmployee.getName(); } // getter, setter省略 }
この例はリレーションシップに関して次のことをあらわしています。
- エンティティEmployeeはエンティティCubicleのひとつのインスタンスを参照する
- エンティティCubicleはエンティティEmployeeのひとつのインスタンスを参照する
- エンティティEmployeeはリレーションシップの所有側
次のマッピングDefaultが適用されるそうです。
- エンティティEmployeeはEMPLOYEEテーブルにマップされる。
- エンティティCubicleはCUBICLEテーブルにマップされる。
- テーブルEMPLOYEEはテーブルCUBICLEに対する外部キーをもつ。外部キーの名前はASSIGNEDCUBICLE_
となる。 とはCUBICLEのプライマリキーの名前のことを意味する。この例ではCUBICLEのプライマリキーの名前はIDなので外部キーはASSIGNEDCUBICLE_IDとなるということです。 - 外部キーはCUBICLEのプライマリキーと同じ型をもちユニーク制約をもつ。
JBossの場合、エンティティをデプロイしたときにテーブルがないと勝手にテーブルを作ってくれます。hbm2ddlが動いているみたい。ということで上記エンティティをデプロイしたときにどんなDDLが作成されるのか見てみます。そういえばどこにも書いてなかったような気がしますがDBはJBossにくっついているHSQLDBを使用してます。
CREATE TABLE CUBICLE(ID SERIAL NOT NULL ,NAME VARCHAR, CONSTRAINT SYS_PK_CUBICLE PRIMARY KEY (ID) ) CREATE TABLE EMPLOYEE(ID SERIAL NOT NULL ,NAME VARCHAR,ASSIGNEDCUBICLE_ID INTEGER, CONSTRAINT SYS_PK_EMPLOYEE PRIMARY KEY (ID) ) CREATE UNIQUE INDEX SYS_PK_CUBICLE ON CUBICLE(ID) CREATE UNIQUE INDEX SYS_IDX_SYS_CT_79_80 ON EMPLOYEE(ASSIGNEDCUBICLE_ID) CREATE UNIQUE INDEX SYS_PK_EMPLOYEE ON EMPLOYEE(ID) CREATE INDEX SYS_IDX_81 ON EMPLOYEE(ASSIGNEDCUBICLE_ID) ALTER TABLE EMPLOYEE ADD CONSTRAINT FK4AFD4ACE316CA1F3 FOREIGN KEY (ASSIGNEDCUBICLE_ID) REFERENCES CUBICLE (ID)
外部キーの名前がASSIGNEDCUBICLE_IDになってたり外部キーにユニーク制約がつけられてたり、ちゃんと上述したマッピングのDefault設定が適用されているみたいです。テーブルの自動生成はとっても便利ー。デプロイめんどくさいけど。
実際に動かしてエンティティの取得をしてみます。エンティティと一緒に次のセッションBeanもデプロイします。
public interface Dao { public Employee findEmployeeById(int id); public Cubicle findCubicleById(int id); }
@Stateless @Remote(Dao.class) public class DaoBean implements Dao { @PersistenceContext private EntityManager em; public Employee findEmployeeById(int id) { return em.find(Employee.class, id); } public Cubicle findCubicleById(int id){ return em.find(Cubicle.class, id); } }
SQLでDBにデータをいれます。
INSERT INTO CUBICLE (id, name) VALUES(1, '作業スーペースA'); INSERT INTO EMPLOYEE(id, name, assignedcubicle_id) VALUES(1, 'ゴン', 1)
クラインアントです。CubicleとEmployeeを別々に取得してみます。
public class Client { public static void main(String[] args) throws Exception { // SessionBeanの取得 InitialContext ctx = new InitialContext(); Dao dao = (Dao) ctx.lookup(Dao.class.getName()); Cubicle cubicle = dao.findCubicleById(1); System.out.println(cubicle); Employee employee = dao.findEmployeeById(1); System.out.println(employee); } }
クライアントの実行結果です。
name=作業スーペースA, residentEmployee.name=ゴン name=ゴン, assignedCubicle.name=作業スーペースA
OK。ちゃんとリレーションシップをたどれているみたい。
ログによると実際に実行されたSQLは次のものです。
2005-09-11 00:17:34,090 DEBUG [org.hibernate.SQL] select cubicle0_.id as id0_1_, cubicle0_.name as name0_1_, employee1_.id as id1_0_, employee1_.name as name1_0_, employee1_.assignedCubicle_id as assigned3_1_0_ from Cubicle cubicle0_ left oute r join Employee employee1_ on cubicle0_.id=employee1_.assignedCubicle_id where cu bicle0_.id=? 2005-09-11 00:17:34,210 DEBUG [org.hibernate.SQL] select employee0_.id as id1_1_, employee0_.name as name1_1_, employee0_.assignedCubicle_id as assigned3_1_1_, cu bicle1_.id as id0_0_, cubicle1_.name as name0_0_ from Employee employee0_ left ou ter join Cubicle cubicle1_ on employee0_.assignedCubicle_id=cubicle1_.id where em ployee0_.id=? 2005-09-11 00:17:34,220 DEBUG [org.hibernate.SQL] select employee0_.id as id1_1_, employee0_.name as name1_1_, employee0_.assignedCubicle_id as assigned3_1_1_, cu bicle1_.id as id0_0_, cubicle1_.name as name0_0_ from Employee employee0_ left ou ter join Cubicle cubicle1_ on employee0_.assignedCubicle_id=cubicle1_.id where em ployee0_.assignedCubicle_id=?
ふーむ、読みにくい。3回SQLが発行されている模様です???。1回目と2回目はわかるんですが3回目はなぜ実行されているんでしょう? ...ひらめいた、外部キーにユニーク制約があることをチェックするためかも。
2.1.8.1 Bidirectional OneToOne Relationships終了。