Oracle Containers for J2EE Enterprise JavaBeans開発者ガイド 10g(10.1.3.1.0) B31852-03 |
|
この章では、次のようなクライアントからのEJBへのアクセス方法を説明します。
詳細は、次を参照してください。
Enterprise Beanには、次のような様々なクライアントからアクセスできます。
Enterprise Bean、リソースまたは環境変数へのアクセス方法は、クライアントのタイプおよびアプリケーションのアセンブルとデプロイの方法によって決まります。
詳細は、「クライアントの構成」を参照してください。
1つのEnterprise Bean(ソースEnterprise Beanと呼ぶ)が別のEnterprise Bean(ターゲットEnterprise Beanと呼ぶ)にアクセスする場合、ソースEnterprise BeanはターゲットEnterprise Beanのクライアントです。
EJB 3.0を使用している場合は、アノテーションおよび依存性注入を通じて、OC4Jはターゲット参照に対応するインスタンス変数を初期化します。
EJB 2.1を使用している場合、このシナリオではJNDIルックアップを使用する必要があります。
スタンドアロンJavaクライアントは、OC4Jの外部で実行し、OC4JにデプロイされるEJBリソースにアクセスするクライアントです。
通常、スタンドアロンJavaクライアントは、Java RMIコールを利用してEJBリソースにアクセスします。スタンドアロンJavaクライアントは、OC4Jで実施されるセキュリティおよび認証の要件を満たすようにコーディングする必要があります。
デフォルトでは、OC4Jは設定範囲内でRMIポートを動的に割り当てるように構成されます。このリリースでは、厳密なRMIポートを指定せずに、スタンドアロンJavaクライアントからOC4JでデプロイされるEnterprise Beanをルックアップできます。厳密なポート番号を使用するようにOC4Jを構成する必要はありません。
EJB 3.0を使用している場合、スタンドアロンJavaクライアントではアノテーションおよび依存性注入がサポートされないことに注意してください。
EJB 2.1を使用している場合は、このシナリオに適応するように初期コンテキストを構成する必要があります(「RMIを使用した、スタンドアロンJavaクライアントからのEJB 2.1 Enterprise Beanへのアクセス」を参照)。
サーブレットまたはJSPはEnterprise Beanにアクセスできます。
このリリースでは、OC4JによりWeb層でのアノテーションおよびリソース・インジェクションがサポートされます(「Web層でのアノテーション」を参照)。
依存性注入は、EJB 3.0アプリケーションでサーブレットまたはJSPクライアントから使用できます。
JNDIルックアップは、EJB 3.0アプリケーションとEJB 2.1アプリケーションの両方でサーブレットまたはJSPクライアントから使用できます。
クライアントからEnterprise Beanにアクセスする前に、次の点を考慮する必要があります。
表29-1に、クライアントのルックアップ対象に応じてクライアントにインストールする必要のあるOC4J固有のJARファイルをリストします。「ソース」列は、<
OC4J_HOME
>
から必要なJARのコピーを取得する場所を示します。
クライアント・クラスパスに含める必要があるのは、oc4jclient.jar
のみです。クライアントにより必要とされる他のすべてのJARファイルは、oc4jclient.jar
のマニフェスト・クラスパスで参照されます。
OC4J JAR | ソース(<OC4J_HOME>に対する相対パス) | EJBルックアップ | JMSコネクタ・ルックアップ | OEMS JMSルックアップ | OEMS JMSデータベース・ルックアップ |
---|---|---|---|---|---|
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
|
|
![]() |
![]() |
![]() |
![]() |
1
Context.PROVIDER_URL でのJNDIルックアップでopmn:ormi 接頭辞を使用することを計画している場合にのみ必要です(「Oracle初期コンテキスト・ファクトリの構成」を参照)。 |
これらのJARファイルのいずれかをブラウザにダウンロードする場合は、特定の権限を付与する必要があります(「ブラウザにおける権限の付与」を参照)。
初期コンテキスト・ファクトリを使用して、初期コンテキスト(JNDIネームスペースへの参照)を取得します。初期コンテキストを使用すると、JNDI APIを使用してEnterprise Bean、リソース・マネージャのコネクション・ファクトリ、環境変数、またはJNDIでアクセス可能なその他のオブジェクトをルックアップできます。使用する初期コンテキスト・ファクトリのタイプは、使用しているクライアント・タイプおよびOC4Jの使用方法(スタンドアロンまたはOracle Application Serverの一部として)によって決まります(「初期コンテキスト・ファクトリの構成」を参照)。
クライアントとターゲットEnterprise Beanが同一JVM上になく、同じアプリケーションにデプロイされず、ターゲットEJBアプリケーションがクライアントの親でない場合、クライアントでは、ターゲットEnterprise Beanにアクセスする前に資格証明を指定する必要があります(「EJBクライアントの資格証明の指定」を参照)。
EJB 3.0では、EJBクライアントでEJB 3.0 Enterprise Beanまたはリソースにアクセスするために、事前定義の環境参照でJNDIルックアップを行うかわりに、アノテーション、リソース・インジェクションおよびデフォルトのJNDI名(クラス名およびインタフェース名に基づく)を使用できます。
EJB 2.1またはEJB 3.0(スタンドアロンJavaクライアントの場合)では、Enterprise Beanまたはリソースにアクセスするには、事前定義の環境参照でJNDIルックアップを行う必要があります(「環境参照の構成」を参照)。EJB 2.1 Enterprise Beanまたはリソースにアクセスするには、適切な事前定義環境参照(実際または論理、ローカルまたはリモート)を選択し、JNDI初期コンテキスト(「初期コンテキスト・ファクトリ・クラスの選択」を参照)を使用してそれをルックアップします。
クライアント実装からの参照によりEnterprise Beanにアクセスする場合は、EJBデプロイメント・ディスクリプタで定義されている<ejb-ref-name
>を使用してJNDIルックアップを実行します。ターゲットEnterprise BeanへのEJB参照の定義の詳細は、「EJB環境参照」を参照してください。
表29-2に、参照にjava:comp/env/ejb/
という接頭辞を付ける場合を示します。これは、コンテナがデプロイメント・ディスクリプタで定義されたEJB参照を入れる場所です。
クライアント | 初期コンテキスト・ファクトリ | 接頭辞の使用方法 |
---|---|---|
|
未使用 |
|
|
必須 |
|
|
未使用 |
例29-1に、java:comp/env/ejb/
接頭辞を使用して論理名ejb/HelloWorld
でEnterprise Beanをルックアップする方法を示し、例29-2に、接頭辞を使用せずにこのEnterprise Beanをルックアップする方法を示します。
InitialContext ic = new InitialContext(); HelloHome hh = (HelloHome)ic.lookup("java:comp/env/ejb/HelloWorld");例29-2 接頭辞を使用しないEnterprise Beanのルックアップ
InitialContext ic = new InitialContext(); HelloHome hh = (HelloHome)ic.lookup("ejb/HelloWorld");
JNDIからBeanインスタンスを直接ルックアップ(またはEJB 3.0 EJBクライアントでリソース・インジェクションを使用)し、ホーム・インタフェースを使用せずにBeanインスタンスを取得できます。<home>
または<local-home>
要素がEJB参照から削除される場合、BeanインスタンスはホームではなくJNDIから返されます。
Beanインスタンスは、ホーム・インタフェースで引数なしのcreate
メソッドを実行することで作成されます。ステートフル・セッションBeanおよびエンティティBeanでもこのショートカットを使用できますが、これらのBeanには引数なしのcreate
メソッドが必要であり、ない場合はルックアップ時に例外がスローされます。
どちらの場合も、EJBビジネス・インタフェースへの参照の取得で使用される構文は、ビジネス・インタフェースがローカルとリモートのどちらであるかに依存しません。リモート・アクセスの場合、参照されるEnterprise BeanおよびEJBコンテナの実際の場所は、一般にBeanのリモート・ビジネス・インタフェースを使用しているクライアントに対して透過的です。
EJB 3.0を使用している場合は、リソース・インジェクション(「アノテーションの使用方法」を参照)またはInitialContext
(「初期コンテキストの使用方法」を参照)を使用してEnterprise Beanをルックアップできます。
別の方法として、OC4J固有のアノテーションまたはデプロイXMLを使用してEJB 3.0への環境参照を定義できます(「EJB環境参照」を参照)。
例29-3に、アノテーションおよび依存性注入を使用してEJBクライアントからEJB 3.0 Enterprise Beanにアクセスする方法を示します。
@EJB AdminService bean;
public void privilegedTask() {
bean.adminTask();
}
この項の内容は次のとおりです。
詳細は、「初期コンテキスト・ファクトリの構成」を参照してください。
ejb-ref
を使用してEnterprise Beanのリモート・インタフェースをルックアップするには、次のようにします。
ejb-jar.xml
ファイルでEnterprise Beanのejb-ref
要素を定義します。<ejb-ref>
<ejb-ref-name>ejb/Test</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>Test</local>
</ejb-ref>
詳細は、「リモートEJBへの環境参照の構成: クラスタ化または結合されたWeb層およびEJB層」を参照してください。
ejb-ref-name
要素および適切な接頭辞(必要な場合)を使用してEnterprise Beanをルックアップします。InitialContext ic = new InitialContext();
Cart cart = (Cart)ic.lookup("java:comp/env/ejb/Test");
詳細は、「初期コンテキスト・ファクトリの構成」を参照してください。
location
を使用してEJBのリモート・インタフェースをルックアップするには、次のようにします。
orion-ejb-jar.xml
ファイル内のentity-deployment
要素のlocation
属性を定義します。<entity-deployment name="Test" location="app/Test" ... > ... </entity-deployment>
location
属性のデフォルト値は、entity-deployment
の属性name
の値です。
location
を使用してEnterprise Beanをルックアップします。
InitialContext ic = new InitialContext();
Cart cart = (Cart)ic.lookup("java:comp/env/app/Test");
詳細は、「初期コンテキスト・ファクトリの構成」を参照してください。
ejb-local-ref
を使用してEJBのリモート・インタフェースをルックアップするには、次のようにします。
<ejb-local-ref>
<ejb-ref-name>ejb/Test</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>Test</local>
</ejb-local-ref>
詳細は、「ローカルEJBへの環境参照の構成」を参照してください。
ejb-ref-name
および適切な接頭辞(必要な場合)を使用してEnterprise Beanをルックアップします。InitialContext ic = new InitialContext();
Cart cart = (Cart)ctx.lookup("java:comp/env/ejb/Test");
詳細は、「初期コンテキスト・ファクトリの構成」を参照してください。
local-location
を使用してEJBのローカル・インタフェースをルックアップするには、次のようにします。
<entity-deployment name="Test" local-location="app/Test" ... > ... </entity-deployment>
local-location
のデフォルト値は、entity-deployment
の属性name
の値です。
local-location
を使用してEnterprise Beanをルックアップします。InitialContext ic = new InitialContext();
Cart cart = (Cart)ctx.lookup("java:comp/env/app/Test");
詳細は、「初期コンテキスト・ファクトリの構成」を参照してください。
通常、Enterprise Beanは、複数のEARファイル間、つまり異なるEARファイルにデプロイされたアプリケーション間で通信を行うことはできません。あるEnterprise Beanが、異なるEARファイルにデプロイされているEnterprise Beanにアクセスする唯一の方法は、Enterprise Beanをクライアントの親として宣言することです。子のみが親の中でメソッドを起動できます。
たとえば、Sales
およびInventory
という2つのEnterprise Beanがあり、それぞれ別のEARファイル内にデプロイされているとします。Sales
Enterprise Beanは、Inventory
Enterprise Beanを起動して十分なウィジェットが使用可能かどうかをチェックする必要があります。この2つのEnterprise Beanは異なるEARファイルにデプロイされているため、Sales
Enterprise BeanでInventory
Enterprise Beanを親として定義しないかぎり、Sales
Enterprise BeanはInventory
Enterprise Bean内でメソッドを起動できません。したがって、Inventory
Enterprise BeanをSales
Enterprise Beanの親として定義すると、Sales
Enterprise Beanは親の中でメソッドを起動できるようになります。
親を定義できるのは、デプロイ・ウィザードを使用してデプロイを行うときのみです。Beanの親アプリケーションの定義方法については、『Oracle Containers for J2EE構成および管理ガイド』の「admin.jarユーティリティの使用方法」の章の「アプリケーションのデプロイ/アンデプロイ」を参照してください。
EJB 3.0アプリケーションでは、javax.persistence.EntityManager
はエンティティの永続化およびデータベースからのエンティティのロードを行うためのランタイム・アクセス・ポイントです。
この項の内容は次のとおりです。
詳細は、「JPAエンティティの問合せ方法」を参照してください。
EntityManager
を使用する前に、EntityManager
インスタンスを取得する必要があります。エンティティ・マネージャの取得方法は、クライアント・タイプによって決まります
(「使用しているクライアントのタイプ」を参照)。
エンティティ・マネージャを取得するときに、永続性ユニットを指定します。永続性ユニットでは、使用するファクトリ、エンティティ・マネージャが管理できる永続管理クラス、使用するオブジェクト・リレーショナル・マッピング・メタデータなどの詳細を含む、エンティティ・マネージャの構成を定義します。クライアントが永続性ユニットの有効範囲内にある場合は、特定の永続性ユニットのエンティティ・マネージャのみ取得できます。詳細は、
「persistence.xmlファイルとは」を参照してください。
次の方法でエンティティ・マネージャを取得できます。
@PersistenceContext
アノテーションを使用して、EJB 3.0セッションBeanクライアント(ステートフルまたはステートレス・セッションBean、メッセージドリブンBean、サーブレットなど)でEntityManager
を注入できます。例29-12に示すように、unitName
属性を指定せずに@PersistenceContext
を使用して、OC4Jのデフォルト永続性ユニットを使用できます。
@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
@PersistenceContext protected EntityManager entityManager;
public void createEmployee(String fName, String lName) {
Employee employee = new Employee();
employee.setFirstName(fName);
employee.setLastName(lName);
entityManager.persist(employee);
}
...
}
詳細は、「OC4Jの永続性ユニットのデフォルトについて」を参照してください。
@PersistenceContext
アノテーションを使用して、EJB 3.0セッションBeanクライアント(ステートフルまたはステートレス・セッションBean、メッセージドリブンBean、サーブレットなど)でEntityManager
を注入できます。例29-13に示すように、@PersistenceContext
の属性unitName
を使用して、永続性ユニットを名前で指定できます。この場合は、persistence.xml
ファイルで永続性ユニットを構成する必要があります。
@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
@PersistenceContext(unitName="myPersistenceUnit") protected EntityManager entityManager;
public void createEmployee(String fName, String lName) {
Employee employee = new Employee();
employee.setFirstName(fName);
employee.setLastName(lName);
entityManager.persist(employee);
}
...
}
詳細は、次を参照してください。
別の方法として、例29-14に示すように、アノテーションを使用して永続性コンテキストを注入し、JNDIを使用してエンティティ・マネージャをルックアップできます。この場合は、persistence.xml
ファイルで永続性ユニットを定義する必要があります。
@PersistenceContext(
name="persistence/InventoryAppMgr",
unitName=InventoryManagement // defined in a persistence.xml file
)
@Stateless
public class InventoryManagerBean implements InventoryManager {
EJBContext ejbContext;
public void updateInventory(...) {
...
// obtain the initial JNDI context
Context initCtx = new InitialContext();
// perform JNDI lookup to obtain container-managed entity manager
javax.persistence.EntityManager entityManager = (javax.persistence.EntityManager)
initCtx.lookup("java:comp/env/persistence/InventoryAppMgr");
...
}
}
詳細は、次を参照してください。
このリリースでは、例29-15に示すように、@PersistenceContext
アノテーションを使用してサーブレットなどのWebクライアントにEntityManager
を注入できます。この例では、デフォルトのEntityManager
を注入していますが、例29-13のように名前付きエンティティ・マネージャを注入することもできます。詳細は、「Web層でのアノテーション」を参照してください。
@Resource
UserTransaction ut;
@PersistenceContext
EntityManager entityManager;
...
try {
ut.begin();
Employee employee = new Employee();
employee.setEmpNo(empId);
employee.setEname(name);
employee.setSal(sal);
entityManager.persist(employee);
ut.commit();
this.getServletContext().getRequestDispatcher(
"/jsp/success.jsp").forward(request, response);
}
catch(Exception e) {
...
}
アノテーションと注入をサポートしないクラス、つまりヘルパー・クラスでエンティティ・マネージャを取得するには、次の処理を行う必要があります。
persistence.xml
ファイルで永続性ユニットを定義します。詳細は、次を参照してください。
java:comp/env
)に出現します。これを行うには、次のいずれかの方法を使用します。
@PersistenceContext
アノテーションを使用します。
@PersistenceContext(name="helperPC", unitName="HelperPU") @Stateless public class EmployeeDemoSessionEJB implements EmployeeDemoSession { import com.acme.Helper; ... void doSomething() { Helper.createNewEmployee(); } }
@PersistenceContext
アノテーションで、次のものを指定します。
name
: 永続性コンテキストのルックアップに使用する名前
unitName
: 返されたエンティティ・マネージャの特性を定義する、手順1で作成した永続性ユニットの名前
persistence-context-ref
を使用しますpersistence-context-ref
で、次のものを指定します。
persistence-context-ref
: 永続性コンテキストのルックアップに使用する名前
persistence-unit-name
: 返されたエンティティ・マネージャの特性を定義する、手順1で作成した永続性ユニットの名前
public class Helper { ... int createNewEmployee() { UserTransaction ut = null; ... try { Context initCtx = new InitialContext(); ut = (UserTransaction)initCtx.lookup("java:comp/UserTransaction"); ut.begin(); Employee employee = new Employee(); employee.setEmpNo(empId); // obtain the initial JNDI context Context initCtx = new InitialContext(); javax.persistence.EntityManager entityManager = (javax.persistence.EntityManager)initCtx.lookup( "java:comp/env/helperPC" ); entityManager.persist(employee); ut.commit(); } catch(Exception e) { ... } } }
詳細は、「初期コンテキスト・ファクトリの構成」を参照してください。
EntityManager
の取得後に(「EntityManagerの取得」を参照)、新規エンティティ・インスタンスを作成するには、例29-16に示すように、エンティティObject
を渡すEntityManager
のメソッドpersist
を使用します。このメソッドをコールすると、メソッドは新規インスタンスにデータベースへの挿入をマークします。このメソッドは、渡したインスタンスと同じインスタンスを返します。
このメソッドは、トランザクション・コンテキスト内でコールする必要があります。
注意
新規エンティティでは、 |
@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
@PersistenceContext protected EntityManager entityManager;
...
public void createEmployee(String fName, String lName) {
Employee employee = new Employee();
employee.setFirstName(fName);
employee.setLastName(lName);
entityManager.persist(employee);
}
...
}
この項では、次のようにEntityManager
を使用してEJB 3.0エンティティを問い合せる方法について説明します。
詳細は、次を参照してください。
例29-17に示すように、主キーがわかっている場合は、EntityManagerのメソッドfindを使用して、問合せを作成しなくても、対応するエンティティをデータベースから取得できます。
@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
...
public void removeEmployee(Integer employeeId) {
Employee employee = (Employee)entityManager.find("Employee", employeeId);
...
entityManager.remove(employee);
}
...
}
例29-18「EntityManagerでの名前付き問合せの作成」に示すように、名前付き問合せ
(「JPA名前付き問合せの実装」を参照)を実装した後で、EntityManager
のメソッドcreateNamedQuery
を使用して実行時にその問合せを取得できます。名前付き問合せがパラメータを受け取る場合は、Query
のメソッドsetParameter
を使用してこれらのパラメータを設定します。
Query queryEmployeesByFirstName = entityManager.createNamedQuery(
"findAllEmployeesByFirstName"
);
queryEmployeeByFirstName.setParameter("firstName", "John");
Collection employees = queryEmployessByFirstName.getResultList();
オプションで、問合せヒントで問合せを構成し、JPA永続性プロバイダのベンダー拡張を使用できます(「JPA問合せでのTopLink問合せヒントの構成」を参照)。
例29-19に、EntityManager
のメソッドcreateQuery
を使用して実行時に非定型EJB QL問合せを作成する方法を示します。
Query queryEmployees = entityManager.createQuery(
"SELECT OBJECT(employee) FROM Employee employee"
);
例29-20に、EntityManager
のメソッドcreateQuery
を使用してfirstname
という名前のパラメータを受け取る非定型問合せを作成する方法を示します。パラメータは、Query
のメソッドsetParameter
を使用して設定します。
Query queryEmployees = entityManager.createQuery(
"SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName = :firstname"
);
queryEmployeeByFirstName.setParameter("firstName", "John");
オプションで、問合せヒントで問合せを構成し、JPA永続性プロバイダのベンダー拡張を使用できます(「JPA問合せでのTopLink問合せヒントの構成」を参照)。
例29-21に示すように、oracle.toplink.ejb.cmp3.EntityManager
のメソッドcreateQuery(Expression expression, Class resultType)
を使用して、TopLink Expression
に基づいて問合せを作成できます。
オプションで、問合せヒントで問合せを構成し、JPA永続性プロバイダのベンダー拡張を使用できます(「JPA問合せでのTopLink問合せヒントの構成」を参照)。
詳細は、『Oracle TopLink開発者ガイド』のTopLinkの式の理解に関する項を参照してください。
@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
...
public Collection findManyProjectsByQuery(Vector params) {
ExpressionBuilder builder = new ExpressionBuilder();
Query query = ((oracle.toplink.ejb.cmp3.EntityManager)em).createQuery(
builder.get("name").equals(builder.getParameter("projectName")),
Project.class);
query.setParameter("projectName", params.firstElement());
Collection projects = query.getResultList();
return projects;
}
...
}
例29-22に示すようにEntityManager
のメソッドcreateNativeQuery(String sqlString)
またはcreateNativeQuery(String sqlString, Class resultType)
を使用して、指定するネイティブSQL文字列に基づいて問合せを作成できます。
Query queryEmployees = entityManager.createNativeQuery(
"Select * from EMP_TABLE where Salary < 50000", Employee.class
);
例29-23に、EntityManager
のメソッドcreateNativeQuer(
String sqlString, Class resultClass)
を使用してsalary
という名前のパラメータを受け取る非定型ネイティブSQL問合せを作成する方法を示します。パラメータは、Query
のメソッドsetParameter
を使用して設定します。
Query queryEmployees = entityManager.createNativeQuery(
"Select * from EMP_TABLE where Salary < #salary", Employee.class
);
queryEmployeeByFirstName.setParameter("salary", 50000);
オプションで、問合せヒントで問合せを構成し、JPA永続性プロバイダのベンダー拡張を使用できます(「JPA問合せでのTopLink問合せヒントの構成」を参照)。
例29-24に示すように、複数の結果を返す問合せを実行するには、Query
のメソッドgetResultList
を使用します。このメソッドは、java.util.List
を返します。
Collection employees = queryEmployees.getResultList();
例29-25に示すように、1つの結果を返す問合せを実行するには、Query
のメソッドgetSingleResult
を使用します。このメソッドは、java.lang.Object
を返します。
Object obj = query.getSingleResult();
例29-26に示すように、エンティティを更新(変更または削除)する問合せを実行するには、Query
のメソッドexecuteUpdate
を使用します。このメソッドは、影響を受ける(更新または削除される)行数をint
として返します。
Query queryRenameCity = entityManager.createQuery(
"UPDATE Address add SET add.city = 'Ottawa' WHERE add.city = 'Nepean'");
int rowCount = queryRenameCity.executeUpdate();
エンティティ・インスタンスは、次のいずれかの方法で変更できます。
これらの操作は、トランザクション・コンテキスト内で実行する必要があります。現在のトランザクションのコミット時に、更新はデータベースにコミットされます。
コミット前に、トランザクション内でデータベースに更新を送信することもできます
(「フラッシュの使用方法」を参照)。
更新問合せ(「EntityManagerでの名前付き問合せの作成」または「EntityManagerでの動的Java永続性問合せ言語の問合せの作成」を参照)を作成し、EntityManager
を使用して問合せを実行します(「問合せの実行」を参照)。
EntityManagerを使用して、エンティティの検索または問合せを行います(「EntityManagerを使用したJPAエンティティの問合せ」を参照)。
エンティティのパブリックAPIを使用して、永続状態を変更します。
例29-27に示すように、EntityManager
のメソッドrefreshを使用して、エンティティ・インスタンスの現在の状態を、データベースからの現在コミットされている状態で上書きできます。
@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
...
public void undoUpdateEmployee(Integer employeeId) {
Employee employee = (Employee)entityManager.find("Employee", employeeId);
em.refresh(employee);
}
...
}
例29-28に示すように、EntityManager
のメソッドremove
を使用して、データベースからエンティティを削除できます。
@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
...
public void removeEmployee(Integer employeeId) {
Employee employee = (Employee)entityManager.find("Employee", employeeId);
...
entityManager.remove(employee);
}
...
}
例29-29に示すように、EntityManager
のメソッドflush
を使用して、トランザクションがコミットされる前にトランザクション内で更新をデータベースに送信できます。同じトランザクション内の後続の問合せでは、更新されたデータが返されます。この機能は、特定のトランザクションが複数の操作にまたがる場合に便利です。
@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
...
public void terminateEmployee(Integer employeeId, Date endDate) {
Employee employee = (Employee) entityManager.find("Employee", employeeId);
employee.getPeriod().setEndDate(endDate);
entityManager.flush();
}
...
}
EntityManager
は、永続性コンテキストを持つと言われます。EntityManager
インスタンスを使用してエンティティを作成(「新規エンティティ・インスタンスの作成」を参照)または検索(「EntityManagerを使用したJPAエンティティの問合せ」を参照)する場合、エンティティはそのEntityManager
の永続性コンテキストの一部であると言われます。
エンティティはEntityManager
の永続性コンテキストの一部ですが、これは永続性エンティティであると言われます。
エンティティがこの永続性コンテキストの一部でなくなった場合は、連結解除されたエンティティであると言われます。
エンティティは、永続性コンテキストの終了時または(たとえば、別のアプリケーション層への)エンティティのシリアライズ時に、永続性コンテキストから連結解除されます。
例29-30に示すように、EntityManager
のメソッドmerge
を使用して、連結解除されたエンティティの状態をEntityManager
の現在の永続性コンテキストにマージできます。
@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
...
public void updateAddress(Address addressExample) {
entityManager.merge(addressExample);
}
...
}
永続性コンテキストの詳細は、次を参照してください。
クライアントは、MDBに直接にはアクセスしません。かわりに、クライアントはMDBに関連付けられているJMS宛先(キューまたはトピック)を通じてメッセージを送信することによりMDBにアクセスします。
EJB 3.0を使用してJMS宛先にメッセージを送信するには、次のようにします。
これらのリソースは、事前定義の論理名またはJMSプロバイダの構成時に定義した明示的なJNDI名を使用して注入できます。この手順および例で示すように、論理名を使用することをお薦めします。
詳細は、次を参照してください。
キューに対するメッセージを受信している場合は、接続を開始します。
例29-31に、サーブレット・クライアントがキューにメッセージを送信する方法を示します。
public final class testResourceProvider extends HttpServlet {
private String resProvider = "myResProvider";
private HashMap msgMap = new HashMap();
// 1a. Rely on Servlet container to inject queue connection factory
@Resource(name=resProvider+"QueueConnectionFactories/myQCF")
private QueueConnectionFactory qcf;
// 1b. Rely on Servlet container to inject queue
@Resource(name=resProvider+"/Queues/rpTestQueue")
private Queue queue;
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
doPost(req, res);
}
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// Retrieve the name of the JMS provider from the request,
// which is to be used in creating the JNDI string for retrieval
String rp = req.getParameter ("provider");
if (rp != null) resProvider = rp;
try {
// 2a. Create queue connection using the connection factory
QueueConnection qconn = qcf.createQueueConnection();
// 2b. You are receiving messages, so start the connection
qconn.start();
// 3. Create a session over the queue connection
QueueSession sess = qconn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
// 4. Since this is for a queue, create a sender on top of the session
// This is used to send out the message over the queue
QueueSender snd = sess.createSender (q);
drainQueue (sess, q);
TextMessage msg = null;
// Send messages to queue
for (int i = 0; i < 3; i++) {
// 5. Create message
msg = sess.createTextMessage();
msg.setText ("TestMessage:" + i);
// Set property of the recipient to be the MDB
// and set the reply destination.
msg.setStringProperty ("RECIPIENT", "MDB");
msg.setJMSReplyTo(q);
// 6. Send the message using the sender
snd.send (msg);
// You can store the messages IDs and sent-time in a map (msgMap),
// so that when messages are received, you can verify if you
// *only* received those messages that you were
// expecting. See receiveFromMDB() method where msgMap gets used
msgMap.put( msg.getJMSMessageID(), new Long (msg.getJMSTimestamp()));
}
// receive a reply from the MDB
receiveFromMDB (sess, q);
// 7. Close sender, session, and connection for queue
snd.close();
sess.close();
qconn.close();
}
catch (Exception e) {
System.err.println ("** TEST FAILED **"+ e.toString());
e.printStackTrace();
}
finally {
}
}
// Receive any messages sent to you through the MDB
private void receiveFromMDB (QueueSession sess, Queue q)
throws Exception {
// The MDB sends out a message (as a reply) to this client. The MDB sets
// the receipient as CLIENT. Thus, You will only receive messages that have
// RECIPIENT set to 'CLIENT'
QueueReceiver rcv = sess.createReceiver (q, "RECIPIENT = 'CLIENT'");
int nrcvd = 0;
long trtimes = 0L;
long tctimes = 0L;
// First message needs to come from MDB.
// May take a little while receiving messages
for (Message msg = rcv.receive (30000); msg != null; msg = rcv.receive (30000)) {
nrcvd++;
String rcp = msg.getStringProperty ("RECIPIENT");
// Verify if message is in message Map
// Check the msgMap to see if this is the message that you are expecting
String corrid = msg.getJMSCorrelationID();
if (msgMap.containsKey(corrid)) {
msgMap.remove(corrid);
}
else {
System.err.println ("** received unexpected message [" + corrid + "] **");
}
}
rcv.close();
}
// Drain messages from queue
private int drainQueue (QueueSession sess, Queue q)
throws Exception {
QueueReceiver rcv = sess.createReceiver (q);
int nrcvd = 0;
// First drain any old messages from queue
for (Message msg = rcv.receive(1000); msg != null; msg = rcv.receive(1000))
nrcvd++;
rcv.close();
return nrcvd;
}
}
EJB 3.0セッションおよびメッセージドリブンBeanの場合は、OC4Jで提供されるEJBContext
にアクセスできます(「リソース・インジェクションの使用方法」を参照)。
詳細は、次を参照してください。
EJB 3.0 EJBクライアントでは、例29-32に示すように、@Resource
注入を使用してEJBContext
にアクセスできます。
@Resource SessionContext ctx;
この項の内容は次のとおりです。
あるサーバーで実行されているサーブレットが別のサーバーのEnterprise Beanに接続して通信する場合は、リモート複数層の状況になります。サーブレットとEnterprise Beanは、両方とも同じアプリケーションに含まれています。アプリケーションを2つの異なるサーバーにデプロイすると、通常、サーブレットはローカルEnterprise Beanを最初に検索します。
図29-1では、HelloBean
アプリケーションがサーバー1と2の両方にデプロイされています。サーバー1のサーブレットからサーバー2のEnterprise Beanへのコールのみを行うには、アプリケーションを両方のサーバーにデプロイする前に、アプリケーションでremote
属性を適切に設定する必要があります。
EJBモジュールのorion-application.xml
における<ejb-module>
要素のremote
属性は、このアプリケーションのEnterprise Beanがデプロイされているかどうかを示します。
orion-application.xml
ファイルの<ejb-module>
要素でremote=true
を設定してから、アプリケーションをデプロイする必要があります。アプリケーション内のEJBモジュールはデプロイされません。したがって、サーブレットはローカルでEnterprise Beanを検索しませんが、EJBリクエストに対してリモート・サーバーにアクセスします。
orion-application.xml
ファイルの<ejb-module>
要素でremote=false
を設定してから、アプリケーションをデプロイする必要があります。EJBモジュールも含めて、アプリケーションは通常どおりデプロイされます。remote
属性のデフォルトはfalse
です。したがって、remote
属性がtrue
でないことを確認し、アプリケーションを再度デプロイします。
rmi.xml
でRMIサーバー・データを指定します。このファイルの場所は、OC4J構成ファイルserver.xml
で指定します。デフォルトでは、この両方のファイルは<
ORACLE_HOME
>/j2ee/home/config
にインストールされます。詳細は、『Oracle Containers for J2EEサービス・ガイド』の「スタンドアロンOC4Jインストール環境でのRMIの構成」を参照してください。
opmn.xml
ファイルを編集して、このローカルRMIサーバーがRMIリクエストをリスニングするポート範囲を指定する必要があります。Oracle Application Server環境の構成ファイルへの手動変更は、各OC4Jインスタンスで手動で更新する必要があります。詳細は、『Oracle Containers for J2EEサービス・ガイド』の「Oracle Application Server環境でのRMIの構成」を参照してください。
java.naming.provider.url
およびjava.naming.factory.initial
を設定します。詳細は、次を参照してください。
複数のリモート・サーバーが構成されている場合、OC4Jはすべてのリモート・サーバーで目的のEJBアプリケーションを検索します。
詳細は、『Oracle Containers for J2EEサービス・ガイド』の「OC4JでのRemote Method Invocationの使用方法」を参照してください。
ローカル複数層の状況は、サーブレットとEnterprise Beanの両方が同じアプリケーションに含まれ、同じサーバーにデプロイされる場合に存在します。
EJBモジュールのorion-application.xml
における<ejb-module>
要素のremote
属性は、このアプリケーションのEnterprise Beanがデプロイされているかどうかを示します。
orion-application.xml
ファイルの<ejb-module>
要素でremote=false
を設定してから、アプリケーションをデプロイする必要があります。EJBモジュールも含めて、アプリケーションは通常どおりデプロイされます。remote
属性のデフォルトはfalse
です。
java.naming.provider.url
およびjava.naming.factory.initial
を設定します。詳細は、次を参照してください。
例29-33に、RMIポートを指定しなくても、OC4JでデプロイされるEnterprise BeanをルックアップするためにこのリリースでスタンドアロンJavaクライアント(「スタンドアロンJavaクライアント」を参照)から使用できるルックアップのタイプを示します。
例29-33では、ホストmyServer
で実行されているoc4j_inst1
という名前のOC4JインスタンスにデプロイされるJava EEアプリケーションejbsamples
で、MyCart
という名前のEnterprise Beanをルックアップする方法を示します。
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"oracle.j2ee.rmi.RMIInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "oc4jadmin");
env.put(Context.SECURITY_CREDENTIALS, "password");
env.put(Context.PROVIDER_URL,"opmn:ormi://myServer:oc4j_inst1/ejbsamples");
Context context = new InitialContext(env);
Object homeObject = context.lookup("MyCart");
CartHome home = (CartHome)PortableRemoteObject.narrow(homeObject,CartHome.class);
詳細は、次を参照してください。
EJB 3.0クライアントからEJB 2.1 Enterprise Beanにアクセスするには、次のようにします。
この例では、EJB 2.1 Scheduler
Beanのホームおよびリモート・インタフェースへの環境参照を構成します。ジョブ・スケジューラの詳細は、『Oracle Containers for J2EEジョブ・スケジューラ開発者ガイド』を参照してください。
<ejb-ref> <ejb-ref-name>ejb/scheduler</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home>oracle.ias.scheduler.SchedulerHome</home> <remote>oracle.ias.scheduler.SchedulerRemote</remote> </ejb-ref>
詳細は、第19章「JNDIサービスの構成」を参照してください。
EJB 3.0クライアントは、次のように様々な方法でEJB 2.1 Enterprise Beanにアクセスできます(これらの方法に限定されるわけではありません)。
@EJB
アノテーションを使用してEJB 2.1ホーム・インタフェースを注入します。この例では、@EJB
アノテーションのname属性をEJB 2.1 Enterprise Beanの<ejb-ref-name>
に設定します。
... public class MyEJB30Client { @EJB(name="ejb/scheduler") SchedulerHome home; public void bar() { home.create(); ... } }
<injection-target
>要素を使用してEJB 2.1ホーム・インタフェースを注入します。例29-36に、<injection-target>
要素をデプロイXMLに追加して、EJB 2.1ホーム・インタフェースをhome
というインスタンス変数に関連付ける方法を示します。例29-37に示すように、EJB 3.0クライアントのインスタンス変数home
は、デプロイ時にOC4Jによって適切に初期化されます。
<ejb-ref> <ejb-ref-name>ejb/scheduler</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home>oracle.ias.scheduler.SchedulerHome</home> <remote>oracle.ias.scheduler.SchedulerRemote</remote> <injection-target> <injection-target-name>home</injection-target-name> </injection-target> </ejb-ref>例29-37 インスタンス変数へのEJB 2.1ホーム・インタフェースの注入
... public class MyEJB30Client { SchedulerHome home; public void bar() { home.create(); ... } }
この例では、EJB 2.1 Enterprise Beanのjava:comp/env/
という接頭辞付きの<ejb-ref-name>
をルックアップします。
... public class MyEJB30Client { SchedulerHome home; public void bar() { InitalContext ic = new InitialContext(); home = ic.lookup("java:comp/env/ejb/scheduler"); home.create(); ... } }
通常、Enterprise Beanは、複数のEARファイル間、つまり異なるEARファイルにデプロイされたアプリケーション間で通信を行うことはできません。あるEnterprise Beanが、異なるEARファイルにデプロイされているEnterprise Beanにアクセスする唯一の方法は、Enterprise Beanをクライアントの親として宣言することです。子のみが親の中でメソッドを起動できます。
たとえば、Sales
およびInventory
という2つのEnterprise Beanがあり、それぞれ別のEARファイル内にデプロイされているとします。Sales
Enterprise Beanは、Inventory
Enterprise Beanを起動して十分なウィジェットが使用可能かどうかをチェックする必要があります。この2つのEnterprise Beanは異なるEARファイルにデプロイされているため、Sales
Enterprise BeanでInventory
Enterprise Beanを親として定義しないかぎり、Sales
Enterprise BeanはInventory
Enterprise Bean内でメソッドを起動できません。したがって、Inventory
Enterprise BeanをSales
Enterprise Beanの親として定義すると、Sales
Enterprise Beanは親の中でメソッドを起動できるようになります。
親を定義できるのは、デプロイ・ウィザードを使用してデプロイを行うときのみです。Beanの親アプリケーションの定義方法については、『Oracle Containers for J2EE構成および管理ガイド』の「admin.jarユーティリティの使用方法」の章の「アプリケーションのデプロイ/アンデプロイ」を参照してください。
クライアントは、MDBに直接にはアクセスしません。かわりに、クライアントはMDBに関連付けられているJMS宛先(キューまたはトピック)を通じてメッセージを送信することによりMDBにアクセスします。
EJB 2.1を使用してJMS宛先にメッセージを送信するには、次のようにします。
これらのリソースは、事前定義の論理名またはJMSプロバイダの構成時に定義した明示的なJNDI名を使用してルックアップできます。この手順および例で示すように、論理名を使用することをお薦めします。
詳細は、次を参照してください。
キューに対するメッセージを受信している場合は、接続を開始します。
例29-39に、サーブレット・クライアントがキューにメッセージを送信する方法を示します。
public final class testResourceProvider extends HttpServlet {
private String resProvider = "myResProvider";
private HashMap msgMap = new HashMap();
Context ctx = new InitialContext();
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
doPost(req, res);
}
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// Retrieve the name of the JMS provider from the request,
// which is to be used in creating the JNDI string for retrieval
String rp = req.getParameter ("provider");
if (rp != null) resProvider = rp;
try {
// 1a. Look up the Queue Connection Factory
QueueConnectionFactory qcf = (QueueConnectionFactory)
ctx.lookup("java:comp/resource/" + resProvider +
"/QueueConnectionFactories/myQCF");
// 1b. Lookup the Queue
Queue queue = (Queue)ctx.lookup("java:comp/resource/" +
resProvider + "/Queues/rpTestQueue");
// 2a. Create queue connection using the connection factory
QueueConnection qconn = qcf.createQueueConnection();
// 2a. You are receiving messages, so start the connection
qconn.start();
// 3. Create a session over the queue connection
QueueSession sess = qconn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
// 4. Since this is for a queue, create a sender on top of the session
//This is used to send out the message over the queue
QueueSender snd = sess.createSender (q);
drainQueue (sess, q);
TextMessage msg = null;
// Send msgs to queue
for (int i = 0; i < 3; i++) {
// 5. Create message
msg = sess.createTextMessage();
msg.setText ("TestMessage:" + i);
// Set property of the recipient to be the MDB
// and set the reply destination
msg.setStringProperty ("RECIPIENT", "MDB");
msg.setJMSReplyTo(q);
//6. Send the message using the sender
snd.send (msg);
// You can store the messages IDs and sent-time in a map (msgMap),
// so that when messages are received, you can verify if you
// *only* received those messages that you were
// expecting. See receiveFromMDB() method where msgMap gets used
msgMap.put( msg.getJMSMessageID(), new Long (msg.getJMSTimestamp()));
}
// receive a reply from the MDB
receiveFromMDB (sess, q);
// 7. Close sender, session, and connection for queue
snd.close();
sess.close();
qconn.close();
}
catch (Exception e) {
System.err.println ("** TEST FAILED **"+ e.toString());
e.printStackTrace();
}
finally {
}
}
// Receive any messages sent through the MDB
private void receiveFromMDB (QueueSession sess, Queue q)
throws Exception {
// The MDB sends out a message (as a reply) to this client. The MDB sets
// the receipient as CLIENT. Thus, you will only receive messages that have
// RECIPIENT set to 'CLIENT'
QueueReceiver rcv = sess.createReceiver (q, "RECIPIENT = 'CLIENT'");
int nrcvd = 0;
long trtimes = 0L;
long tctimes = 0L;
// First message needs to come from MDB. May take
// a while receiving messages
for (Message msg = rcv.receive (30000); msg != null; msg = rcv.receive (30000)) {
nrcvd++;
String rcp = msg.getStringProperty ("RECIPIENT");
// Verify if messages in message Map
// Check the msgMap to see if this is the message that you are expecting
String corrid = msg.getJMSCorrelationID();
if (msgMap.containsKey(corrid)) {
msgMap.remove(corrid);
}
else {
System.err.println ("** received unexpected message [" + corrid + "] **");
}
}
rcv.close();
}
// Drain messages from queue
private int drainQueue (QueueSession sess, Queue q)
throws Exception {
QueueReceiver rcv = sess.createReceiver (q);
int nrcvd = 0;
// First drain any old messages from queue
for (Message msg = rcv.receive(1000); msg != null; msg = rcv.receive(1000))
nrcvd++;
rcv.close();
return nrcvd;
}
}
EJB 2.1セッション、エンティティおよびメッセージドリブンBeanの場合は、Beanの実装時に適切なgetterおよびsetterメソッドを提供することにより、OC4Jで提供されるEJBContext
にアクセスできます。
詳細は、次を参照してください。
この項の内容は次のとおりです。
Enterprise Beanを実装する場合、またはEJBメソッドをコールするクライアント・コードを作成する場合、Enterprise Beanで使用されるパラメータの受渡し規則に注意する必要があります。
Beanメソッドに渡すパラメータ(またはBeanメソッドからの戻り値)には、シリアライズ可能なすべてのJavaタイプを使用可能です。int
、double
など、Javaのプリミティブ型は、シリアライズ可能です。java.io.Serializable
インタフェースを実装する非リモート・オブジェクトは、すべて受渡し可能です。パラメータとしてBeanに渡されるかBeanから返される非リモート・オブジェクトは、参照渡しではなく、値渡しされます。たとえば、次のようにBeanメソッドをコールしたとします。
public class theNumber { int x; } ... bean.method1(theNumber);
この場合、Bean内のmethod1()
は、theNumber
のコピーを受信します。BeanによってサーバーのtheNumber
オブジェクトの値が変更されても、値渡しのセマンティクスを使用しているため、クライアントにはこの変更は反映されません。
非リモート・オブジェクトが複合的である場合(複数のフィールドが含まれているクラスなど)、非静的で非一時的なフィールドのみコピーされます。
リモート・オブジェクトをパラメータとして渡す場合、リモート・オブジェクトのスタブが渡されます。パラメータとして渡されるリモート・オブジェクトは、リモート・インタフェースを拡張する必要があります。
次の項では、Beanへのパラメータの受渡しと、戻り値としてのリモート・オブジェクトについて説明します。
EmployeeBean
のメソッドgetEmployee
はEmpRecord
オブジェクトを返すため、このオブジェクトをアプリケーション内で定義しておく必要があります。この例では、EmpRecord
クラスは、EJBインタフェースと同じパッケージに含まれています。
クラスはpublic
として宣言されており、シリアライズされたリモート・オブジェクトとしてクライアントに値を返せるよう、java.io.Serializable
インタフェースを実装する必要があります。次のように宣言します。
package employee; public class EmpRecord implements java.io.Serializable { public String ename; public int empno; public double sal; }
この項の内容は次のとおりです。
Enterprise Beanにリモートでアクセスしようとし、javax.naming.NamingException
エラーが発生する場合、JNDIプロパティが正しく初期化されていない可能性があります。リモート・オブジェクトまたはリモート・サーブレットからEnterprise BeanにアクセスするときのJNDIプロパティの設定に関する説明は、「ロード・バランシング」を参照してください。
WebアプリケーションからリモートEnterprise Beanにアクセスするときに、「java.lang.NullPointerException: domain was null
」というエラーが表示されます。この場合、dedicated.rmicontext
がtrue
に設定されているEnterprise Beanにアクセスするときは、環境プロパティをクライアントに設定する必要があります。
次の例は、この追加環境プロパティを使用する方法を示しています。
Hashtable env = new Hashtable( ); env.put (Context.INITIAL_CONTEXT_FACTORY, "oracle.j2ee.rmi.RMIInitialContextFactory"); env.put (Context.SECURITY_PRINCIPAL, "oc4jadmin"); env.put (Context.SECURITY_CREDENTIALS, "oc4jadmin"); env.put (Context.PROVIDER_URL, "ormi://myhost-us/ejbsamples"); env.put ("dedicated.rmicontext", "true"); // for 9.0.2.1 and later Context context = new InitialContext (env);
dedicated.rmicontext
の詳細は、「ロード・バランシング」を参照してください。
デッドロックの原因が複数のBeanのコール・シーケンスにある場合、OC4Jはデッドロック状態を検出し、違反しているBeanの1つにあるデッドロック状態の詳細を示すRemote
例外をスローします。
|
![]() Copyright © 2002, 2008 Oracle Corporation. All Rights Reserved. |
|