この章では、開発サイクル後半で最も一般的に使用される高度なTopLink問合せAPIのコールと手法について説明します。
この章の内容は次のとおりです。
使用可能な問合せAPIの詳細は、『Oracle Fusion Middleware Java API Reference for Oracle TopLink』を参照してください。
リダイレクト問合せは、名前付き問合せで、問合せの実行制御をアプリケーションに委譲します。リダイレクト問合せを使用すると、問合せの実装をstaticメソッドとしてコードで定義できます。
複雑な操作を実行する場合、問合せのリダイレクタをTopLinkの問合せフレームワークと組み合せることができます。
この項では、リダイレクト問合せの作成方法について説明します。
複雑な操作を実行する場合、問合せのリダイレクタをTopLinkの問合せフレームワークと組み合せることができます。リダイレクタを作成するには、oracle.toplink.queryframework.QueryRedirectorインタフェースを実装します。問合せメカニズムは、Object invokeQuery(DatabaseQuery query, Record arguments, Session session)メソッドを実行し、結果を待ちます。
TopLinkは、あらかじめ実装されたリダイレクタであるMethodBasedQueryRedirectorメソッドを提供します。このリダイレクタを使用するには、staticな起動メソッドを1つのクラスに作成し、setMethodName(String)コールを使用して、起動するメソッドを指定します。
例111-1 リダイレクト問合せ
ReadObjectQuery query = new ReadObjectQuery(Employee.class);
query.setName("findEmployeeByAnEmployee");
query.addArgument("employee");
MethodBaseQueryRedirector redirector = new
MethodBaseQueryRedirector(QueryRedirectorTest.class, "findEmployeeByAnEmployee");
query.setRedirector(redirector);
Descriptor descriptor = getSession().getDescriptor(query.getReferenceClass());
descriptor.getQueryManager().addQuery(query.getName(), query);
Vector arguments = new Vector();
arguments.addElement(employee);
objectFromDatabase =
getSession().executeQuery("findEmployeeByAnEmployee", Employee.class, arguments);
public class QueryRedirectorTest {
public static Object findEmployeeByAnEmployee(
DatabaseQuery query,
oracle.toplink.sessions.Record arguments,
oracle.toplink.sessions.Session
session) {
((ReadObjectQuery) query).setSelectionObject(arguments.get("employee"));
return session.executeQuery(query);
}
}
時間に関する問合せを作成するには、TopLinkで問合せに追加するAsOfClauseを指定します。AsOfClauseクラスは、履歴スキーマがタイムスタンプに基づいている場合に使用し、AsOfSCNClauseクラスは、履歴スキーマがデータベース・システムの変更数に基づいている場合に使用します。履歴セッションを取得する際にAsOfClauseを指定することにより、TopLinkですべての問合せに対して同じ句が追加されるようにするか、問合せごとにAsOfClauseを指定できます。
例111-2は、特定のAsOfClauseを使用する問合せの作成方法を示します。この問合せは、EmployeeディスクリプタのHistoryPolicy設定で記述されている適切な履歴表を使用して、timestampで指定された時点でEmployeeオブジェクトをすべて読み取ります。
フェッチ・グループは、ReadObjectQueryまたはReadAllQueryとともに使用できます。問合せを実行すると、TopLinkによりフェッチ・グループ内の属性のみが取得されます。除外された属性のいずれかに関するgetterメソッドをコールすると、TopLinkでは、このサブセットから除外されたすべての属性をフェッチする問合せが自動的に実行されます。
この項の内容は次のとおりです。
フェッチ・グループの詳細は、108.7.1.6項「フェッチ・グループとオブジェクト・レベルの読取り問合せ」を参照してください。
必要であれば、最大1つのフェッチ・グループをディスクリプタの参照クラスのデフォルトのフェッチ・グループとして指定できます。
例111-3に示すように、特定のフェッチ・グループを使用しないでReadObjectQueryまたはReadAllQueryを実行する場合、問合せを構成しないかぎり、TopLinkではデフォルトのフェッチ・グループが使用されます。
例111-3 フェッチ・グループのデフォルト動作の構成
// at the descriptor level FetchGroup group = new FetchGroup("nameOnly"); group.addAttribute("firstName"); group.addAttribute("lastName"); employeeDescriptor.getFetchGroupManager().addFetchGroup(group); // set the default fetch group employeeDescriptor.getFetchGroupManager().setDefaultFetchGroup(group); // when query1 is executed, the default fetch group applies ReadAllQuery query1 = new ReadAllQuery(Employee.class); // when query2 is executed, the default fetch group does not apply ReadAllQuery query2 = new ReadAllQuery(Employee.class); query2.setShouldUsedefaultFetchGroup(false);
例111-4は、以前、Employeeクラスのディスクリプタが所有するFetchGroupManagerに保存した、nameOnlyという名前のFetchGroupを持つEmployeeクラスで使用するReadObjectQueryの構成方法を示します。
例111-4 FetchGroupManagerを使用したフェッチ・グループによる問合せの構成
この例では、Employeeの属性firstNameおよびlastNameのみがフェッチされています。その他の属性についてEmployeeのメソッドgetをコールすると、TopLinkでは他の問合せを実行し、フェッチされていないすべての属性値を取得します。その後、getメソッドをコールすると、オブジェクトから直接値が返ります。
// create static fetch group at the descriptor level FetchGroup group = new FetchGroup("nameOnly"); group.addAttribute("firstName"); group.addAttribute("lastName"); descriptor.getFetchGroupManager().addFetchGroup(group); // use static fetch group at query level ReadAllQuery query = new ReadAllQuery(Employee.class); query.setFetchGroupName("nameOnly");
例111-5は、問合せを作成して実行する際のFetchGroupインスタンスを動的に作成する方法、およびそのFetchGroupを使用して直接問合せを構成する方法を示します。
この例では、firstName、lastNameおよびsalary属性のみがフェッチされています。その他の属性についてEmployeeのメソッドgetをコールすると、TopLinkでは他の問合せを実行し、フェッチされていないすべての属性値を取得します。その後、getメソッドをコールすると、オブジェクトから直接値が返ります。
例111-6は、オブジェクト・レベルの読取り問合せを作成して、読取り専用であることがわかっているデータを返す方法を示します。読取り専用データに対してこのような問合せを使用して、パフォーマンスを向上させることができます。
例111-6 読取り専用としてのObjectLevelReadQueryの構成
ReadAllQuery query = new ReadAllQuery(Employee.class); query.setIsReadOnly(true);
詳細は、次を参照してください。
インタフェースのディスクリプタを定義して問合せを有効にした場合、TopLinkではインタフェースに対する問合せが次のようにサポートされます。
インタフェースのインプリメンタが1つしかない場合、問合せでは具象クラスのインスタンスが返されます。
インタフェースのインプリメンタが複数ある場合、問合せでは実装するすべてのクラスのインスタンスが返されます。
あるクラスで実行する各問合せに自動的に式を追加するよう、問合せマネージャを設定できます。たとえば、特定のクラスの有効なインスタンスに対してデータベースをフィルタ処理する式を追加できます。
これは、次の目的で使用します。
論理的に削除されたオブジェクトをフィルタ処理する場合
2つの別々のクラスが継承を使用せずに単一の表を共有できるようにする場合
オブジェクトの履歴バージョンをフィルタ処理する場合
Javaを使用して、修正メソッド(119.35項「修正メソッドの構成」を参照)を作成することにより、追加の結合式を使用してディスクリプタを構成します。その後、例111-7に示すようにDescriptorQueryManagerのメソッドsetAdditionalJoinExpressionまたはsetMultipleTableJoinExpressionを使用します。
例111-7 結合式を含む問合せの登録
例111-7では、結合式が問合せからemployeeの無効なインスタンスをフィルタします。
public static void addToDescriptor(Descriptor descriptor) {
ExpressionBuilder builder = new ExpressionBuilder();
descriptor.getQueryManager().setAdditionalJoinExpression(
(builder.getField("EMP.STATUS").notEqual("DELETED")).and(
builder.getField("EMP.STATUS").notEqual("HISTORICAL"))
);
}
TopLinkは、可変1対1マッピングに対して直接問合せを行うためのメソッドを提供しません。このタイプのマッピングに対して問合せを行うには、次のようにTopLinkのDirectQueryKeysとTopLinkのReportQueriesを組み合せて、インタフェースを実装するクラスの問合せ選択基準を作成します。
インタフェースの可能なインプリメンタを問い合せるDirectQueryKeysを2つ作成します。
最初のDirectQueryKeyは、可変1対1マッピングのクラス・インジケータ・フィールド用です。
2番目のDirectQueryKeyは、インタフェースを実装するクラスまたは表の外部キー用です。
問合せ選択基準に含まれるインタフェースを実装する具象クラスごとにsubSelect文を作成します。
ReportQueryを実装します。
例111-8 DirectQueryKeysの作成
// The DirectQueryKeys as generated in the TopLink project Java
// source code from TopLink Workbench
…
descriptor.addDirectQueryKey("locationTypeCode","DEALLOCATION.DEALLOCATIONOBJECTTYPE");
descriptor.addDirectQueryKey("locationTypeId","DEALLOCATION.DEALLOCATIONOBJECTID");
Oracleデータベースを使用している場合は、次のOracleデータベース機能でTopLinkのサポートを利用できます。
Oracleヒント(111.9.1項「Oracleヒントの使用方法」を参照)
階層問合せ(111.9.2項「階層問合せの使用方法」を参照)
Oracleヒントとは、通常オプティマイザに任せる決定を開発者が下すことのできるOracleデータベースの機能です。開発者は、ヒントを使用して、結合文の結合順序やSQLコールの最適化方法などを指定します。
TopLinkの問合せフレームワークでは、次のAPIによってOracleヒントがサポートされます。
setHintString("/*[hints or comments]*/");
TopLinkでは、SELECT文、UPDATE文、INSERT文、DELETE文のいずれかの直後でヒントをコメントとしてSQL文字列に追加します。
ヒントを読取り問合せに追加するには、次のようにします。
ReadObjectQueryまたはReadAllQueryを作成します。
選択基準を設定します。
必要に応じてヒントを追加します。
たとえば、次のコードではFULLヒント(指定された表の全表スキャンを明示的に選択)が使用されています。
// Create the query and set Employee as its reference class ReadObjectQuery query = new ReadObjectQuery(Employee.class); // Retrieve ExpressionBuilder from the query ExpressionBuilder builder = query.getExpressionBuilder(); query.setSelectionCritera(builder.get("id").equal(new Integer(1)); // Add the hint query.setHintString("/*+ FULL */" );
このコードでは、次のSQLが生成されます。
SELECT /*+ FULL */ FROM EMPLOYEE WHERE ID=1
WRITE、INSERT、UPDATEおよびDELETEにヒントを追加するには、TopLinkの問合せフレームワークでこれらの操作のカスタム問合せを作成した後、必要に応じてヒントを指定します。詳細は、次を参照してください。
使用可能なヒントの詳細は、Oracleデータベースのドキュメントを参照してください。
階層問合せとは、階層順序に基づいてデータベースの行を選択することのできる、Oracleデータベースのメカニズムです。たとえば、特定の従業員の行、その従業員が管理する人々の行、その人々に管理される従業員の行などの順序で読取りを行う問合せを設計できます。
階層問合せを作成するには、setHierarchicalQueryClauseメソッドを使用します。このメソッドは、次のような3つのパラメータをとります。
setHierarchicalQueryClause(startWith, connectBy, orderSibling)
この式には、後述の3つのパラメータがすべて必要です。
式のstartWithパラメータには、階層の最初のオブジェクトを指定します。このパラメータは、OracleデータベースのSTART WITH句と同じ機能を持ちます。
startWithパラメータを含めるには、式を作成して適切なオブジェクトを指定し、その式をsetHierarchicalQueryClauseメソッドのパラメータとして渡します。階層のルート・オブジェクトを指定しない場合は、この値をnullに設定します。
connectByパラメータには、階層を作成するリレーションシップを指定します。このパラメータは、OracleデータベースのCONNECT BY句と同じ機能を持ちます。
式を作成してconnectByパラメータを指定し、その式をsetHierarchicalQueryClauseメソッドのパラメータとして渡します。このパラメータは階層の特性を定義するものであるため、setHierarchicalQueryClauseの実装には必須です。
式のorderSiblingパラメータには、階層内の兄弟関係にあるオブジェクトが問合せによって返される順序を指定します。このパラメータは、OracleデータベースのORDER SIBLINGS句と同じ機能を持ちます。
orderSiblingパラメータを含めるにはVectorを定義し、順序の基準を含めるにはaddElementメソッドを使用します。Vectorは、setHierarchicalQueryClauseメソッドの3番目のパラメータとして渡します。順序を指定しない場合は、この値をnullに設定します。
例111-9 階層問合せ
ReadAllQuery raq = new ReadAllQuery(Employee.class); // Specifies a START WITH expression Expression startExpr = expressionBuilder.get("id").equal(new Integer(1)); // Specifies a CONNECT BY expression Expression connectBy = expressionBuilder.get("managedEmployees"); // Specifies an ORDER SIBLINGS BY vector Vector order = new Vector(); order.addElement(expressionBuilder.get("lastName")); order.addElement(expressionBuilder.get("firstName")); raq.setHierarchicalQueryClause(startExpr, connectBy, order); Vector employees = uow.executeQuery(raq);
このコードでは、次のSQLが生成されます。
SELECT * FROM EMPLOYEE START WITH ID=1 CONNECT BY PRIOR ID=MANAGER_ID ORDER SIBLINGS BY LAST_NAME, FIRST_NAME
この項では、次のようなTopLinkでのEJBファインダの使用方法を説明します。
通常、TopLink問合せフレームワークを使用するエンティティBeanに対してファインダを作成するには、ファインダの定義、宣言および構成が必要です。
事前定義ファインダの場合(108.15.1項「事前定義ファインダ」を参照)、明示的にファインダを作成する必要はありません。
デフォルト・ファインダの場合(108.15.2項「デフォルト・ファインダ」を参照)、ファインダ・メソッドの定義のみが必要です。
TopLink問合せフレームワークを使用するエンティティBeanに対してファインダを作成するには、次のようにします。
エンティティBeanのremoteHomeまたはlocalHomeインタフェースでファインダ・メソッドを定義します。
コンテナ管理の永続性を備えたエンティティBeanの場合、そのエンティティBeanのHomeインタフェースでメソッドを定義します。
デフォルト・ファインダの場合(108.15.2項「デフォルト・ファインダ」を参照)、次のようにファインダを定義する必要があります。
<RETURN-TYPE> findBy<CMP-FIELD-NAME>(<CMP-FIELD-TYPE>)
<CMP-FIELD-NAME>の最初の文字は、大文字にする必要があります。
<RETURN-TYPE>は、単一のBeanタイプまたはCollectionです。
次に例を示します。
EmployeeBean (Integer id, String name)
EmployeeHome ..{
Employee findById(Integer id) throws...;
Collection findByName(String name) throws...;
}
ejb-jar.xmlファイルでファインダを宣言します(111.10.1.1項「ejb-jar.xmlファインダ・オプション」を参照)。
TopLink Workbenchを起動します。
ナビゲータでプロジェクト・アイコンをクリックし、「選択」→「プロジェクトのejb-jar.xmlからの更新」を選択してファインダを読み取ります。
Beanの「問合せ」→「名前付き問合せ」タブに移動します(109.3項「名前付き問合せの使用」を参照)。
ファインダを選択し、構成します。
|
注意: 事前定義ファインダfindOneByQueryおよびfindManyByQueryについては、クライアントが実行時に問合せを作成して、ファインダにパラメータとして渡します。このため、このファインダでは問合せオプションを構成しないでください。かわりに、ファインダに渡された問合せでオプションを構成します。事前定義ファインダの詳細は、108.15.1項「事前定義ファインダ」を参照してください。 |
必要に応じて、問合せの実装を作成します。一部の問合せオプションでは、ヘルパー・クラスに問合せをコードで定義する必要がありますが、一般的な問合せの場合は不要です。
TopLink CMPを使用する場合、エンティティBean自体ではなく、BeanのHomeインタフェースでファインダ・メソッドを定義します。TopLink CMPにはこの機能が用意されており、ファインダの作成とカスタマイズをいくつかの方法で行うことができます。EJBコンテナとTopLinkにより、実装が自動的に生成されます。
ejb-jar.xmlファイルには、Beanで使用されるファインダの定義など、プロジェクトのEJBエンティティBean情報が含まれています。ejb-jar.xmlファイルの作成とメンテナンスを行うには、テキスト・エディタまたはTopLink Workbenchを使用します。
entityタグにより、EJBエンティティBeanの定義がカプセル化されます。各Beanには専用のentityタグがあり、その中にはBeanファインダなどのBean機能を定義する他のタグがいくつか含まれています。
例111-10は、ejb-jar.xmlファイルに定義された一般的なファインダの構造を示します。
|
注意: EJB QLを使用して問合せを定義する際には、エスケープ文字と二重引用符(\")の組合せを使用してください。正しい問合せ構文の詳細は、「名前付き問合せの選択基準の構成」の最後の注意を参照してください。 |
例111-10 ejb-jar.xmlファイル内の単純なファインダ
<entity>...
<query>
<query-method>
<method-name>findLargeAccounts</method-name>
<method-params>
<method-param>double</method-param>
</method-params>
</query-method>
<ejb-ql><![CDATA[SELECT OBJECT(account) FROM AccountBean account WHERE
account.balance > ?1]]></ejb-ql>
</query>...</entity>
entityタグには、0個以上のquery要素が含まれています。各queryタグは、BeanのホームまたはローカルHomeインタフェースで定義されたファインダ・メソッドに対応しています。
|
注意: 次のように、両方のHomeインタフェースの間で1つの問合せを共有できます。
|
ejb-jar.xmlファイルのqueryセクションに定義される要素を次に示します。
description(オプション): ファインダの説明を提供します。
query-method: ファインダまたはejbSelect問合せのメソッドを指定します。
method-name: エンティティBean実装クラスのファインダまたは選択メソッドの名前を指定します。
method-params: メソッド・パラメータの完全修飾Javaタイプ名のリストを含みます。
method-param: メソッド・パラメータの完全修飾Javaタイプ名を含みます。
result-type-mapping(オプション): ejbSelectメソッドの問合せによって返される抽象スキーマ・タイプのマップ方法を指定します。タイプは、EJBLocalObjectまたはEJBObjectタイプにマップできます。有効な値はLocalまたはRemoteです。
ejb-ql: すべてのEJB QLファインダに使用されます。ファインダまたはejbSelect問合せを定義するEJB QL問合せ文字列を含みます。EJB QL以外のファインダの場合、この要素は空のまま残します。
TopLinkでは、ReadAllQueryなどのDatabaseQueryをとる事前定義のファインダを提供しています。この機能をBeanで使用するには、BeanのHomeインタフェースに次のファインダ定義を追加します。
public Collection findManyByQuery(ReadAllQuery query) throws RemoteException, FinderException; public <EJBLocal/Remote> findOneByQuery(ReadObjectQuery query) throws RemoteException, FinderException;
例111-11に示すように、ReadAllQueryファインダを実行するには、クライアントで問合せを作成します。
TopLinkの事前定義ファインダを含め、EJBファインダ・メソッドを名前付き問合せとして実装できます。詳細は、109.3項「名前付き問合せの使用」を参照してください。他のファインダの実行と同様に、このファインダを実行します。
TopLinkでは、Objectとして主キーをとる事前定義のファインダ(findByPrimaryKey)を用意しています。
EJB QLは、EJB 2.0の仕様で最初に定義された標準的な問合せ言語です。TopLinkでは、EJB QLがサポートされています。EJB QLファインダを使用すると、EJB QL文字列を問合せの実装として指定できます。
|
注意: EJB QLを使用して問合せを定義する際には、エスケープ文字と二重引用符(\")の組合せを使用してください。正しい問合せ構文の詳細は、119.7.1.3項「名前付き問合せの選択基準の構成」の最後の注意を参照してください。 |
EJB QLには次のような利点があります。
問合せのEJB 2.0および2.1標準です。
ほとんどの問合せの構成に使用できます。
EJB QLを使用すると、依存オブジェクト問合せを実装できます。
EJB QLの短所は、複雑な問合せを構成する場合に使用方法が難しいことです。
EJB QLファインダの作成手順:
LocalHomeまたはRemoteHomeインタフェースでファインダを宣言します。
TopLink Workbenchを起動します。
ejb-jar.xmlファイルを再インポートして、プロジェクトをファイルと同期化します。
TopLink Workbenchにより、プロジェクトとejb-jar.xmlファイルの間で変更内容が同期化されます。
パラメータを1つ必要とする単純なEJB QL問合せの例を次に示します。この例では、?1の疑問符(?)がパラメータを示しています。
SELECT OBJECT(employee) FROM Employee employee WHERE (employee.name =?1)
コンテナ管理の永続性を備えたエンティティBeanのEJB QLファインダの作成手順:
ejb-jar.xmlファイルでファインダを宣言し、ejb-qlタグにEJB QL文字列を入力します。
必要に応じて、HomeインタフェースまたはLocalHomeインタフェース、あるいはその両方でファインダを宣言します。
TopLink Workbenchを起動します。
ejb-jar.xmlファイルの場所を指定し、「ファイル」→「プロジェクトのejb-jar.xmlからの更新」を選択します。
Beanの「問合せ」→「ファインダ」→「名前付き問合せ」タブに移動します。
ファインダを追加し、Beanのホームで宣言したファインダと同じ名前を付けます。その後、必要なパラメータを追加します。
ファインダを選択し、構成します。
パラメータを1つ必要とする単純なEJB QL問合せの例を次に示します。この例では、?1の疑問符(?)がパラメータを示しています。
SELECT OBJECT(employee) FROM Employee employee WHERE (employee.name =?1)
カスタムSQLコードを使用してファインダ・ロジックを指定できます。SQLを使用すると、TopLinkの式やEJB QLでは表現できないロジックを実装できます。
SQLファインダの作成手順:
ejb-jar.xmlファイルでファインダを宣言し、ejb-qlタグを空のまま残します。
TopLink Workbenchを起動します。
ejb-jar.xmlファイルの場所を指定し、「ファイル」→「プロジェクトのejb-jar.xmlからの更新」を選択します。
Beanの「問合せ」→「名前付き問合せ」タブを選択します。
ファインダを選択し、「SQL」ラジオ・ボタンを選択して、SQL文字列を入力します。
ファインダを構成します。
パラメータを1つ必要とする単純なSQLファインダの例を次に示します。この例では、シャープ記号(#)がSQL文字列内の引数projectNameのバインドに使用されています。
SELECT * FROM EJB_PROJECT WHERE (PROJ_NAME = #projectName)
リダイレクト・ファインダを使用すると、実装が任意のヘルパー・クラスでstaticメソッドとして定義されるファインダを指定できます。ファインダを起動したときには、コールは指定したstaticメソッドにリダイレクトされます。
リダイレクト問合せの詳細は、108.10項「リダイレクト問合せ」を参照してください。
ファインダは任意のパラメータをとることができます。ファインダにパラメータが含まれている場合、パラメータはVectorにパッケージ化され、リダイレクト・メソッドに渡されます。
リダイレクト・ファインダには、次のような利点があります。リダイレクト・ファインダの実装は、リダイレクト・ファインダを起動するBeanとは別に定義するため、任意のタイプおよび数のパラメータを受け入れるリダイレクト・ファインダを作成できます。これにより、入力パラメータに応じていくつかの異なるパラメータと戻りタイプを受け入れる汎用リダイレクト・ファインダの作成が可能になります。
リダイレクト・ファインダを使用するための一般的な方法は、次のような汎用ファインダを作成することです。
いくつかのタスクを実行するロジックを含むファインダであること
最初に渡されたパラメータを読み取り、要求されたファインダのタイプを特定して、適切なロジックを選択するファインダであること
リダイレクト・メソッドは、パラメータから関連データを抽出するために必要なロジックを含んでおり、このロジックを使用してTopLinkの問合せを構成します。
リダイレクト・ファインダの主な短所としては、複雑で、構成が難しい点があります。問合せを定義するための余分なヘルパー・メソッドも必要になります。しかし、リダイレクト問合せは複雑なロジックをサポートしているため、実装する必要のあるロジックが、リダイレクト・メソッドがコールされるBeanに関連していない場合には、しばしば最適な方法となります。
ejb-jar.xmlファイルでファインダを宣言し、ejb-qlタグを空のまま残します。
必要に応じて、HomeインタフェースまたはlocalHomeインタフェース、あるいはその両方でファインダを宣言します。
修正メソッドを作成します。
詳細は、119.35項「修正メソッドの構成」を参照してください。
TopLink Workbenchを起動します。
Beanのメニューから「アドバンスト・プロパティ」→「ロード後」を選択します。
staticメソッドのクラスと名前を指定し、修正メソッドをディスクリプタに対して有効にします。
修正メソッドは、次のようにディスクリプタの問合せマネージャに問合せを追加します。
ReadAllQuery query = new ReadAllQuery();
query.setRedirector(new MethodBaseQueryRedirector (examples.ejb.cmp20.advanced.
FinderDefinitionHelper.class,"findAllEmployeesByStreetName"));
descriptor.getQueryManager().addQuery ("findAllEmployeesByStreetName", query);
リダイレクト・メソッドは、単一のエンティティBean(オブジェクト)またはVectorを返す必要があります。可能なメソッド・シグネチャは次のとおりです。
public static Object redirectedQuery(oracle.toplink.sessions.Sessions, Vector args)
および
public static Vector redirectedQuery(oracle.toplink.sessions.Sessions, Vector args)
問合せメソッドを実装する際には、メソッドが必ず正しいタイプを返すようにする必要があります。複数のBeanを返すメソッドの場合、戻りタイプをjava.util.Vectorに設定します。この結果は、必要に応じてjava.util.Enumeration(またはコレクション)に変換されます。
実行時に、クライアントはエンティティBeanのホームからファインダを起動し、ファインダ・メソッドのシグネチャに指定されている順に、引数をVectorのargsにパッケージ化します。クライアントはVectorをリダイレクト・ファインダに渡します。リダイレクト・ファインダは、そのVectorを使用してTopLinkの式を実行します。
例111-13 単純なリダイレクト問合せの実装
public class RedirectorTest {
private Session session;
private Project project;
public static void main(String args[]) {
RedirectorTest test = new RedirectorTest();
test.login();
try {
// Create the arguments to be used in the query
Vector arguments = new Vector(1);
arguments.add("Smith");
// Run the query
Object o = test.getSession()
.executeQuery(test.redirectorExample(), arguments);
o.toString();
}
catch (Exception e) {
System.out.println("Exception caught -> " + e);
e.printStackTrace();
}
}
public ReadAllQuery redirectorExample() {
// Create a redirector
MethodBasedQueryRedirector redirector = new MethodBasedQueryRedirector();
// Set the class containing the public static method
redirector.setMethodClass(RedirectorTest.class);
// Set the name of the method to be run
redirector.setMethodName("findEmployeeByLastName");
// Create a query and add the redirector previously created
ReadAllQuery readAllQuery = new ReadAllQuery(Employee.class);
readAllQuery.setRedirector(redirector);
readAllQuery.addArgument("lastName");
return readAllQuery;
}
// Call the static method
public static Object findEmployeeByLastName(
oracle.toplink.sessions.Session session,
Vector arguments) {
// Create a query and set Employee as its ref. class
ReadAllQuery raq = new ReadAllQuery(Employee.class);
raq.addArgument("lastName");
// Create the selection criteria
ExpressionBuilder employee = query.getExpressionBuilder();
Expression whereClause =
employee.get("lastName").equal(arguments.firstElement());
// Set the selection criteria
raq.setSelectionCriteria(whereClause);
return (Vector)session.executeQuery(raq, arguments);
}
...
}
ejbSelectメソッドとは、エンティティBeanインスタンスで内部的に使用することを目的とした問合せメソッドです。抽象Bean自体に指定されるejbSelectメソッドは、ホームまたはコンポーネント・インタフェースでクライアントに直接公開されることはありません。各Beanは抽象として定義され、このようなメソッドを0個以上含むことができます。
選択メソッドには次の特性があります。
メソッド名に接頭辞としてejbSelectを付ける必要があります。
publicとして宣言する必要があります。
abstractとして宣言する必要があります。
throws句にはjavax.ejb.FinderExceptionを指定する必要がありますが、アプリケーション固有の例外も指定できます。
ejb-jar.xmlファイルのresult-type-mappingタグによってejbSelectメソッドの戻りタイプが決まります。このフラグをRemoteに設定するとEJBObjectsが返され、Localに設定するとEJBLocalObjectsが返されます。
ejbSelectメソッド定義の書式は次のようになります。
public abstract type ejbSelect<METHOD>(...);
ejbSelect問合せの戻りタイプは、ejbSelectが起動されるエンティティBeanのタイプに限定されません。コンテナ管理のリレーションシップまたはコンテナ管理のフィールドに対応する任意のタイプを返すことができます。
ejbSelectメソッドは、それが起動されるエンティティBeanインスタンスのアイデンティティに基づいていませんが、エンティティBeanの主キーを引数として使用できます。その場合、有効範囲を特定のエンティティBeanインスタンスに論理的に設定した問合せが作成されます。
次のようにejb-jar.xmlファイルを更新します。
ejbSelectメソッドを宣言します。
ejb-qlタグにEJB QL文字列を入力します。
必要に応じて、result-type-mappingタグの戻りタイプを指定します。
抽象BeanクラスでejbSelectを宣言します。
TopLink Workbenchを起動します。
ナビゲータでプロジェクト・アイコンをクリックし、「選択」→「プロジェクトのejb-jar.xmlからの更新」を選択してファインダを読み取ります。
Beanの「問合せ」→「名前付き問合せ」タブを選択します。
ejbSelectメソッドを選択し、構成します。
カーソルとストリームは、大きな結果セットを効率的に扱うことのできる関連メカニズムです。詳細は、108.5.3項「ストリームとカーソルの問合せの結果」を参照してください。
表111-1は、問合せの結果をカーソルまたはストリームとして返すことのできるDataReadQueryおよびReadAllQueryのすべてのサブクラスに対して、TopLinkが提供しているメソッドを示します。
表111-1 ストリームとカーソルの問合せの結果のオプション
| メソッド | 問合せの戻り値 | 説明 |
|---|---|---|
|
|
|
データベース結果セットのカーソルにアクセスし、結果セットを前後に移動できます。 |
|
|
|
基礎となるデータベース結果セットのカーソルで結果を使用できるようになるため、一度に1つずつ順に結果にアクセスできます。 |
ScrollableCursorまたはCursoredStreamを使用して、TopLinkの機能とデータベースのデータ・カーソル機能を組み合せて、結果セットを小さな扱いやすい単位に分割できます。
ScrollableCursorまたはCursoredStreamを使用する問合せの動作は、クライアントがリクエストした要素をクライアントに送信するその他の問合せとは異なります。
この項の内容は次のとおりです。
TopLinkのスクロール可能カーソルを使用すると、結果セット全体を読み取らずに、1回のデータベース読取り操作でデータベースから結果セットをスクロールできます。ScrollableCursorクラスは、JavaのListIteratorインタフェースを実装し、ストリームでのダイレクト・アクセスと相対アクセスを可能にします。スクロール可能カーソルによって、ストリームを前後にスクロールすることもできます。
次のメソッドにより、スクロール可能カーソルを使用してデータをナビゲートできます。
カーソルを使用してデータの走査を行うには、いくつかの方法があります。たとえば、データセットの最後から始めて、最初のレコードに向かって進むには、次のようにします。
afterLastメソッドをコールして、結果セットの最終行の後にカーソルを置きます。
hasPreviousメソッドを使用して、現在のレコードの上にレコードがあるかどうかを判断します。このメソッドは、データセットの最後のレコードに達したときにfalseを返します。
hasPreviousメソッドがtrueを返した場合、previousメソッドをコールして、現在の行の前にある行にカーソルを移動し、そのオブジェクトを読み取ります。
これらはデータ走査用の一般的なメソッドですが、使用可能なメソッドは他にもあります。使用可能なメソッドの詳細は、『Oracle Fusion Middleware Java API Reference for Oracle TopLink』を参照してください。
ScrollableCursorオブジェクトを使用する場合、JDBCドライバがJDBC 2.0の仕様に準拠している必要があります。
Javaストリームを使用すると、問合せ結果を個別のレコードまたはレコードのグループとして取得できるため、パフォーマンスの向上につながります。問合せで大きな結果セットが生成される可能性が高い場合は特に、ストリームを使用して効率的なTopLink問合せを作成することができます。
カーソル付きストリームでは、問合せの結果セットを扱いやすいサブセット単位でデータベースから読み取ったり、結果セットのストリームをスクロールしたりすることが可能になります。
ReadAllQueryクラスのuseCursoredStreamメソッドがカーソル付きストリームをサポートします。
CursoredStream stream; ReadAllQuery query = new ReadAllQuery(Employee.class); query.useCursoredStream(); stream = (CursoredStream) session.executeQuery(query);
問合せではListではなくCursoredStreamのインスタンスが返されるため、これはより効率的な方法です。たとえば、次の2つのコード例を検討してください。例111-16では、すべての従業員オブジェクトを含むListが返されます。ACMEの従業員数が10,000名の場合、Listには10,000のEmployeeオブジェクトに対する参照が含まれます。
例111-16 Listの使用
ReadAllQuery query = new ReadAllQuery(Employee.class);
Enumeration employeeEnumeration;
List employees = (List) session.executeQuery(query);
employeeEnumeration = employee.elements();
while (employeeEnumeration.hasMoreElements()) {
Employee employee = (Employee) employeeEnumeration.nextElement();
employee.doSomeWork();
}
次の例では、ListではなくCursoredStreamインスタンスが返されます。CursoredStreamコレクションには10,000のオブジェクトがすべて含まれているように思われますが、当初は最初の10個のEmployeeオブジェクトのみに対する参照が含まれています。コレクション内の残りのオブジェクトは必要に応じて取得されます。多くの場合、アプリケーションがすべてのオブジェクトを読み取る必要はありません。
ReadAllQuery query = new ReadAllQuery(Employee.class);
query.useCursoredStream();
CursoredStream stream = (CursoredStream) session.executeQuery(query);
while (! stream.atEnd()) {
Employee employee = (Employee) stream.read();
employee.doSomeWork();
stream.releasePrevious();
}
stream.close();
|
注意: releasePreviousメッセージはオプションです。これにより、それまでに読み込まれたオブジェクトとシステム・メモリーが解放されます。解放されたオブジェクトはカーソル付きストリームの記憶域から削除されますが、アイデンティティ・マップには残る場合があります。 |
To optimize CursoredStreamのパフォーマンスを最適化するには、次のように、しきい値とページ・サイズをuseCursoredStream(Threshold, PageSize)メソッドに指定します。
しきい値は、最初にストリームに読み込まれるオブジェクトの数を示します。デフォルトのしきい値は10です。
ページ・サイズは、最初のオブジェクト・グループの後、ストリームに読み込まれるオブジェクトの数を示します。この処理が行われるのは、しきい値の数のオブジェクトが読み取られた後です。ページ・サイズが大きければ大きいほど全体的なパフォーマンスは向上しますが、各ページがロードされるときにアプリケーションの遅延が発生します。デフォルトのページ・サイズは5です。
バッチタイプの操作を実行する場合は、カーソル付きストリームとともにdontMaintainCacheメソッドを使用します。バッチ操作では、単純な操作が多数のオブジェクトに対して実行された後、オブジェクトが破棄されます。カーソル付きストリームにより、必要なオブジェクトが必要な場合にのみ作成されます。また、dontMaintainCacheを使用すれば、これらの一時オブジェクトはキャッシュされません。
n CMPファインダを使用したカーソルとストリームの使用方法大きな結果セットは、収集して処理する際に、リソースを大量に消費します。クライアントが、返された結果をより確実にコントロールできるようにするには、カーソルを使用するようTopLinkのファインダを構成します。そうすることで、TopLinkのCursoredStreamをデータベースのデータ・カーソル機能と組み合せて、結果セットを小さな扱いやすい単位に分割できます。
|
注意: エンティティBeanにトランザクション属性REQUIREDを使用する場合、カーソルの最初のページを超える読取り操作がトランザクションの中で行われるように、すべての読取り操作をUserTransactionのメソッドbeginおよびcommitの中にラップする必要があります。 |
java.util.Collectionを返す任意のファインダを、カーソルを使用するように構成できます。ファインダに対する問合せを作成するときには、useCursoredStreamオプションを追加してカーソルを有効にします。
例111-17 ファインダでのカーソル付きストリーム
ReadAllQuery raq = new ReadAllQuery(ProjectBean.class);
ExpressionBuilder bldr = raq.getExpressionBuilder();
raq.useCursoredStream();
raq.addArgument("projectName");
raq.setSelectionCriteria(bldr.get("name").
like(bldr.getParameter("projectName")));
descriptor.getQueryManager().addQuery ("findByNameCursored", query);
TopLinkは、ファインダ結果を走査するための次の追加要素を提供します。
isEmptyメソッド: java.util.Collectionと同様に、isEmptyメソッドはCollectionが空かどうかを示すブール値を返します。
sizeメソッド: java.util.Collectionと同様に、sizeメソッドはCollection内の要素数を示す整数を返します。
iteratorメソッド: java.util.Collectionと同様に、iteratorメソッドはCollection内の要素を列挙するためのjava.util.Iteratorを返します。
TopLinkは、(java.util.Iteratorに基づく)oracle.toplink.ejb.cmp.wls.CursoredIterator用の拡張プロトコルも提供します。
closeメソッド: サーバーでカーソルを閉じます。クライアントは、このメソッドをコールして、データベース接続をクローズする必要があります。
hasNextメソッド: 結果セット内にまだ要素があるかどうかを示すブール値を返します。
nextメソッド: 使用可能な次の要素を返します。
next(int count)メソッド: 結果セットに要素がいくつ残っているかに応じて、最大のカウントを持つ要素のVectorを、使用可能な結果から取得します。
例111-18は、カーソル・ファインダを実行するクライアント・コードを示します。
例111-18 カーソル・ファインダ
// import both CursoredCollection and CursoredIterator import oracle.toplink.ejb.cmp.wls.*; //... other imports as necessary getTransaction().begin(); CursoredIterator cursoredIterator = (CursoredIterator) getProjectHome().findByNameCursored("proj%").iterator(); Vector projects = new Vector(); for (int index = 0; index < 50; i++) { Project project = (Project)cursoredIterator.next(); projects.addElement(project); } // Rest all at once ... Vector projects2 = cursoredIterator.next(50); cursoredIterator.close(); getTransaction().commit();
問合せを構成して、結果セットを複数のページとして取得できます(つまり、結果を分割して、pageSize以下の結果からなるListとして取得できます)。例111-19は、ReadQueryメソッドsetMaxRowsおよびsetFirstResultを使用すると問合せの結果セット全体をページ区切りできることを示します。
詳細は、次を参照してください。
例111-19 setMaxRowおよびsetFirstResultを使用した結果セット全体のページ区切り
...
int pageSize = 100;
int firstResult = 0;
int maxRows = pageSize;
boolean hasNext = true;
List page = null;
while (hasNext) {
query.setFirstResult(firstResult);
query.setMaxRows(maxRows);
page = (List)sesssion.executeQuery(query);
// process this page of results
if (page.size() == 0) {
hasNext = false;
} else {
firstResult = firstResult + pageSize;
maxRows = maxRows + pageSize;
}
}
...
この項では、次のようなTopLink問合せでのキャッシュ・オプションの使用方法を説明します。
デフォルトでは、ReadQueryを実行するたびに、現在の問合せ構成が読取り操作に適用されます。この実行中、TopLinkではセッション・キャッシュ、データ・ソースまたはその両方にアクセスします。
問合せの中には、同じ結果セットを返すことがわかっているものがあります(たとえば、現在の営業担当者が昨年販売した装置の数など)。最初の問合せを実行した後、問合せが再起動される場合は、実際に実行する必要はありません。
このようなタイプの問合せの場合、TopLinkのいずれかのReadQueryを使用し、その問合せ結果が内部問合せキャッシュに保存されるように構成できます。
一連の問合せパラメータについて問合せを1回実行すると、同じ問合せパラメータを起動するたびにキャッシュの結果セットが返されます。これにより、頻繁に実行される問合せのパフォーマンスが向上します。デフォルトでは、問合せを実行すると、特定のパラメータに対して最後の100個の問合せの結果セットがキャッシュされます。この問合せキャッシュをQueryResultsCachePolicyの一部として構成できます。
この機能は、ReadQueryのメソッドcacheQueryResultsを使用するか、QueryResultsCachePolicyのインスタンスを使用するReadQueryのメソッドsetQueryResultsCachePolicyをコールすることで有効にでき、ReadQueryのメソッドdoNotCacheQueryResultsを使用すると無効にできます。
この機能を使用する前に、108.16.7.1項「内部問合せキャッシュの制限」に記載されている制限に留意してください。詳細は、108.16.7項「問合せキャッシュへの問合せ結果のキャッシュ方法」を参照してください。
キャッシュの無効化ポリシーを問合せの内部キャッシュに適用できます(111.13.2項「問合せレベルにおけるキャッシュの有効期限の構成方法」を参照)。詳細は、102.2.5項「キャッシュの無効化」を参照してください。
例111-20は、結果をキャッシュするReadQueryの構成方法を示します。
例111-20 問合せ結果をキャッシュするReadQueryの構成
ReadObjectQuery query = new ReadObjectQuery(Employee.class); // Instruct the ReadQuery to cache its query results query.cacheQueryResults(); // The first time you invoke it, the ReadQuery reads from the database, session // cache, or both and stores the result set in its internal query cache Employee employeeFirst = (Employee) session.executeQuery(query);
例111-21は、結果のキャッシュを中止するReadQueryの構成方法を示します。次回問合せを実行する際、TopLinkでは問合せキャッシュは使用されません。かわりにデータ・ソースにアクセスします。
例111-21 問合せ結果のキャッシュを中止するReadQueryの構成
// Disable query caching query.doNotCacheQueryResults(); // The ReadQuery does not use the query cahce and instead accesses the database Employee employee = (Employee) session.executeQuery(query);
または、セッションに渡されるReadQueryのメソッドclearQueryResultsを使用して、問合せの内部キャッシュを消去できます。これにより、現在キャッシュされている結果を消去し、次回問合せを実行する際には、データベースから読み取ります。
CacheInvalidationPolicyを持つReadQueryを構成できます。
独自の内部キャッシュに結果をキャッシュするよう問合せを構成する場合(111.13.1項「ReadQueryでの結果のキャッシュ方法」を参照)、キャッシュの無効化ポリシーを使用すると、時間による失効または日次の失効に基づいて、キャッシュされた問合せの結果セットに期限を指定できます。この無効化の時間は、問合せを実行した時間から計算されます。この問合せにより、特定の問合せパラメータのセットに対する問合せ結果セットがキャッシュされます。
例111-22は、問合せによって返されるすべてのオブジェクトと、問合せの内部キャッシュにキャッシュされるすべてのオブジェクトにTimeToLiveCacheInvalidationPolicyが適用される、ReadQueryの構成方法を示します。
例111-22 問合せの内部キャッシュに対するReadQueryでのCacheInvalidationPolicyの構成
// The TimeToLiveCacheInvalidationPolicy applies to all objects returned by the query and // cached in the query's internal cache readQuery.setQueryResultsCachePolicy( new QueryResultsCachePolicy(new TimeToLiveCacheInvalidationPolicy(1000)) );
詳細は、102.2.5項「キャッシュの無効化」を参照してください。