EJB 3.0(Public Draft)入門記 Java Persistence API Chapter2 その9
Chapter2ももうその9になってしましました。でもその割りにはすすんでいないような。finalが出るまでにPublic Draftの最後までいきたいなぁ。
2.1.8.4 Bidirectional ManyToMany Relationships
今回は双方向のManyToManyなリレーションシップについてです。例をベースに進めます。次のようなProjectエンティティとEmployeeエンティティがあります。前回までは@Id(generate=GeneratorType.AUTO)とgenerate要素にGeneratorType.AUTOを指定してましたが取得するだけなら関係ないのではずしました。
@Entity(access=AccessType.FIELD) public class Project { @Id private int id; private String name; @ManyToMany private Collectionemployees; //getter,setter省略 }
@Entity(access = AccessType.FIELD) public class Employee { @Id private int id; private String name; @ManyToMany(mappedBy="employees") private Collectionprojects; // getter,setter省略 }
エンティティ間の関係はつぎのとおりです。
- エンティティProjectはエンティティEmployeeのコレクションを参照する
- エンティティEmployeeはエンティティProjectのコレクションを参照する
- エンティティProjectがリレーションシップの所有側となる
このとき次のマッピングDefaultが適用されるそうです。
- エンティティProjectはテーブルPROJECTにマップされる。
- エンティティEmployeeはテーブルEMPLOYEEにマップされる。
- PROJECT_EMPLOYEE(所有側の名前が先)という関連テーブルが必要。この関連テーブルは次の2つの外部キーをもつ。
Embeddable EJB 3を動かすとhbm2ddlが次のDDLでテーブルを作ってくれました。上記のマッピングDefaultにしたがっていることがわかります。
CREATE TABLE EMPLOYEE(ID INTEGER NOT NULL PRIMARY KEY,NAME VARCHAR(255)) CREATE TABLE PROJECT(ID INTEGER NOT NULL PRIMARY KEY,NAME VARCHAR(255)) CREATE TABLE PROJECT_EMPLOYEE(PROJECTS_ID INTEGER NOT NULL,EMPLOYEES_ID INTEGER NOT NULL, CONSTRAINT FKD1E6E154CD4FF0E4 FOREIGN KEY(PROJECTS_ID) REFERENCES PROJECT(ID), CONSTRAINT FKD1E6E154BF508158 FOREIGN KEY(EMPLOYEES_ID) REFERENCES EMPLOYEE(ID))
関連テーブルの外部キーのカラム名ですが、PROJECTS_IDとかEMPLOYEES_IDとか複数形になるのですね(エンティティクラスのフィールド名が使われているからなんですが)。ちょっと違和感。
あと、例ではProjectがリレーションシップの所有側ですが、所有側をProjectではなくEmployeeにしても関連テーブルの名前がEMPLOYEE_PROJECTになるだけでどっちでもいいのかもしれないです。
実際に動かしてみます。
まずデータを作ります。いつもながら適当なデータ...。
INSERT INTO EMPLOYEE VALUES(1,'ゴン'); INSERT INTO EMPLOYEE VALUES(2,'うさはな'); INSERT INTO EMPLOYEE VALUES(3, 'タクヤ'); INSERT INTO PROJECT VALUES(1, 'Javaプロジェクト'); INSERT INTO PROJECT VALUES(2, 'dot Netプロジェクト'); INSERT INTO PROJECT_EMPLOYEE VALUES(1, 1); INSERT INTO PROJECT_EMPLOYEE VALUES(1, 2); INSERT INTO PROJECT_EMPLOYEE VALUES(1, 3); INSERT INTO PROJECT_EMPLOYEE VALUES(2, 1); INSERT INTO PROJECT_EMPLOYEE VALUES(2, 2);
次のプログラムを使ってエンティティを取得してみます。
@Stateless public class ClientBean implements Client { @PersistenceContext private EntityManager em; public void main() { // Projectからたどる System.out.println("** Projectからたどる **"); Project project = em.find(Project.class, 1); System.out.println(project.getName()); for (Employee e : project.getEmployees()) { System.out.println(" " + e.getName()); } // Employeeからたどる System.out.println("** Employeeからたどる **"); Employee employee = em.find(Employee.class, 1); System.out.println(employee.getName()); for (Project p : employee.getProjects()) { System.out.println(" " + p.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のログ出力含む)です。
** Projectからたどる ** Hibernate: select project0_.id as id1_0_, project0_.name as name1_0_ from Project project0_ where project0_.id=? Javaプロジェクト Hibernate: select employees0_.projects_id as projects1_0_, employees0_.employees_id as employees2_0_ from Project_Employee employees0_ where employees0_.projects_id=? Hibernate: select employee0_.id as id0_0_, employee0_.name as name0_0_ from Employee employee0_ where employee0_.id=? ゴン Hibernate: select employee0_.id as id0_0_, employee0_.name as name0_0_ from Employee employee0_ where employee0_.id=? うさはな Hibernate: select employee0_.id as id0_0_, employee0_.name as name0_0_ from Employee employee0_ where employee0_.id=? タクヤ ** Employeeからたどる ** ゴン Hibernate: select projects0_.employees_id as employees2_0_, projects0_.projects_id as projects1_0_ from Project_Employee projects0_ where projects0_.employees_id=? Javaプロジェクト Hibernate: select project0_.id as id1_0_, project0_.name as name1_0_ from Project project0_ where project0_.id=? dot Netプロジェクト
ProjectからもEmployeeからもたどれました。思いっきりLazy Loadingされてる模様。実行されたSQLから察するにEmployeeからたどった場合はProjectからたどったときにキャッシュされた値(ゴン、Javaプロジェクト)が使われているようです。