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 APIEJB専用ではないにもかかわらずXML記述子がでてきます。ということはJava SEでPersistence APIが使われるときはアノテーションに加えてXMLO/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終了。