Entity Beanを使ってみます。
EJB3.0では、Entity BeanはもはやEJBではありません。
通常のBeanクラスをEntity Beanとして使い、
それを扱うDAOクラスをEJBとして使います。
まずは、Entity Beanを作成します。
Entity.java
import java.io.Serializable;
import javax.ejb.Entity;
import javax.ejb.GeneratorType;
import javax.ejb.Id;
@Entity
public class Entity implements Serializable {
public Entity {}
public Entity(String name) {
this.name = name;
}
@Id(generate = GeneratorType.AUTO)
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;
}
private int id;
private String name;
}
通常のBeanクラスとほとんど変わりません。
いくつかのAnnotationを追加するだけです。
では、一つずつ説明します。
そのものずばり、対象とするクラスをEntity Beanとして使うことを指示します。
Entity Beanのidentifier(識別子)に関する情報を定義します。
IDとするカラムのgetterメソッドに付加する必要があります。
DB上では、プライマリキー(Primary Key)として実装されます。
IDの生成方法を定義します。
@Id(generate = GeneratorType.AUTO)
上の例では、IDを自動生成します。
IDを生成するジェネレータを定義します。
詳細は不明です。
@Id(generate=SEQUENCE, generator="CUST_SEQ")
対象とするEntity BeanのDBテーブルに関する情報を定義します。
DBテーブル名を定義します。
省略すると、クラス名から自動生成されます。
@Table(name = "CUSTOMER")
DBのカタログ名を定義します。
カタログとは、JDBCで定められているキーワードです。
実装方法はDBに依存します。
@Table(catalog = "CATALOG")
DBのスキーマ名を定義します。
スキーマとは、JDBCで定められているキーワードです。
実装方法はDBに依存します。
@Table(schema = "SCHEMA")
テーブル定義に関する情報を定義します。
プライマリキーの有無やカラム名などを定義します。
Preview版では未実装のようです。
@Table(uniqueConstraints={
@UniqueConstraint(primary=true, columnNames={"ID", "NAME"
})
このEntity Beanは、「id」「name」という2つのフィールドを持ちます。
idはプライマリキーとなり、自動生成(AUTO_INCREMENT)されます。
nameは任意の文字列を格納するフィールドです。
なお、EntityBeanに任意のコンストラクタを作成することは可能ですが
デフォルト(引数無し)コンストラクタは必ず作成する必要があります。
では次に、このEntity Beanを扱うDAOクラスを作成します。
@Remote
public interface EntityDAO {
int create(String name);
Entity find(int id);
void merge(Entity entity);
}
@Stateless
public class EntityDAOBean implements EntityDAO {
@Inject
private EntityManager manager;
public int create(String name) {
Entity entity = new Entity(name);
manager.create(entity);
return entity.getId();
}
public Entity find(int id) {
return manager.find(Entity.class, id);
}
public void merge(Entity entity) {
manager.merge(entity);
}
}
今回初めて登場した @Inject Annotationについて説明します。
EJBから別のEJBを使用するときに使います。
通常、EJBはJNDIにより管理されているので
EJBを取得するにはInitialContextを生成してlookupして…という手順が必要です。
しかしこの処理は面倒かつ共通化できるものなので、Annotationが用意されています。
具体的には、EJBのフィールドを定義したら
その前にAnnotationを付けるだけです。
@Inject private EntityManager manager;
こうすることによって、常にmanagerにはEJB(へのインターフェイス)がセットされることが
保証されます。
まずはcreate。文字通り、Entity Beanを生成します。
処理内容は、Entity Beanのインスタンスを生成して
EntityManagerに登録します。これで、DBにレコードが格納されます。
最後にプライマリキー値を返します。
次はfind。これも文字通り、プライマリキーからEntity Beanを検索します。
EntityManagerを使うことによって、処理内容はたった1行で済みます。
最後にmerge。これは、レコードの内容を変更したときに使います。
具体的な使い方は後述するClientのところで説明します。
EntityDAO ejb = (EntityDAO) ctx.lookup(EntityDAO.class.getName());
int id = ejb.create("jon");
int id2 = ejb.create("jon");
Entity entity = ejb.find(id2);
entity.setName("jon2");
ejb.merge(entity);
簡単に説明します。
まず「jon」という名前のEntity Beanを一つ作成します。
idは自動生成なので、おそらく「1」が割り当てられているはずです。
次に、もう一つEntity Beanを作成します。
そして、findメソッドを使ってこのEntity Beanを取得します。
setNameメソッドを呼び出し、名前を「jon2」に変更します。
最後に、mergeメソッドを呼び出します。
mergeメソッドを呼び出さないと、一体どうなるでしょうか。
この場合、2つ目に作成したBeanの名前は「jon」のままです。
つまり、setNameメソッドの結果がDBに反映されません。
ただし、entityインスタンスのnameフィールドは「jon2」になっています。
何故こんな作りになっているかというと、
パフォーマンスを良くする為です。
以前までのEJBでは、setNameメソッドを呼んだ段階で
DBに書き込み処理を行っていました。
これは一見便利ですが、もしsetAddressやsetTelなど複数のメソッドを呼んだ場合
その度にSQL文が発行されることになるのです。
この処理は非常に無駄があるので、これを解消するために
DTOというオブジェクトを別途用意してこれを介して更新処理を行う必要がありました。
entity.set(dtoObject);
しかし、DTO(Data Transfer Object)という概念は極めて良くない事なのです。
何故なら、Entity Beanクラスと全く同じようなクラスの複製を作ることになるからです。
Entity Beanを変更したらDTOも同じように変更しなければならないので
保守性が非常に悪くなります。
このような事情があって、EJB3.0では明示的にmergeメソッドを呼び出さない限り
Entity Beanの内容はDBに反映されません。
その代わり、DTOオブジェクトを作る必要は無くなりました。