EJB 3.0(Public Draft)入門記 Java Persistence API Chapter2 その8

今回からJBossアプリケーションサーバでなくEmbeddable EJB 3.0を使ってすすめます。

2.1.8.3 Unidirectional Single-Valued Relationships

今日のお題は単方向の単一リレーションシップです。

単方向の単一リレーションシップとは次のようなものです。

  • エンティティAがひとつのエンティティBのインスタンスを参照する
  • エンティティBはエンティティAを参照しない

単一のリレーションシップには所有側だけがあります。この場合、エンティティAが所有側となります。
単方向の単一リレーションシップは単方向のOneToOneもしくは単方向のManyToOneのリレーションシップどちらとして指定できるそうです。

2.1.8.3.1 Unidirectional OneToOne Relationships

まず単方向のOneToOneリレーションシップからです。

2つのエンティティEmployeeとTravelProfileがあります。

@Entity(access = AccessType.FIELD)
public class Employee implements Serializable {
  @Id(generate = GeneratorType.AUTO)
  private int id;

  private String name;

  @OneToOne
  private TravelProfile profile;

  //getter, setter省略
}
@Entity(access = AccessType.FIELD)
public class TravelProfile implements Serializable {
  @Id(generate = GeneratorType.AUTO)
  private int id;
  
  private String name;

  //getter, setter省略
}

これらのエンティティは最初に書いた想定で言えば、

  • EmployeeがエンティティA、TravelProfileがエンティティBにあたります。
  • あと、もうおなじみですが、それぞれエンティティはEMPLOYEEテーブルとTRAVELPROFILEテーブルにマッピングされます
  • 。EMPLOYEEテーブルはTRAVELPROFILEテーブルへの外部キーをもち、外部キーの名称はPROFILE_IDとなります。外部キーはTRAVELPROFILEテーブルのプライマリキーと同じ型でユニーク制約をもちます。

antとかでhbm2ddlを実行すればいいのかもしれませんが、(使い方がよくわからないので)次のコードを動かしてテーブルを生成しました。

    EJB3StandaloneBootstrap.boot(null);
    EJB3StandaloneBootstrap.deployXmlResource("ejb3-deployment.xml");
    EJB3StandaloneBootstrap.shutdown();

HSQLDBGUIからDumpを実行してもなぜかDumpされない。CHEKPOINTしてSQLを出力しました。JBoss4.0.2R1のHSQLDBとバージョンが違うのか出力されるDDLが前回とちがいます。こんなでした。

CREATE TABLE EMPLOYEE(ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1)  NOT NULL PRIMARY KEY,NAME VARCHAR(255),
  PROFILE_ID INTEGER,CONSTRAINT SYS_CT_13 UNIQUE(PROFILE_ID))
CREATE TABLE TRAVELPROFILE(ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1)  NOT NULL PRIMARY KEY,NAME VARCHAR(255))
ALTER TABLE EMPLOYEE ADD CONSTRAINT FK4AFD4ACE4558BF2B FOREIGN KEY(PROFILE_ID) REFERENCES TRAVELPROFILE(ID)


では動かしてみます。まずデータをつくります。

INSERT INTO TRAVELPROFILE VALUES(1, '京都')
INSERT INTO EMPLOYEE VALUES(1, 'ゴン', 1)

エンティティを取得してみます。

@Stateless
public class ClientBean implements Client {

  @PersistenceContext
  private EntityManager em;

  public void main() {
    // Employeeからたどる
    Employee employee = em.find(Employee.class, 1);
    System.out.println(employee.getName());
    System.out.println(employee.getProfile().getName());
  }

  public static void main(String[] args) throws Exception {
    EJB3StandaloneBootstrap.boot(null);
    EJB3StandaloneBootstrap.deployXmlResource("ejb3-deployment.xml");

    InitialContext ctx = new InitialContext();
    Client c = (Client) ctx.lookup(Client.class.getName());
    c.main();
    
    EJB3StandaloneBootstrap.shutdown();    
  }
}

実行結果です。実行されたSQLと取得したデータが表示されます。ちゃんとJoinしてくれるんですね。

Hibernate: select employee0_.id as id0_1_, employee0_.name as name0_1_,
employee0_.profile_id as profile3_0_1_, travelprof1_.id as id1_0_, travelprof1_.name as name1_0_ from Employee employee0_ 
left outer join TravelProfile travelprof1_ on employee0_.profile_id=travelprof1_.id where employee0_.id=?
ゴン
京都


2.1.8.3.2 Unidirectional ManyToOne Relationships

次は単方向のManyToOneリレーションシップです。

2つのエンティティEmployeeとAddressがあります。

public class Employee implements Serializable {
  @Id(generate = GeneratorType.AUTO)
  private int id;

  private String name;

  @ManyToOne
  private Address address;
  
  // getter, setter 省略
}
@Entity(access=AccessType.FIELD)
public class Address implements Serializable {
  @Id(generate = GeneratorType.AUTO)
  private int id;
  
  private String prefecture;
  
  // getter, setter省略
}

これらのエンティティは最初に書いた想定で言えば、

  • EmployeeがエンティティA、AddressがエンティティBにあたります。
  • それぞれエンティティはEMPLOYEEテーブルとADDRESSテーブルにマッピングされます
  • 。EMPLOYEEテーブルはADDRESSテーブルへの外部キーをもち、外部キーの名称はADDRESS_IDとなります。外部キーはTRAVELPROFILEテーブルのプライマリキーと同じ型になります。OneToOneのときと違ってユニーク制約はもちません。ManyToOneですからね。

エンティティから生成されるDDLです。

CREATE TABLE ADDRESS(ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1)  
  NOT NULL PRIMARY KEY,PREFECTURE VARCHAR(255))
CREATE TABLE EMPLOYEE(ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1)  
  NOT NULL PRIMARY KEY,NAME VARCHAR(255),ADDRESS_ID INTEGER,CONSTRAINT FK4AFD4ACE233D5405 FOREIGN KEY(ADDRESS_ID) REFERENCES ADDRESS(ID))


動かしてみます。データを作成します。

INSERT INTO ADDRESS VALUES(1, '京都')
INSERT INTO EMPLOYEE VALUES(1, 'ゴン', 1)
INSERT INTO EMPLOYEE VALUES(2, 'タクヤ', 1)

エンティティを取得します。

@Stateless
public class ClientBean implements Client {

  @PersistenceContext
  private EntityManager em;

  public void main() {
    // Employeeからたどる
    Employee employee = em.find(Employee.class, 1);
    System.out.println(employee.getName());
    System.out.println(employee.getAddress().getPrefecture());
  }

  public static void main(String[] args) throws Exception {
    EJB3StandaloneBootstrap.boot(null);
    EJB3StandaloneBootstrap.deployXmlResource("ejb3-deployment.xml");

    InitialContext ctx = new InitialContext();
    Client c = (Client) ctx.lookup(Client.class.getName());
    c.main();
    
    EJB3StandaloneBootstrap.shutdown();    
  }
}

実行結果です。使っているエンティティはちがいますがリレーションの張られ方という意味ではOneToOneのときと同じですね。

Hibernate: select employee0_.id as id1_1_, employee0_.name as name1_1_,
employee0_.address_id as address3_1_1_, address1_.id as id0_0_, address1_.prefecture as prefecture0_0_ from Employee employee0_ 
left outer join Address address1_ on employee0_.address_id=address1_.id where employee0_.id=?
ゴン
京都


なんかつかれた。細かく書きすぎかも。
次回は2.1.8.4 Bidirectional ManyToMany Relationshipsです。