JPA + EJB + JSF による Web アプリケーション(その 6)
エンティティ間の結合を表現する
現在サンプルとして使用しているデータベースにおいて、担当者マスタテーブルには MGR_ID
というカラムがあります。これは外部キーとして同じ担当者マスタテーブルの担当者ID
カラムを参照しており、上司である担当者の担当者IDを入力することになっています。これを JPA で表現してみましょう。
package jp.mydns.akanekodou.entity; import java.io.Serializable; import java.util.Date; import java.util.List; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Table; import javax.persistence.Id; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.OneToMany; import javax.persistence.ManyToOne; import javax.persistence.JoinColumn; @Entity @Table(name = "担当者マスタ") public class Employee implements Serializable { private static final long serialVersionUID = 1L; @Id @Column(name = "担当者ID") @GeneratedValue private int id; @Column(name = "担当者名") private String name; @Column(name = "ふりがな") private String phonetic; @ManyToOne @JoinColumn(name = "MGR_ID", referencedColumnName="担当者ID") private Employee manager; @OneToMany(mappedBy = "manager", fetch = FetchType.EAGER) private List<Employee> assistants; @Column(name = "生年月日") private Date birthday; @Column(name = "性別") private int sex; public Employee() { } public Employee( String name, String phonetic, Employee manager, Date birthday, int sex ) { this.name = name; this.phonetic = phonetic; this.manager = manager; this.birthday = birthday; this.sex = sex; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhonetic() { return phonetic; } public void setPhonetic(String phonetic) { this.phonetic = phonetic; } public Employee getManager() { return manager; } public List<Employee> getAssistants() { return assistants; } public void setAssistants(List<Employee> assistants) { this.assistants = assistants; } public void setManager(Employee manager) { this.manager = manager; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } }
ここで OneToMany
, ManyToOne
という新しいアノテーションが登場しました。上司一人に対して部下数人という関係ですので上司を示すプロパティに ManyToOne
を、部下を示すプロパティに OneToMany
を使います(1 対 1 の場合は OneToOne
を使います)。結合の方法を明示するために JoinColumn
を使います。今回は MGR_ID
カラムを使って、担当者ID
カラムを参照して Employee
エンティティを「ぶら下げる」形になります。Employee
が Employee
型をプロパティとして持つというちょっと奇妙な格好ですが、自己参照ですので必然的にこうなります。
OneToMany
の mappedBy
は、参照される側のプロパティを示します。この場合は部下の List
ですから参照されるのは上司(manager)ですね。fetch
はデフォルトの LAZY
ではなく EAGER
にします((LAZY
では実際に参照される段階の時に関連エンティティのリストを取得することになるため、セッションが閉じてから参照を試みるとエラーになる。))。
エンティティを作成したら persistence.xml を修正して、Employee
を追加しておきましょう。
EJB による DAO 層の実装
続いて DAO 層を EJB で実装します。
package jp.mydns.akanekodou.dao; import javax.ejb.Local; import java.util.List; import jp.mydns.akanekodou.entity.Employee; @Local public interface EmployeeDAO { List<Employee> all(); }
package jp.mydns.akanekodou.dao; import javax.ejb.Stateless; import javax.persistence.PersistenceContext; import javax.persistence.EntityManager; import javax.persistence.Query; import java.util.List; import jp.mydns.akanekodou.entity.Employee; @Stateless public class EmployeeDAOImpl implements EmployeeDAO { @PersistenceContext EntityManager manager; @Override public List<Employee> all() { Query query = manager.createQuery("from Employee"); @SuppressWarnings("unchecked") List<Employee> result = query.getResultList(); return result; } }
今回は全件取得メソッドのみ実装しました。
表示用の Facelets と管理 Bean の作成は次回に。