作成 2002/11/9
更新 2002/11/27
このページはEJBについてのメモです。
Enterprise Java Beans。
EJBのしきたりに従って、開発することで、 アプリケーションの生産性、保守性、拡張性などが向上する。
EJBコンテナがシステムレベルのサービスを行ってくれるので、 アプリケーション開発者は作りたいドメインに専念できる。 トランザクションやセキュリティを (理解すれば?)簡単な設定で実現できる。
ただ、しきたりを憶えるのが大変。敷居が高い。初期教育コストが高い。 めんどくさい。システムの信頼性も拡張性も それほど重要でない場合には逆効果。 なので、なんでもかんでも使えばOKというわけでもない。
| ロール名 | 説明 |
|---|---|
| エンタープライズBeanプロバイダ | EJBをコーディングする人 |
| アプリケーションアセンブラ | Beanプロバイダの作ったEJBに
DD上でトランザクションやセキュリティを追加する。 |
| デプロイヤ | EJBをアプリケーションサーバにデプロイし、
リソースのマッピングを行う。 |
| システム管理者 | アプリケーションサーバを管理する |
| EJBサーバプロパイダ | EJBサーバを作る |
| EJBコンテナプロバイダ | EJBコンテナを作る |
サーバプロバイダ、コンテナプロバイダは、 アプリケーションサーバを作る人。
仕様上ロールは分かれているが、同じ人が複数のロールをこなしても可。 小さなチームだと1人4役になるでしょう。
EJBにはいくつかの種類があります。
セッションBeanは、ビジネスプロセスをモデル化したものです。 プロセスとは、例えば、検索する、受注するなどのロジックを指します。 セッションBeanはさらに、状態をもつステートフルセッションBeanと、 状態を持たない、ステートレスセッションBeanの種類があります、
エンティティーBeanは、ビジネスエンティティーをモデル化したものです。 エンティティーとは、例えばデータベースの1テーブルだったりします。 エンティティーBeanはさらに、永続性をコンテナにまかせる、 CMP(Container Managed Persistence : コンテナ管理の永続性) と BMP(Bean Managed Persistence : Bean管理の永続性) の種類があります。
エンティティーBeanの背後がデータベースの場合、 CMPはSQLはコンテナが自動的に作ります。 BMPはプログラマがSQLを書きます。 CMPが既製品、BMPがオーダメイドという感じです。
メッセージドリブンBeanは非同期メッセージを扱う場合に利用します。
ステート(状態)をもつかもたないかの違い。
ステートフルの代表例はショッピングカート
ステートレスの方が、ステートフルよりパフォーマンスがよいので、 どっちでもよければ、ステートレスを選択すべき。
InitialContext context = new InitialContext();
Object object = context.lookup("HelloBean");
HelloHome home = (HelloHome)javax.rmi.PortableRemoteObject.narrow(object, HelloHome.class);
Hello hello = home.create();
String message = hello.sayHello();
System.out.println(message);
InitialContextは引数なしの場合には、クラスパスからjndi.propertiesを検索する。
引数のPropertiesを指定した場合にはその値を使う。
lookupの引数にはEJBコンテナで指定したJNDI名を指定
narrowはナロー。訳すと狭める。プログラムの意味的にはクラスのタイプを保証すること。
Remote呼び指しには必須。Localインターフェイスの時には単にキャストで可。
よくあるエラーはClassCastException。
クライアントJARをクラスパスに含めていないため(スタブがないため)おこる。
スタブを動的に作成する仕組みのEJBコンテナでは、クライアントJARが不要(JBossとか)。
Handleを使うことで、一時的にシリアライズしたEJB参照の保存が可能。 保存したHandleを他のクライアントに渡すこともできる。
EJBHomeのハンドルHomeHanleと、EJBObject用のハンドルHandleがある。
使い分けは不明。
EJB1.1 エンティティーBean
CMPは既製品、BMPはオーダーメイド。
いろいろ決まりがある
public class ManBean implements EntityBean{
//フィールド
//EJB1.1のCMPではフィールドはpublicである必要があります。
public String id;
public String name;
public int age;
/**
* 返り値の型は主キークラスでCreateExceptionをthrowする必要があります。
* また返り値はnullを返す必要があります。
*/
public String ejbCreate(String id) throws CreateException{
this.id = id;
return null;
}
/**
* ejbCreateと同じ引数を持ちます。
*/
public void ejbPostCreate(String id){
}
//...
CMPと違い、データベースアクセスコードを自前で実装する。
ファインダメソッドもコード上に書く
public class ManBean implements EntityBean{
private EntityContext context;
//フィールド
//CMPじゃないので普通にprivate
private String id;
private String name;
private int age;
/**
* 返り値の型は主キークラスでCreateExceptionをthrowする必要があります。
* また返り値は主キーを返す必要があります。
*/
public String ejbCreate(String id) throws CreateException{
this.id = id;
String sql = "INSERT INTO " + TABLE + "(" +ID+ ") VALUES(\"" +id+ "\")";
executeUpdate(sql);
return id;//CMPじゃないので普通に主キーを返す
}
//...
| メソッド | 説明 |
|---|---|
| ejbCreate | DBにINSERTし、返り値に主キーを返す |
| ejbPostCreate | 依存オブジェクトなどを処理 |
| ejbFindByPrimaryKey | DBからSELECTし、存在すれば主キーを返す |
| その他のファインダメソッド | DBからSELECTし、主キーのコレクションを返す |
| ejbLoad | DBからSELECT |
| ejbRemove | DBからDELETE |
| ejbStore | DBにUPDATE |
| setEntityContext | コンテキストのセット |
| unsetEntityContext | コンテキストのアンセット |
| ejbActivate | EJBをアクティブ化する。
コンテキストから主キーを取得し、セット。 |
| ejbPassivate | EJBを非アクティブ化し、リソースを開放する。
例えば主キーにnullを代入する。 |
EJB2.0では1.1からCMPの書き方がかなり変わりました。 バージョンアップというよりは別物です。
新たに導入された仕様で以下の問題を解決できます。
| 1.1の時の問題点 | 説明 |
|---|---|
| RDBテーブルの関連が簡単に表現できない | 1.1では特殊な状況でもないのに、関連を表現するためにBMPにしなければなりませんでした。
2.0ではRelationにより関連を表現できます。 CMP2.0を使うことで、自前で工夫しなくても、エイリアシングの問題を解決できます。 |
| 遅い | Localインターフェイスが導入されました。
結合度の高い、ひとまとまりのEJBは、EJBからEJBにLocalインターフェイスでアクセスすることで、高速になります。 また、EJBコンテナの最適化が進み、速度の向上が見られます。 最近は一般的にBMPより、コンテナが最適化可能なCMPの方が高速になります。 |
| finderメソッドのクエリに移植性がない | 2.0ではQuerry Languageという共通言語で記述することにより、
EJBのポータビリティを高めます。 |
EJBクラスにフィールドは持たず、abstractのセッタ、ゲッタを定義します。
Container Managed Relationship。テーブル間の関連をCMPで表現できます。
よく分かってないが。。。
JMSのQueueを介して非同期にEJBを呼ぶ
よく使われるのは確認メール送信とか
リモート、ホームインターフェイスは不要で、 onMessage(Message)を実装するだけ
JMSメッセージングには以下の2つのモデルがある。
Point-to-Point →Queue
Publish/Subscribe →Topic
トピックのサブスクライブには以下の2つの方式がある。
恒久(Durable)
永続(Persistent)
InitialContext ctx = new InitialContext();
QueueConnectionFactory factory =
(QueueConnectionFactory)ctx.lookup("QueueConnectionFactory");
Queue queue =
(Queue)ctx.lookup("HelloQueue");
QueueConnection con = factory.createQueueConnection();
QueueSession session = con.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
MapMessage message = session.createMapMessage();
message.setString("word", "hello");
QueueSender sender = session.createSender(queue);
sender.send(message);
接続ファクトリと接続先の2つをルックアップして使うらしい。
リソースの物理的な位置をソースコードから分離するための仕組み
| 項目 | DD上の記述 | 説明 |
|---|---|---|
| 環境エントリ | env-entry | 単なる値へのマッピング |
| Bean参照 | ejb-ref/ejb-local-ref | 他のEJBへのマッピング |
| リソースマネージャ接続ファクトリ | resource-ref | データソース、JavaMailセッション、JMS接続、
URLリソース、レガシーリソースへのマッピング resource-refはリソースファクトリへの参照です。 例えばデータソース参照は、参照先はConnectionでなくDataSoruceです。 |
| リソース環境参照 | resource-env-ref | JMS送信先やJavaMailSessionへのマッピング |
| EJB | java:comp/env/ejb |
| データソース | java:comp/env/jdbc |
| JMS | java:comp/env/jms |
| URL | java:comp/env/url |
| コネクタAPI | java:comp/env/eis |
InitialContextの引数に直接JNDIを指定せず、
java:comp/env/〜にマッピングすることで、
後で(デプロイ時やEJB公開後)リソースを柔軟に変更可能。
なお、java:comp/env/〜のlookupは同じJ2EE環境からしか使えない。
コンテナ違いやコンテナなしのクライアントからは
直ルックアップします。
トランザクション
トランザクションがトランザクションたりうる特性。
| 特性 | 説明 |
|---|---|
| 原子性(Atomicity) | |
| 一貫性(Consistency) | |
| 分離性(Isolation) | |
| 持続性(Durability) |
並列処理の問題
| 説明 | |
|---|---|
| ダーティリード | 更新途中コミット前のデータが読まれる
(その後ロールバックされて、あらおかしい) |
| ノンリピータブルリード | |
| ファントムリード | 挿入、削除されたデータが読まれる(読まれない) |
分離レベルの設定
| 分離レベル | ダーティ リード | ノンリピータブル リード | ファントム リード | 説明 |
|---|---|---|---|---|
| TRANSACTION_NONE | × | × | × | トランザクションを行わない |
| TRANSACTION_READ_UNCOMMITED | × | × | × | |
| TRANSACTION_READ_COMMITED | ○ | × | × | |
| TRANSACTION_REPEATABLE_READ | ○ | ○ | × | |
| TRANSACTION_SERIALIZABLE | ○ | ○ | ○ |
CMTがコンテナ管理のトランザクション。 トランザクションの境界設定の方法は、 DDにトランザクション属性を記述することで行う。
BMTはBean管理のトランザクション。 トランザクションをサポートしないリソースへのアクセスや、 ステートフルセッションBeanで、トランザクション的な処理を 実装したいときに利用する。UserTransactionインターフェイスを使う。
なお、エンティティーBeanは必ずCMTらしい。
| 属性 | 説明 |
|---|---|
| Required | なければ作る。普通。 |
| RequiredNew | 新たに作る。 |
| Mandatory | ないとエラー。 |
| Supports | あれば使う。 |
| NotSupported | あっても使わない。 |
| Never | あったらエラー。 |
クライアントからUserTransactionを使う例
UserTransaction ut = (UserTransaction)ctx.lookup("java:comp/UserTransaction");
ut.begin();
//...
ut.commit();
java:comp/UserTransactionは決まり文句?
セッションBeanでCMTを実装する方法に SessionSynchronizationインターフェイスを使う方法がある。
| メソッド | 説明 |
|---|---|
| void afterBegin() | |
| void afterCompletion(boolean committed) | |
| void beforeCompletion() |
設定できるトランザクション属性はRequired、RequiredNew、Mandatoryのみ
| 用語 | 説明 |
|---|---|
| 認証 | |
| 許可 | |
| プリンシパル(主体)
Principal | 個々のユーザー |
| クレデンシャル | |
| レルム |
勉強しながら作ったサンプルです。 現状、かなりいいかげん。
| session.zip | シンプルセッションBean |
| cmp11.zip | シンプルCMP1.1 |
| bmp11.zip | シンプルBMP1.1 |
| cmp20.zip | シンプルCMP2.0 |
| cmr.zip | シンプルCMR |
| - | シンプルCMR 多重度 1:* |
| mdbean.zip | シンプルJMS |
| resref.zip | シンプルリソース参照 |
| trans.zip | トランザクション属性 |
| - | UserTransaction |
| sec.zip | シンプルセキュリティ |
| sec2.zip | シンプルセキュリティ2、シンプルEAR(clientjar+ejbjar) |
| helloear.zip | 2002/12/1
シンプルEAR(war+ejbjar) ビルドがイマイチ |
| handle.zip | 2002/11/26
ステートフルセッションBeanでHandleを使ってみた。 銀行預金Bean。サーバーがクラッシュすると預金もふっとぶっすね。 |