この章では、開発サイクル全般で最も一般的に使用される基本的なTopLink問合せAPIのコールについて説明します。
この章の内容は次のとおりです。
詳細は、第111章「詳細問合せAPIの使用」を参照してください。
この項では例をあげ、次のセッション問合せメソッドの使用方法を説明します。
注意: すべてのデータ・ソース操作は作業ユニットを使用して実行することをお薦めします。これがトランザクション、同時実行性および参照制約を管理する最も効率のよい方法です。詳細は、第113章「TopLinkトランザクションの概要」を参照してください。 |
詳細は、108.6項「セッション問合せ」を参照してください。
セッション問合せAPIを使用すると、次の読取り操作を実行できます。
readObject
メソッドは、データベースからシングル・オブジェクトを取得します。アプリケーションでは、読取り対象のオブジェクトのクラスを指定する必要があります。基準に一致するオブジェクトがない場合は、null値が返されます。
たとえば、基本的な読取り操作は次のようになります。
session.readObject(MyDomainObject.class);
この例では、MyDomainObject
に使用される表で見つかったMyDomainObject
の最初のインスタンスを返します。TopLinkは、特定のオブジェクト用の問合せパラメータを指定するためのExpression
クラスを提供します。
主キーを使用して特定のシングル・オブジェクトを検索する場合、readAllObjects
メソッドよりもreadObject
メソッドの方が効率的です。これは、readObject
がデータベースにアクセスせずにキャッシュ内でインスタンスを見つけられるためです。readAllObjects
メソッドでは基準に一致するオブジェクトの数がわからないため、一致するオブジェクトがキャッシュ内で見つかったとしても、一致するオブジェクトを見つけるために常にデータベースを検索します。
import oracle.toplink.sessions.*;
import oracle.toplink.expressions.*;
// Use an expression to read in the employee whose last name is Smith. Create an
// expression using the Expression Builder and use it as the selection criterion // of the search
Employee employee = (Employee) session.readObject(Employee.class,
new ExpressionBuilder().get("lastName").equal("Smith"));
readAllObjects
メソッドは、データベースからオブジェクトのList
を取得します。返されるオブジェクトの順序は指定しません。問合せで一致するオブジェクトが見つからない場合は、空のList
が返されます。
問合せに対するクラスを指定します。例109-2に示すように、式を含めてさらに複雑な検索基準を定義することもできます。
セッション問合せAPIを使用すると、次の作成、更新および削除操作を実行できます。
writeObject
メソッドを起動すると、オブジェクトが存在するかどうかを特定するための存在チェックが実行されます。オブジェクトが存在する場合、writeObject
はオブジェクトを更新し、存在しない場合は新規オブジェクトを挿入します。
writeObject
メソッドは、参照整合性が維持されるように、正しい順序で私有されたオブジェクトを書き込みます。
オブジェクトがデータベースに存在するかどうかを確認できない場合は、writeObject
メソッドをコールします。
writeAllObjects
メソッドをコールすると、データベースに複数のオブジェクトを書き込むことができます。writeAllObjects
メソッドは、writeObject
メソッドの場合と同じ存在チェックを実行した後、適切な挿入操作または更新操作を実行します。
例109-4 writeAllObjectsを使用した複数のオブジェクトの書込み
// Read a List of all the current employees in the database. List employees = session.readAllObjects(Employee.class); // Modify any employee data as necessary ... // Create a new employee and add it to the list of employees Employee susan = new Employee(); ... // Initialize the new instance of employee employees.add(susan); // Write all employees to the database. The new instance of susan not currently in // the database will be inserted. All the other employees currently stored in the // database will be updated session.writeAllObjects(employees);
insertObject
メソッドはデータベースで新規オブジェクトを作成しますが、挿入操作を試みる前に存在チェックを実行しません。オブジェクトがまだデータベースに存在しないことが確実な場合は、insertObject
メソッドは、writeObject
メソッドより効率的です。オブジェクトが存在する場合にinsertObject
メソッドを実行すると、データベースで例外がスローされます。
updateObject
メソッドはデータベース内の既存のオブジェクトを更新しますが、更新操作を試みる前に存在チェックを実行しません。オブジェクトがデータベースに存在することが確実な場合は、writeObject
メソッドよりもupdateObject
の方が効率的です。オブジェクトが存在しない場合にupdateObject
メソッドを実行すると、データベースで例外がスローされます。
この項では、DatabaseQuery
問合せの作成および実行により次のような様々な基本永続化操作を実行する方法について説明します。
この項では例をあげ、次のようなDatabaseQuery
を使用したオブジェクトの読取り方法について説明します。
例109-5は、単純な読取り問合せを示します。この例では、TopLinkの式を使用しますが、問合せ用の独自の引数は使用しません。かわりに、式で提供される検索パラメータを使用します。また、コード内に式を作成しますが、問合せをセッションに登録しません。
// This example returns a List of employees whose employee ID is > 100 // Initialize the DatabaseQuery by specifying the query type // and set the reference class for the query ReadAllQuery query = new ReadAllQuery(Employee.class); // Retrieve ExpressionBuilder from the query ExpressionBuilder builder = query.getExpressionBuilder(); // Configure the query execution. Because this example uses an expression, // it uses the setSelectionCriteria method query.setSelectionCriteria(builder.get("id").greaterThan(100)); // Execute the query List employees = (List) session.executeQuery(query);
例109-6は、使用可能な構成オプションをすべて使用した、複雑なreadObject
問合せを示します。
// Initialize the DatabaseQuery by specifying the query type // and set the reference class for the query ReadObjectQuery query = new ReadObjectQuery(Employee.class); // Retrieve ExpressionBuilder from the query ExpressionBuilder builder = query.getExpressionBuilder(); // Define two expressions that map to the first and last names of the employee Expression firstNameExpression = emp.get("firstName").equal(emp.getParameter("firstName")); Expression lastNameExpression = emp.get("lastName").equal(emp.getParameter("lastName")); // Configure the query execution. Because this example uses an expression, // it uses the setSelectionCriteria method query.setSelectionCriteria(firstNameExpression.and(lastNameExpression)); // Specify the required arguments for the query query.addArgument("firstName"); query.addArgument("lastName"); // Add the query to the session session.addQuery("getEmployeeWithName", query); // Execute the query by referencing its name and providing values // for the specified arguments Employee employee = (Employee)session.executeQuery("getEmployeeWithName","Bob","Smith");
例109-7は、部分オブジェクト読取りの使用方法を示します。部分オブジェクト読取りでは、従業員の姓と主キーのみが読み取られます。その結果、データベースから読み取られるデータの量が減少します。
例109-7 部分オブジェクト読取りの使用
/* Read all the employees from the database, ask the user to choose one and return it. This uses partial object reading to read just the last name of the employees. Since TopLink automatically includes the primary key of the object, the full object can easily be read for editing */ List list; // Fetch data from database and add to list box ReadAllQuery query = new ReadAllQuery(Employee.class); query.addPartialAttribute("lastName"); // The next line avoids a query exception query.dontMaintainCache(); List employees = (List) session.executeQuery(query); list.addAll(employees); // Display list box .... // Get selected employee from list Employee selectedEmployee = (Employee)session.readObject(list.getSelectedItem()); return selectedEmployee;
例109-8は、カナダ人従業員の都市別の合計給与と平均給与を示します。
ExpressionBuilder emp = new ExpressionBuilder(); ReportQuery query = new ReportQuery(Employee.class, emp); query.addMaximum("max-salary", emp.get("salary")); query.addAverage("average-salary", emp.get("salary")); query.addAttribute("city", emp.get("address").get("city")); query.setSelectionCriteria(emp.get("address").get("country").equal("Canada")); query.addOrdering(emp.get("address").get("city")); query.addGrouping(emp.get("address").get("city")); List reports = (List) session.executeQuery(query);
Report
Q
uery
クラスには、属性の平均、最大、最小、合計、標準偏差、分散、総数を計算するメソッドなど、広範なレポートAPIがあります。Report
Q
uery
で使用可能なメソッドの詳細は、『Oracle Fusion Middleware Java API Reference for Oracle TopLink』を参照してください。
注意: ReportQuery はReadAllQuery から継承されているため、ほとんどのReadAllQuery プロパティもサポートします。 |
例による問合せを使用すると、問合せで使用する属性のみを移入したサンプル・オブジェクト・インスタンスの形式で、問合せ選択基準を指定できます。
例による問合せを定義するには、ReadObjectQuery
またはReadAllQuery
を、永続オブジェクトのサンプル・インスタンスおよび例による問合せポリシー(オプション)とともに指定します。サンプル・インスタンスには、問い合せるデータと、オプションでQueryByExamplePolicy
(「QueryByExamplePolicyの定義」を参照)を含めます。QueryByExamplePolicy
には、使用する演算子や無視する属性値など、構成設定を指定します。また、例による問合せと式を組み合せることもできます(「例による問合せと式の結合」を参照)。
詳細は、108.2.8.6項「例による問合せ」を参照してください。
例109-9 例による問合せを使用した従業員の問合せ
例109-9では、従業員Bob Smithを問い合せています。
Employee employee = new Employee();
employee.setFirstName("Bob");
employee.setLastName("Smith");
// Create a query and set Employee as its reference class
ReadObjectQuery query = new ReadObjectQuery(Employee.class);
query.setExampleObject(employee);
Employee result = (Employee) session.executeQuery(query);
例109-10 例による問合せを使用した従業員の住所の問合せ
例109-10では、従業員の住所に対して問合せを行っています。
Employee employee = new Employee();
Address address = new Address();
address.setCity("Ottawa");
employee.setAddress(address);
// Create a query and set Employee as its reference class
ReadAllQuery query = new ReadAllQuery(Employee.class);
query.setExampleObject(employee);
List results = (List) session.executeQuery(query);
QueryByExamplePolicyの定義
TopLinkの例による問合せサポートには、例による問合せポリシーも含まれています。このポリシーを編集すると、例による問合せのデフォルト動作を変更できます。次のような場合のために、ポリシーを変更できます。
LIKE
などの演算子を使用して、属性を比較する場合。デフォルトでは、例による問合せで使用できるのはEQUALS
のみです。
例による問合せで無視される値のセット(IGNORE
セット)を変更する場合。デフォルトで無視される値は、ゼロ(0)、空の文字列およびFALSE
です。
属性の値がIGNORE
セットに含まれるものであっても、例による問合せでその値を強制的に考慮する場合。
属性値としてisNull
またはnotNull
を使用する場合。
例による問合せポリシーを指定するには、問合せにQueryByExamplePolicy
のインスタンスを含めます。
例109-11 like演算子を使用した、例による問合せポリシー
例109-11では、文字列に対してlike
演算子を使用し、給与がゼロを超えるオブジェクトのみを含めています。
Employee employee = new Employee(); employee.setFirstName("B%"); employee.setLastName("S%"); employee.setSalary(0); // Create a query and set Employee as its reference class ReadAllQuery query = new ReadAllQuery(Employee.class); query.setExampleObject(employee); // Query by example policy section adds like and greaterThan QueryByExamplePolicy policy = new QueryByExamplePolicy(); policy.addSpecialOperation(String.class, "like"); policy.addSpecialOperation(Integer.class, "greaterThan"); policy.alwaysIncludeAttribute(Employee.class, "salary"); query.setQueryByExamplePolicy(policy); List results = (List) session.executeQuery(query);
例109-12 キーワードを使用した、例による問合せポリシー
例109-12では、文字列に対してキーワードを使用し、値-1を無視します。
Employee employee = new Employee(); employee.setFirstName("bob joe fred"); employee.setLastName("smith mc mac"); employee.setSalary(-1); // Create a query and set Employee as its reference class ReadAllQuery query = new ReadAllQuery(Employee.class); query.setExampleObject(employee); // Query by example policy section QueryByExamplePolicy policy = new QueryByExamplePolicy(); policy.addSpecialOperation(String.class, "containsAnyKeyWords"); policy.excludeValue(-1); query.setQueryByExamplePolicy(policy); List results = (List) session.executeQuery(query);
例による問合せと式の結合
例109-13に示すように、さらに複雑な、例による問合せを作成するには、例による問合せをTopLinkの式と結合します。
例109-13 例による問合せと式の結合
Employee employee = new Employee(); employee.setFirstName("Bob"); employee.setLastName("Smith"); // Create a query and set Employee as its reference class ReadAllQuery query = new ReadAllQuery(Employee.class); query.setExampleObject(employee); // Specify expression ExpressionBuilder builder = query.getExpressionBuilder(); query.setSelectionCriteria(builder.get("salary").between(100000,200000); List results = (List) session.executeQuery(query);
順序付けは、DatabaseQuery
の一般的なオプションです。addOrdering
、addAscendingOrdering
、addDescendingOrdering
のいずれかのメソッドを使用して、ReadAllQuery
から返される一連のオブジェクトを順序付けできます。属性名または問合せキーと式に基づいた順序を適用できます。
例109-14 単純な順序付けを行う問合せ
// Retrieves objects ordered by last name then first name in ascending order
ReadAllQuery query = new ReadAllQuery(Employee.class);
query.addAscendingOrdering ("lastName");
query.addAscendingOrdering ("firstName");
List employees = (List) session.executeQuery(query);
例109-15 複雑な順序付けを行う問合せ
// Retrieves objects ordered by street address, descending
// case-insensitive order of cities, and manager's last name
ReadAllQuery query = new ReadAllQuery(Employee.class);
ExpressionBuilder emp = query.getExpressionBuilder();
query.addOrdering (emp.getAllowingNull("address").get("street"));
query.addOrdering(
emp.getAllowingNull("address").get("city").toUpperCase().descending());
query.addOrdering(emp.getAllowingNull("manager").get("lastName"));
List employees = (List) session.executeQuery(query);
getAllowingNull
を使用し、住所とマネージャのリレーションシップの外部結合を作成していることに注意してください。これにより、住所またはマネージャを持たない従業員もリストに含まれます。
読取り順序の構成の詳細は、119.7.1.4項「すべて読取り問合せの順序の構成」を参照してください。
デフォルトでは、ReadAllQuery
は結果オブジェクトをListで返します。例109-16に示すように、この問合せを構成して、結果を、Collection
またはMap
インタフェースを実装した任意のコレクション・クラスに入れて返すようにできます。
指定した最大行数に問合せを制限できます。この機能を使用して、非常に多くのオブジェクトが返される可能性のある問合せを回避します。
例109-18に示すように、最大行数を指定するには、setMaxRows
メソッドを使用し、問合せの最大行数を表す整数を渡します。
例109-18 返されるオブジェクトの最大サイズの設定
ReadAllQuery query = new ReadAllQuery(Employee.class); query.setMaxRows(5); List employees = (List) session.executeQuery(query);
setMaxRows
メソッドでは問合せが返す行数が制限されますが、最初の結果セットの後にその行数を超えるレコードを取得することはできません。
固定の増分で結果セットを参照するには、カーソルまたはカーソル付きストリームを使用します。詳細は、111.11項「カーソルとストリームの問合せ結果の処理」を参照してください。
TopLinkで問合せ結果を待つ最大時間を設定できます。これを設定した場合、指定時間が過ぎると、ハングしている問合せや長時間かかっている問合せが強制的に中断されます。タイムアウト時間が経過すると、DatabaseException
がスローされます。
例109-19に示すように、問合せごとにタイムアウト時間を指定するには、DatabaseQuery
のメソッドsetQueryTimeout
を使用し、タイムアウト時間を、タイムアウト時間が経過するまでの秒数を表す整数として渡します。
// Create the appropriate query and set timeout limits ReadAllQuery query = new ReadAllQuery(Employee.class); query.setQueryTimeout(2); try { List employees = (List) session.executeQuery(query); } catch (DatabaseException ex) { // timeout occurs }
特定のオブジェクト・タイプに対するすべての問合せに関してタイムアウト時間を指定するには、ディスクリプタ・レベルで問合せタイムアウト時間を構成します(119.8項「ディスクリプタ・レベルでの問合せタイムアウトの構成」を参照)。
バッチ読取りでは、オブジェクトのリレーションシップ属性のマッピングを通じて、問合せの選択基準が伝播されます。バッチ読取り操作を複雑なオブジェクト・グラフにネストさせることもできます。そうすることで、必要なSQLのSELECT文の数が大幅に減少し、データベース・アクセスが効率化されます。
バッチ読取りを実装する際は、次のガイドラインに留意してください。
バッチ読取りは、オブジェクトとそのすべての関連オブジェクトを読み取るプロセスに使用します。
双方向リレーションシップの両側でバッチ読取りを有効にしないでください。
バッチ読取り操作のネストは、データベースで複数の結合が発生して問合せの実行速度が低下する原因となるため、避けてください。
詳細は、12.12.9.2項「読取り例2: オブジェクトのバッチ読取り」を参照してください。
たとえば、n名の従業員とその関連プロジェクトを読み取る場合、選択操作をn + 1回行う必要があります。従業員はすべて一度に読み取られますが、各従業員のプロジェクトはそれぞれ個別に読み取られます。バッチ読取りを使用すれば、関連プロジェクトも元の選択基準を使用してすべて一度の選択操作で読み取ることができるため、選択操作の回数は合計2回のみとなります。
バッチ読取りを実装してバッチ読取り属性を問合せに追加するには、次の例に示すように、query.addBatchReadAttribute(Expression anExpression)
APIを使用します。
… ReadAllQuery raq = new ReadAllQuery(Trade.class); ExpressionBuilder tradeBuilder = raq.getBuilder(); … Expression batchReadProduct = tradeBuilder.get("product"); readAllQuery.addBatchReadAttribute(batchReadProduct); Expression batchReadPricingDetails = batchReadProduct.get("pricingDetails"); readAllQuery.addBatchReadAttribute(batchReadPricingDetails); …
ディスクリプタのマッピング・レベルでバッチ読取りを追加することもできます。詳細は、28.5項「バッチ読取りの構成」を参照してください。
バッチ読取りとインダイレクション(遅延ロード)を組み合せると、オブジェクトの属性の読取りを制御できます。たとえば、1対1のバックポインタ・リレーションシップ属性がある場合、問合せの最後に親の所有側オブジェクトがすべてインスタンス化されるまで、バックポインタのインスタンス化を遅らせることができます。これにより、不要なデータベース・アクセスが防止され、TopLinkキャッシュの使用状況が最適化されます。
結合読取りをObjectLevelReadQuery
とともに使用すると、クラスの問合せを構成して、クラスのインスタンスおよび関連オブジェクトのインスタンスを作成するデータが返されるようにできます。詳細は、108.7.1.5項「結合読取りとオブジェクト・レベルの読取り問合せ」を参照してください。
結合読取りをObjectLevelReadQuery
とともに使用するには、Oracle JDeveloper、TopLink Workbench(119.7.1.5項「名前付き問合せの最適化の構成」を参照)、Javaのいずれかを使用できます。
注意: 1対多のマップ属性についての結合式を使用してObjectLevelReadQuery を作成する場合は、TopLink Workbenchは使用できません。この場合はJavaを使用する必要があります。 |
ObjectLevelReadQuery
APIを使用して、マッピングに対する結合属性を追加できます。
次のいずれかのAPIを使用できます。
ObjectLevelReadQuery
のメソッドaddJoinedAttribute
は、1対1または1対多のマップ属性の結合式または属性名で使用します。
このメソッドを使用して、ネストされた結合を含む、複数の結合属性を追加できます。ソースおよびターゲットは、同じクラス・タイプにすることができます。
1対1のマップ属性についてこのメソッドを使用すると、データベースに1回アクセスすることにより、ObjectLevelReadQuery
のクラスおよびそのクラスの1対1にマップされた属性のターゲットを取得できます。
1対多のマップ属性についてこのメソッドを使用すると、データベースに1回アクセスすることにより、ObjectLevelReadQuery
のクラスおよびそのクラスの1対多にマップされた属性のターゲット・コレクションを取得できます。
ObjectLevelReadQuery
のメソッドsetShouldFilterDuplicates
は、1対多のマップ属性の結合式で使用し、重複する行をフィルタ処理します。このメソッドのデフォルト値はtrue
です。
結合式を使用して、ネストされたバッチ読取りおよび内部または外部結合を構成します(110.2.7項「結合リレーションシップと複雑なリレーションシップを表す式」を参照)。マッピングのメソッドuseInnerJoinFetch
またはuseOuterJoinFetch
を使用して、内部結合または外部結合を指定することもできます。
例109-20は、TopLink ThreeTierEmployee
サンプル・オブジェクトをベースにしています。この例では、ReadAllQuery
が複数属性の結合読取りを行うように構成されています。
例109-20 複数属性の結合読取り
ReadAllQuery query = new ReadAllQuery(Employee.class); Expression managedEmployees = query.getExpressionBuilder().anyOfAllowingNone("managedEmployees"); query.addJoinedAttribute(managedEmployees); query.addJoinedAttribute(managedEmployees.get("address")); query.addJoinedAttribute(managedEmployees.anyOf("phoneNumbers")); List employees = (List) getSession().executeQuery(query);
ObjectLevelReadQuery
のメソッドaddJoinedAttribute(java.lang.String attributeName)
を使用すると、例109-21に示すように、1つの属性の結合読取りを行うように問合せを構成できます。
DatabaseSession
を使用すると、DatabaseQuery
でオブジェクトを作成、更新または削除できます。詳細は、108.6項「セッション問合せ」を参照してください。
この項の内容は次のとおりです。
書込み問合せを実行するには、セッションのwriteObject
メソッドを使用するかわりに、WriteObjectQuery
インスタンスを使用します。同様に、DeleteObjectQuery
、UpdateObjectQuery
、InsertObjectQuery
の各オブジェクトをそれぞれのSession
メソッドのかわりに使用します。
例109-22 WriteObjectQueryの使用
WriteObjectQuery writeQuery = new WriteObjectQuery(); writeQuery.setObject(domainObject); session.executeQuery(writeQuery);
例109-23 InsertObjectQuery、UpdateObjectQueryおよびDeleteObjectQueryの使用
InsertObjectQuery insertQuery= new InsertObjectQuery(); insertQuery.setObject(domainObject); session.executeQuery(insertQuery); // When you use UpdateObjectQuery without a unit of work, // UpdateObjectQuery writes all direct attributes to the database UpdateObjectQuery updateQuery= new UpdateObjectQuery(); updateQuery.setObject(domainObject2); session.executeQuery(updateQuery); DeleteObjectQuery deleteQuery = new DeleteObjectQuery(); deleteQuery.setObject(domainObject2); session.executeQuery(deleteQuery);
書込み問合せを実行すると、デフォルトではオブジェクトとその私有された部分の両方がデータベースに書き込まれます。私有された部分を更新しない書込み問合せを作成するには、問合せの定義にdontCascadeParts
メソッドを含めます。
このメソッドは、次の目的で使用します。
オブジェクトのダイレクト属性のみが変更されたことがわかっている場合に、パフォーマンスを向上させること。
独立した新規オブジェクトの大きなグループを作成する場合に、参照整合性の依存関係を解決すること。
注意: 作業ユニットでは参照整合性が内部的に解決されるため、作業ユニットを使用してデータベースに書き込む場合はこのメソッドは不要です。 |
データベースにオブジェクトを書き込む場合、TopLinkでは、デフォルトでセッション・キャッシュにオブジェクトがコピーされます。問合せにおけるこの動作を無効にするには、問合せでdontMaintainCache
メソッドをコールします。そうすることで、データベースにオブジェクトを挿入する際の問合せのパフォーマンスが向上しますが、この方法は後でアプリケーションで必要にならないオブジェクトに対してのみ使用できます。
例109-25 書込み問合せ中のアイデンティティ・マップ・キャッシュの無効化
例109-25は、フラット・ファイルからすべてのオブジェクトを読み取り、オブジェクトの新規コピーを表に書き込みます。
// Reads objects from an employee file and writes them to the employee table void createEmployeeTable(String filename, Session session) { Iterator iterator; Employee employee; // Read the employee data file List employees = Employee.parseFromFile(filename); Iterator iterator = employees.iterator(); while (iterator.hasNext()) { Employee employee = (Employee) iterator.next(); InsertObjectQuery query = new InsertObjectQuery(); query.setObject(employee); query.dontMaintainCache(); session.executeQuery(query); } }
注意: アイデンティティ・マップを無効にするのは、後続の操作においてオブジェクト・アイデンティティが重要ではない場合にかぎってください。 |
作業ユニットを使用すると、複数のオブジェクトに対して更新および削除操作を実行できます。
この項の内容は次のとおりです。
UpdateAllQuery
を使用すると、一度に多数のオブジェクトを更新できます。この問合せを使用すると、オブジェクトをメモリーに読み取って個別に更新するかわりに、1つのSQL文で多数のオブジェクトを更新できます。例109-26では、すべての常勤従業員を昇給させるUpdateAllQuery
を示しています。
例109-26 UpdateAllQueryの使用
// Give all full time employees a 10% raise
UpdateAllQuery updateQuery = new UpdateAllQuery(Employee.class);
ExpressionBuilder employee = updateQuery.getExpressionBuilder();
updateQuery.setSelectionCriteria(employee.get("status").equal("FULL_TIME"));
updateQuery.addUpdateExpression(employee.get("salary"),
ExpressionMath.multiply(employee.get("salary"), new Float(1.10)));
UpdateAllQuery
では、キャッシュが使用され、キャッシュは常に最新の状態に維持されます。UpdateAllQuery
を構成してキャッシュを無効にする(102.2.5項「キャッシュの無効化」を参照)には、キャッシュの使用状態をINVALIDATE_CACHE
(デフォルト)に設定するか、NO_CACHE
オプションを指定してキャッシュの使用を停止します。これらの設定は、setCacheUsage
メソッドを通じて操作できます。一致する式のキャッシュのみを更新できます。キャッシュの詳細は、第102章「キャッシュの概要」を参照してください。
注意: 属性は集約内にのみ設定できます。集約全体に対しては設定できません。 |
UpdateAllQueryをオプティミスティック・ロック(16.4項「ディスクリプタとロック」を参照)と組み合せ、データベースの行を更新するレベルで使用できます。この場合、キャッシュの更新は行われません。データベースのロック・フィールドが更新されます。また、バージョンおよびタイムスタンプ・ロックのサポートに加え、フィールド・ロックの間接的サポートも提供されます。
例109-27は、すべてのパートタイム従業員の地位を削除するDeleteAllQuery
を示します。
例109-27 DeleteAllQueryの使用
// Delete all part-time employeesDeleteAllQuery deleteQuery = new DeleteAllQuery(Employee.class);
ExpressionBuilder employee = deleteQuery.getExpressionBuilder();
deleteQuery.setSelectionCriteria(employee.get("status").equal("PART_TIME"));
deleteQuery.setObjects(domainObjects);
session.executeQuery(deleteQuery);
詳細は、108.7.3.6項「DeleteAllQuery」を参照してください。
この項の内容は次のとおりです。
例109-28に示すように、DataReadQuery
を使用すると、結果セットを表すRecord
オブジェクトのCollection
を返す選択SQL文字列を実行できます。
例109-29に示すように、DirectReadQuery
を使用すると、データの1列(1つのフィールド)を読み取り、結果セットを表すRecord
オブジェクトのCollection
を返すことができます。
ValueReadQuery
を使用すると、単一データ値(1つのフィールド)を読み取ることができます。例109-30に示すように、単一データ値が返されます。または、行が返されない場合は、NULLが返されます。
例109-30 ValueReadQueryの使用
ValueReadQuery valueReadQuery = new ValueReadQuery();
valueReadQuery.setSQLString("SELECT DISTINCT CURRENT TIMESTAMP FROM SYSTABLES");
// result is a single Object value
Object result = session.executeQuery(valueReadQuery);
警告: 検証されていないSQL文字列をメソッド(たとえば |
例109-31に示すように、DataModifyQuery
を使用すると、選択を行わないSQL文を(直接またはSQLCall
として)実行できます。これは、Session
メソッドexecuteNonSelectingCall
と同等です(109.4項「SQLCallの使用」を参照)。
すべてのDatabaseQuery
オブジェクトには、カスタムSQL文字列を定義できるsetSQLString
メソッドが用意されています。
問合せにおけるカスタムSQLの使用方法の詳細は、109.4項「SQLCallの使用」を参照してください。
例109-32では、SQLを使用してすべての従業員IDを読み取っています。
例109-32 SQLを使用したダイレクト読取り問合せ
DirectReadQuery query = new DirectReadQuery(); query.setSQLString("SELECT EMP_ID FROM EMPLOYEE"); List ids = (List) session.executeQuery(query);
例109-33では、SQLを使用して別のデータベースに切り替えています。
例109-33 SQLを使用したデータ変更問合せ
DataModifyQuery query = new DataModifyQuery(); query.setSQLString("USE SALESDATABASE"); session.executeQuery(query);
警告: 検証されていないSQL文字列をメソッド(たとえば |
詳細は、『EclipseLink Developer's Guide』の「How to Specify a Custom JPQL String in a DatabaseQuery」(http://wiki.eclipse.org/Using_Basic_Query_API_%28ELUG%29#How_to_Specify_a_Custom_JPQL_String_in_a_DatabaseQuery
)を参照してください。
すべてのDatabaseQuery
オブジェクトには、カスタムEJB QL文字列を指定できるsetEJBQLString
メソッドが用意されています。
JPA問合せの詳細は、109.2.7項「DatabaseQueryにおけるカスタムJPQL文字列の指定方法」を参照してください。
参照クラスおよびSELECT
句の両方を指定し、問合せを通常どおり実行します。
例109-34 EJB QL
ReadAllQuery query = new ReadAllQuery(EmployeeBean.class); query.setEJBQLString("SELECT OBJECT(emp) FROM EmployeeBean emp"); … List returnedObjects = (List)session.executeQuery(query);
例109-35は例109-34と同様の問合せを定義したものですが、引数のVectorを作成して入力し、executeQuery
メソッドに渡しています。
例109-35 EJB QLを使用し、引数を渡す単純なReadAllQuery
// First define the query ReadAllQuery query = new ReadAllQuery(EmployeeBean.class); query.setEJBQLString("SELECT OBJECT(emp) FROM EmployeeBean emp WHERE emp.firstName = ?1"); query.addArgument("1", String.class); ... // Next define the arguments Vector arguments = new Vector(); arguments.add("Bob"); ... // Finally, execute the query passing in the arguments List returnedObjects = (List)session.executeQuery(query, arguments);
デフォルトでは、TopLinkでパラメータ使用のSQL(パラメータ・バインド)およびSQL文のキャッシュを有効にします。そうすることで、TopLinkではプリコンパイルされたSQL文を使用し、すべてのSQLパラメータをバインドして、プリコンパイルされたSQL文をキャッシュします。この問合せを再実行するときにはSQLのプリコンパイルが不要になるため、パフォーマンスが向上します。
個々の問合せでパラメータ使用のSQLおよびSQL文のキャッシュを無効にするには、DatabaseQuery
のメソッドsetShouldBindAllParameters
およびsetShouldcacheStatement
を使用し、false
の引数付きで渡します。この機能を再度有効にするには、true
の引数付きで渡します。
例109-36 パラメータ使用のSQLによる単純なReadObjectQuery
ReadObjectQuery query = new ReadObjectQuery(Employee.class); query.setShouldBindAllParameters(true); query.setShouldCacheStatement(true);
あるいは、次のいずれかのレベルでパラメータ使用のSQLとバインドを構成できます。
プロジェクト・レベル: すべての名前付き問合せに適用します(20.7項「プロジェクト・レベルでの名前付き問合せパラメータ使用のSQLおよびSQL文のキャッシュの構成」を参照)。
ディスクリプタ・レベル: 名前付き問合せごとに適用します(119.7.1.9項「名前付き問合せのオプションの構成」を参照)。
セッション・データベース・ログイン・レベル: すべての問合せに適用し(98.6項「JDBCオプションの構成」を参照)、その他のパラメータ・バインドAPIを提供して、SQL文のサイズについて一部のドライバの制限を緩和します。
データ・アクセスの最適化のためのパラメータ使用のSQLおよびバインドの使用方法の詳細は、12.11.5項「パラメータ使用のSQL(パラメータ・バインド)とプリコンパイルされたSQL文のキャッシュを使用した最適化方法」を参照してください。
注意: Java EEデータ・ソースまたは外部接続プールを使用するアプリケーションでは、TopLinkにおいてではなくJava EEサーバーのデータ・ソースにおいて、文のキャッシュを構成する必要があります。 |
名前付き問合せは、一度作成すると、基礎となる関連オブジェクトすべてとともに後で効率的に再利用できるため、頻繁に実行される操作に適しており、アプリケーションのパフォーマンスを向上させることができます。
名前付き問合せはセッション・レベル(89.13項「セッション・レベルでの名前付き問合せの構成」を参照)またはディスクリプタ・レベル(119.7項「ディスクリプタ・レベルでの名前付き問合せの構成」を参照)で構成できます。
セッション・レベルでの名前付き問合せの場合は、次のセッション
APIメソッドのいずれかを使用して問合せを実行できます。
executeQuery(String queryName)
executeQuery(String queryName, arg1)
executeQuery(String queryName, arg1, arg2)
executeQuery(String queryName, arg1, arg2, arg3)
executeQuery(String queryName, Vector args)
例109-37 セッション・レベルでの名前付き問合せの実行
Vector args = new Vector(); args.add("Sarah"); Employee sarah = (Employee)session.executeQuery( "employeeReadByFirstName", args );
ディスクリプタ・レベルでの名前付き問合せの場合は、例109-38に示すように、次のセッション
APIコールのいずれかを使用して問合せを実行できます。
executeQuery(String queryName, Class domainClass)
executeQuery(String queryName, Class domainClass, arg1)
executeQuery(String queryName, Class domainClass, arg1, arg2)
executeQuery(String queryName, Class domainClass, arg1, arg2, arg3)
executeQuery(String queryName, Class domainClass, Vector args)
例109-38 ディスクリプタ・レベルでの名前付き問合せの実行
Vector args = new Vector(); args.add("Sarah"); Employee sarah = (Employee)session.executeQuery( "ReadByFirstName", Employee.class, args );
詳細は、108.8項「名前付き問合せ」を参照してください。
TopLinkの式フレームワークを使用すると、複雑な問合せをオブジェクト・レベルで定義できます。アプリケーションでより複雑な問合せ、またはデータに直接アクセスする問合せを必要とする場合は、カスタムSQL文字列をSQLCall
オブジェクトに指定し、DatabaseQuery
のコンテキストまたはCall
オブジェクト実行用のセッションAPIを使用して、SQL文字列を実行できます。
どのような問合せにも式のかわりにSQLCall
オブジェクトを指定できますが、SQLCall
に含まれるSQL文字列は、問い合せたクラスのインスタンスの作成に必要なデータをすべて返す必要があります。
SQL文字列には、JDBCデータ・タイプを使用する入力、出力、および入出力引数を含む複雑なSQL問合せを指定できます。
警告: 検証されていないSQL文字列をメソッドに渡せるようにすると、SQLインジェクション攻撃に対してアプリケーションが脆弱になります。 |
SQLCall
は引数なしでも構成でき、セッション
APIを使用して直接実行できます。この手法は、引数なしで(またはハードコードされた引数の値を指定して)SQL文字列を実行する場合に使用します。
引数なしのSQLCall入力の構成手順:
入力、出力、入出力引数のいずれかの組合せをとるSQLCall
を構成できます。この手法は、実行時に引数の値をSQLCall
にバインドするか、実行時間にSQLCall
からの出力値を受け取るか、あるいはその両方を実行する場合に使用します。
JDBCデータ・タイプを使用した引数を持つSQLCallの構成手順:
SQLCall
オブジェクトをインスタンス化します。
SQL文字列を作成し、入力、出力または入出力のいずれかとして引数を指定します。
TopLinkでは、次のようにSQLCall
のカスタムSQL文字列のトークンの先頭に1つ以上のシャープ記号(#
)が付いている場合は、そのトークンが引数であるものとみなします。
コンストラクタにSQL文字列を渡します(例109-40、例109-41および例109-42を参照)。
または、SQLCall
メソッドのsetSQLString
を使用することもできます。
各出力引数について、適切なSQLCall
メソッドsetCustomSQLArgumentType
を使用してTopLinkで使用するJavaデータ・タイプを指定し、出力値を返します(例109-41を参照)。
入力引数の場合、TopLinkではJavaデータ・タイプが自動的に適切なJDBCデータ・タイプに変換されます。
入出力引数の場合、入力値のタイプによって、出力値のタイプが決まります。例109-42が示すように、in_out
に渡される引数の値のデータ・タイプはString
(MacDonald)であり、このためTopLinkでは、String
として(EMP_ID
の)出力値を返します。
SQL文字列用に適切なDatabaseQuery
をインスタンス化します。
DatabaseQuery
メソッドsetCall
を使用し、SQLCall
でDatabaseQuery
を構成します(例109-40、例109-41および例109-42を参照)。
DatabaseQuery
メソッドaddArgument
を使用し、すべての入力引数および入出力引数の名前を指定します(例109-40、例109-41および例109-42を参照)。
手順7で引数名を指定したときと同じ順序で引数の値のVector
を作成します(例109-40、例109-41および例109-42を参照)。
引数に値をバインドし、Session
メソッドexecuteQuery(DatabaseQuery, java.util.Vector)
を使用して引数の値のDatabaseQuery
およびVector
に渡し、DatabaseQuery
を実行します(例109-40、例109-41および例109-42を参照)。
例109-40 入力引数(JDBCデータ・タイプ接頭辞#付き)を持つSQLCallの指定
SQLCall sqlCall = new SQLCall("INSERT INTO EMPLOYEE (L_NAME) VALUES (#last_name)");
DataModifyQuery query = new DataModifyQuery();
query.setCall(sqlCall);
query.addArgument("last_name"); // input
Vector arguments = new Vector();
arguments.add("MacDonald");
session.executeQuery(query, arguments);
例109-41 出力引数(JDBCデータ・タイプ接頭辞###付き)を持つSQLCallの指定
SQLCall sqlCall = new SQLCall(
"BEGIN INSERT INTO EMPLOYEE (L_NAME) VALUES (#last_name)
RETURNING EMP_ID INTO ###employee_id; END;");
sqlCall.setCustomSQLArgumentType("employee_id", Integer.class); // specify output value type
ValueReadQuery query = new ValueReadQuery();
query.setCall(sqlCall);
query.addArgument("last_name"); // input
Vector args = new Vector();
args.add("MacDonald");
Integer employeeID = (Integer) getSession().executeQuery(query, args);
例109-42 入出力引数(JDBCデータ・タイプ接頭辞####付き)を持つSQLCallの指定
SQLCall sqlCall = new SQLCall(
"BEGIN INSERT INTO EMPLOYEE (L_NAME) VALUES (####in_out)
RETURNING EMP_ID INTO ####in_out; END;");
ValueReadQuery query = new ValueReadQuery();
query.setCall(sqlCall);
query.addArgument("in_out"); // input and outpu
Vector args = new Vector();
args.add("MacDonald"); // type of input argument determines type of output value
String lastName = (String) getSession().executeQuery(query, args);
SQLコールを使用する際に、ReturningPolicy
を使用して、TopLinkでパラメータに書き込むかまたはデータベースによって生成された値を取得するかを制御できます。
ストアド・プロシージャまたはストアド・ファンクションを起動する場合は、StoredProcedureCall
またはStoredFunctionCall
を使用します。
あるいは、DatabaseQuery
に直接単純なSQL文字列を指定することもできます。この方法を使用すると、SQL文字列が単純な場合、ハードコードされた引数を使用して(または引数なし)SQLCall
オブジェクト作成のオーバーヘッドを回避でき、SQLCall
が提供する追加のAPIが不要です。
詳細は、次を参照してください。
TopLinkの式フレームワークを使用すると、複雑な問合せをオブジェクト・レベルで定義できます。アプリケーションでより複雑な問合せ、またはデータベースに付属の既存のストアド・プロシージャを起動する問合せを必要とする場合は、JDBCおよびPL/SQLの両方のデータ・タイプを使用してStoredProcedureCall
オブジェクトを定義し、DatabaseQuery
のコンテキストでストアド・プロシージャを起動できます。
Oracleデータベースを使用している場合は、JDBCおよびPL/SQL(非JDBC)の両方のデータ・タイプに渡すことができます。
Oracle以外のデータベースを使用している場合は、JDBCデータ・タイプにのみ渡すことができます。
この項の内容は次のとおりです。
JDBCデータ・タイプを使用したStoredProcedureCallでの簡易オプティミスティック・バージョン・ロック値の指定方法
JDBCまたはPL/SQLデータ・タイプを使用したStoredProcedureCall出力パラメータ・イベントの構成方法
StoredProcedureCall
は引数なしでも構成でき、セッション
APIを使用して直接実行できます。この手法は、引数をとらないあるいは値を返さないストアド・プロシージャを実行する場合に使用します。
JDBCデータ・タイプを使用した引数なしのStoredProcedureCallの構成手順:
StoredProcedureCall
オブジェクトをインスタンス化します。
例109-43に示すように、StoredProcedureCall
メソッドsetProcedureName
を使用して、実行するストアド・プロシージャの名前を設定します。
適切なセッション
APIを使用してStoredProcedureCall
を実行します(例109-43を参照)。
実行するストアド・プロシージャのタイプに応じて、次のSession
メソッドのいずれかを使用できます。
executeSelectingCall
: それぞれがデータベース行を表しているRecord
オブジェクトからなるList
を返します。
executeNonSelectingCall
: void
を返します。
入力、出力、入出力引数のいずれかの組合せをとるStoredProcedureCall
を構成できます。この手法は、実行時に引数の値をStoredProcedureCall
にバインドするか、実行時間にStoredProcedureCall
からの出力値を受け取るか、あるいはその両方を実行する場合に使用します。
注意: このプロシージャは、入力、出力および入出力引数がすべてJDBCデータ・タイプである場合に使用します。1つまたは複数の引数がPL/SQL(非JDBC)データ・タイプである場合は、109.5.3項「PL/SQLデータ・タイプ引数を持つPLSQLStoredProcedureCallの構成方法」を参照してください。 |
JDBCデータ・タイプを使用した引数を持つStoredProcedureCallの構成手順:
StoredProcedureCall
オブジェクトをインスタンス化します。
StoredProcedureCall
メソッドsetProcedureName
を使用し、コールするストアド・プロシージャの名前を指定します(例109-44、例109-45および例109-46を参照)。
各引数について、適切なStoredProcedureCall
メソッドを使用して引数が入力、出力または入出力のいずれかの引数であるかを指定します。
入力引数の場合: addNamedArgument
(例109-44を参照)
出力引数の場合: addNamedOutputArgument
(例109-45を参照)
入出力引数の場合: addNamedInOutputArgument
(例109-46を参照)
通常は、すべての出力引数および入出力引数に対して戻りJavaデータ・タイプを常に指定します(例109-45および例109-46を参照)。戻りJavaデータ・タイプを指定しない場合、デフォルトはjava.lang.String
です。
一般的に引数を指定するには、ストアド・プロシージャ引数名をそのまま使用します。ただし、ストアド・プロシージャ引数名を、DatabaseQuery
で使用する代替名に関連付けることができます(例109-44を参照)。ストアド・プロシージャ引数名が曖昧な場合は、この方法を使用して有効な引数名を指定します。
ストアド・プロシージャ用に適切なDatabaseQuery
をインスタンス化します。
DatabaseQuery
メソッドsetCall
を使用し、StoredProcedureCall
でDatabaseQuery
を構成します(例109-44、例109-45および例109-46を参照)。
DatabaseQuery
メソッドaddArgument
を使用し、すべての入力引数および入出力引数の名前を指定します(例109-44、例109-45および例109-46を参照)。
手順3でストアド・プロシージャ引数名を有効な代替名に関連付けた場合は、DatabaseQuery
メソッドaddArgument
でその代替名を使用します(例109-44を参照)。
手順6で引数名を指定したときと同じ順序で引数の値のVector
を作成します(例109-44、例109-45および例109-46を参照)。
引数に値をバインドし、Session
メソッドexecuteQuery(DatabaseQuery, java.util.Vector)
を使用して引数の値のDatabaseQuery
およびVector
に渡し、DatabaseQuery
を実行します(例109-44、例109-45および例109-46を参照)。
例109-44 入力引数(JDBCデータ・タイプ)を持つStoredProcedureCallの指定
// CREATE PROCEDURE INSERT_EMPLOYEE(L_NAME IN VARCHAR) AS // BEGIN // Insert an EMP record initialized with last name. // END; StoredProcedureCall spcall = new StoredProcedureCall(); spcall.setProcedureName("INSERT_EMPLOYEE"); spcall.addNamedArgument("L_NAME", "last_name"); DataModifyQuery query = new DataModifyQuery(); query.setCall(spcall); query.addArgument("last_name"); // input Vector arguments = new Vector(); arguments.add("MacDonald"); session.executeQuery(query, arguments);
例109-45 出力引数(JDBCデータ・タイプ)を持つStoredProcedureCallの指定
// CREATE PROCEDURE GET_EMP_ID(L_NAME IN VARCHAR, EMP_ID OUT INTEGER) AS // BEGIN // Insert an EMP record initialized with last name and return the EMP_ID for this record. // END; StoredProcedureCall spcall = new StoredProcedureCall(); spcall.setProcedureName("GET_EMP_ID"); spcall.addNamedArgument("L_NAME"); spcall.addNamedOutputArgument( "EMP_ID", // procedure parameter name "EMP_ID", // out argument field name Integer.class // Java type corresponding to type returned by procedure ); ValueReadQuery query = new ValueReadQuery(); query.setCall(spcall); query.addArgument("L_NAME"); // input Vector args = new Vector(); args.add("MacDonald"); Integer employeeID = (Integer) getSession().executeQuery(query, args);
例109-46 入出力引数(JDBCデータ・タイプ)を持つStoredProcedureCallの指定
// CREATE PROCEDURE INSERT_EMPLOYEE(IN_OUT INOUT VARCHAR) AS
// BEGIN
// Insert an EMP record initialized with last name
// and return the EMP_CODE_NAME for this record.
// END;
StoredProcedureCall spcall = new StoredProcedureCall();
spcall.setProcedureName("INSERT_EMP"); // returns EMP_CODE_NAME after insert
spcall.addNamedInOutputArgument(
"IN_OUT", // procedure parameter name
"IN_OUT", // out argument field name
String.class // Java type corresponding to type returned by procedure
);
ValueReadQuery query = new ValueReadQuery();
query.setCall(sqlCall);
query.addArgument("INOUT"); // input and outpu
Vector args = new Vector();
args.add("MacDonald"); // type of input argument determines type of output value
String employeeCode = (String) getSession().executeQuery(query, args));
入力引数、出力引数、入出力引数のいずれかの組合せのデータ・タイプがPL/SQL(非JDBC)の場合には、oracle.toplink.platform.database.oracle.PLSQLStoredProcedureCall
クラスを使用する必要があります。この手法は、実行時に引数の値をPLSQLStoredProcedureCall
にバインドするか、実行時間にPLSQLStoredProcedureCall
からの出力値を受け取るか、あるいはその両方を実行する場合に使用します。
注意: すべての引数がJDBC(非PL/SQLデータ・タイプ)である場合は、109.5.2項「JDBCデータ・タイプを使用した引数を持つStoredProcedureCallの構成方法」を参照してください。 |
JDBCデータ・タイプおよびPL/SQLデータ・タイプを使用した引数を持つPLSQLStoredProcedureCallの構成手順:
PLSQLStoredProcedureCall
オブジェクトをインスタンス化します。
PLSQLStoredProcedureCall
メソッドsetProcedureName
を使用し、コールするストアド・プロシージャの名前を指定します(例109-47、例109-48および例109-49を参照)。
各引数について、適切なPLSQLStoredProcedureCall
メソッドを使用して、引数が入力、出力または入出力引数のいずれであるかを指定します。
入力引数の場合: addNamedArgument
(例109-47を参照)
出力引数の場合: addNamedOutputArgument
(例109-48を参照)
入出力引数の場合: addNamedInOutputArgument
(例109-49を参照)
入力、出力および入出力のすべての引数にデータ・タイプを指定する必要があります。JDBCタイプの指定にはoracle.toplink.platform.database.jdbc.JDBCTypes
を、PL/SQL(非JDBC)タイプの指定にはoracle.toplink.platform.database.oracle.OraclePLSQLTypes
を使用します。
JDBCおよびPL/SQL入力引数(および入出力引数のinの値)の場合は、十分なサイズと精度のあるJavaタイプを引数に使用できます。
JDBC出力引数(および入出力引数のoutの値)の場合、TopLinkでは従来どおり、JDBCデータ・タイプをJavaタイプに変換します。PL/SQL出力引数(および入出力引数のoutの値)の場合、TopLinkではPL/SQLデータ・タイプを、表109-1に示すJavaデータ・タイプに変換します。
一般的に引数を指定するには、ストアド・プロシージャ引数名をそのまま使用します。ただし、ストアド・プロシージャ引数名を、DatabaseQuery
で使用する代替名に関連付けることができます(例109-47を参照)。ストアド・プロシージャ引数名が曖昧な場合は、この方法を使用して有効な引数名を指定します。
ストアド・プロシージャ用に適切なDatabaseQuery
をインスタンス化します。
DatabaseQuery
メソッドsetCall
を使用し、PLSQLStoredProcedureCall
でDatabaseQuery
を構成します(例109-47、例109-48および例109-49を参照)。
DatabaseQuery
メソッドaddArgument
を使用し、すべての入力引数および入出力引数の名前を指定します(例109-47、例109-48および例109-49を参照)。
手順3でストアド・プロシージャ引数名を有効な代替名に関連付けた場合は、DatabaseQuery
メソッドaddArgument
でその代替名を使用します(例109-47を参照)。
手順6で引数名を指定したときと同じ順序で引数の値のVector
を作成します(例109-47、例109-48および例109-49を参照)。
引数に値をバインドし、Session
メソッドexecuteQuery(DatabaseQuery, java.util.Vector)
を使用して引数の値のDatabaseQuery
およびVector
に渡し、DatabaseQuery
を実行します(例109-47、例109-48および例109-49を参照)。
例109-47 入力引数(JDBCおよびPL/SQLデータ・タイプ)を持つPLSQLStoredProcedureCallの指定
// CREATE PROCEDURE INSERT_EMPLOYEE(L_NAME IN VARCHAR, MANAGER IN BOOLEAN) AS // BEGIN // Insert an EMP record initialized with last name and whether or not the employee // is a manager. // END; PLSQLStoredProcedureCall plsqlcall = new PLSQLStoredProcedureCall(); plsqlcall.setProcedureName("INSERT_EMPLOYEE"); plsqlcall.addNamedArgument("L_NAME", JDBCTypes.VARCHAR_TYPE, 40); // must define length plsqlcall.addNamedArgument("MANAGER", OraclePLSQLTypes.PLSQLBoolean); DataModifyQuery query = new DataModifyQuery(); query.setCall(plsqlcall); query.addArgument("L_NAME"); // input query.addArgument("MANAGER"); // input Vector arguments = new Vector(); arguments.add("MacDonald"); arguments.add(Integer.valueOf(1)); session.executeQuery(query, arguments);
例109-48 出力引数(JDBCおよびPL/SQLデータ・タイプ)を持つPLSQLStoredProcedureCallの指定
// CREATE PROCEDURE GET_EMP_ID(L_NAME IN VARCHAR, EMP_ID OUT PLS_INTEGER) AS // BEGIN // Insert an EMP record initialized with last name and return EMP_ID for this row. // END; PLSQLStoredProcedureCall plsqlcall = new PLSQLStoredProcedureCall(); plsqlcall.setProcedureName("GET_EMP_ID"); plsqlcall.addNamedArgument("L_NAME", JDBCTypes.VARCHAR_TYPE, 25); // must define length plsqlcall.addNamedOutputArgument("EMP_ID", OraclePLSQLTypes.PLSQLInteger); ValueReadQuery query = new ValueReadQuery(); query.setCall(plsqlcall); query.addArgument("L_NAME"); // input Vector args = new Vector(); args.add("MacDonald"); Number employeeID = (Number) getSession().executeQuery(query, args);
例109-49 入出力引数(JDBCおよびPL/SQLデータ・タイプ)を持つPLSQLStoredProcedureCallの指定
// CREATE PROCEDURE INSERT_EMP(IN_OUT INOUT PLS_INTEGER) AS
// BEGIN
// Insert an EMP record initialized with department id and return
// the EMP_ID for this record.
// END;
PLSQLStoredProcedureCall plsqlcall = new PLSQLStoredProcedureCall();
plsqlcall.setProcedureName("INSERT_EMP");
plsqlcall.addNamedInOutputArgument("IN_OUT", OraclePLSQLTypes.PLSQLInteger);
ValueReadQuery query = new ValueReadQuery();
query.setCall(plsqlcall);
query.addArgument("IN_OUT"); // input and outpu
Vector args = new Vector();
args.add(Integer.valueOf(1234)); // department id
Integer employeeID = new Integer(BigDecimal.intValue(
getSession().executeQuery(query, args)));
オプティミスティック・バージョン・ロックを使用する場合は、一般的にバージョン・フィールドを更新する役割をTopLinkに委譲します。
あるいは、ストアド・プロシージャを使用して、すべての作成、読取り、更新および削除操作のバージョン・フィールドの手動更新を選択することもできます。
オプティミスティック・ロックおよびストアド・プロシージャ・コールを使用する場合、ストアド・プロシージャでTopLinkとは別に生成できる単純な連続数値のみ使用できます。タイムスタンプなどの複雑な値を使用する場合は、バージョン・フィールドを更新する役割をTopLinkに委譲する必要があります。
詳細は、16.4.1項「オプティミスティック・バージョン・ロック・ポリシー」を参照してください。
JDBCデータ・タイプを使用したStoredProcedureCallで簡易オプティミスティック・バージョン・ロック値を指定する手順:
作成、読取り、更新および削除操作用のストアド・プロシージャを作成します。
各ストアド・プロシージャには、オプティミスティック・ロック・フィールドをチェックおよび更新する役割があります。データベース内では単純な連続する数値です。
例109-50は、更新操作用の標準的なストアド・プロシージャを示します。
例109-50 簡易オプティミスティック・バージョン・ロックを使用した更新操作用のストアド・プロシージャ
PROCEDURE Update_Employee ( P_EMP_ID NUMBER, P_SALARY NUMBER, P_END_DATE DATE, P_MANAGER_ID NUMBER, P_START_DATE DATE, P_F_NAME VARCHAR2, P_L_NAME VARCHAR2, P_GENDER VARCHAR2, P_ADDR_ID NUMBER, P_VERSION NUMBER, P_START_TIME DATE, P_END_TIME DATE, O_ERROR_CODE OUT NUMBER) AS BEGIN Update SALARY set SALARY = P_SALARY WHERE (EMP_ID = P_EMP_ID); Update EMPLOYEE set END_DATE = P_END_DATE, MANAGER_ID = P_MANAGER_ID, VERSION = P_VERSION + 1, START_DATE = P_START_DATE, F_NAME = P_F_NAME, L_NAME = P_L_NAME, GENDER = P_GENDER, ADDR_ID = P_ADDR_ID where ((EMP_ID = P_EMP_ID) and (VERSION = P_VERSION)); O_ERROR_CODE := SQL%ROWCOUNT; END;
それぞれの作成、読取り、更新および削除カスタム・ストアド・プロシージャについてStoredProcedureCall
を作成します。
例109-51は、例109-50の更新ストアド・プロシージャ用のStoredProcedureCall
を示します。
例109-51 更新ストアド・プロシージャ用のStoredProcedureCall
UpdateObjectQuery updateQuery = new UpdateObjectQuery(); call = new StoredProcedureCall(); call.setUsesBinding(true); call.setProcedureName("Update_Employee"); call.addNamedArgument("P_EMP_ID", "EMP_ID"); call.addNamedArgument("P_SALARY", "SALARY"); call.addNamedArgument("P_END_DATE", "END_DATE"); call.addNamedArgument("P_MANAGER_ID", "MANAGER_ID"); call.addNamedArgument("P_START_DATE", "START_DATE"); call.addNamedArgument("P_F_NAME", "F_NAME"); call.addNamedArgument("P_L_NAME", "L_NAME"); call.addNamedArgument("P_GENDER", "GENDER"); call.addNamedArgument("P_ADDR_ID", "ADDR_ID"); call.addNamedArgument("P_VERSION", "VERSION"); call.addNamedArgument("P_START_TIME", "START_TIME"); call.addNamedArgument("P_END_TIME", "END_TIME"); call.addNamedOutputArgument("O_ERROR_CODE", "O_ERROR_CODE", Long.class); updateQuery.setCall(call);
詳細は、次を参照してください。
TopLinkディスクリプタ問合せマネージャを構成して、StoredProcedureCall
オブジェクトを作成、読取り、更新および削除操作に使用します。
例109-52は、ディスクリプタ・カスタマイザ・クラスを使用し、例109-51の更新StoredProcedureCall
でTopLinkディスクリプタ問合せマネージャを更新する方法を示します。
例109-52 StoredProcedureCallを使用したTopLinkディスクリプタ問合せマネージャの構成
import oracle.toplink.tools.sessionconfiguration.DescriptorCustomizer; import oracle.toplink.descriptors.ClassDescriptor; public class EmployeeDescriptorCustomizer implements DescriptorCustomizer { public void customize(ClassDescriptor descriptor) { descriptor.getQueryManager().setUpdateQuery(updateQuery); } }
詳細は、次を参照してください。
エラーを処理するためにStoredProcedureCall
出力パラメータ・イベントを定義します。
注意: Oracleデータベースでは、ストアド・プロシージャをコールする際、行数は保持されません。出力パラメータを使用して行数が必ず返されるようにしてください。Session イベントoutputParametersDetected を使用すると、行数がチェックされてエラーがスローされます。または、このストアド・プロシージャによって、行数をチェックしてエラーをスローすることもできます。 |
詳細は、109.5.5項「JDBCまたはPL/SQLデータ・タイプを使用したStoredProcedureCall出力パラメータ・イベントの構成方法」を参照してください。
TopLinkでは、出力パラメータ・イベントをサポートするデータベース用に出力パラメータ・イベントが管理されます。たとえば、アプリケーションがエラー条件のチェックを必要としていることを示すエラー・コードがストアド・プロシージャから返されると、セッション・イベントoutputParametersDetected
が発生し、アプリケーションは出力パラメータを処理することができます。
JDBCまたはPL/SQLデータ・タイプを使用したStoredProcedureCall出力パラメータ・イベントの構成手順:
JDBC引数またはPL/SQL引数あるいはその両方を使用して、StoredProcedureCall
を作成します。例109-53は、JDBC引数を使用したStoredProcedureCall
を示します。
詳細は、次を参照してください。
例109-53 ストアド・プロシージャ
PROCEDURE Update_Employee ( P_EMP_ID NUMBER, P_SALARY NUMBER, P_END_DATE DATE, P_MANAGER_ID NUMBER, P_START_DATE DATE, P_F_NAME VARCHAR2, P_L_NAME VARCHAR2, P_GENDER VARCHAR2, P_ADDR_ID NUMBER, P_VERSION NUMBER, P_START_TIME DATE, P_END_TIME DATE, O_ERROR_CODE OUT NUMBER) AS BEGIN Update SALARY set SALARY = P_SALARY WHERE (EMP_ID = P_EMP_ID); Update EMPLOYEE set END_DATE = P_END_DATE, MANAGER_ID = P_MANAGER_ID, VERSION = P_VERSION + 1, START_DATE = P_START_DATE, F_NAME = P_F_NAME, L_NAME = P_L_NAME, GENDER = P_GENDER, ADDR_ID = P_ADDR_ID where ((EMP_ID = P_EMP_ID) and (VERSION = P_VERSION)); O_ERROR_CODE := SQL%ROWCOUNT; END;
outputParametersDetected
イベントを処理するSessionEventListener
を作成します(例109-54を参照)。
SessionEventListener
を作成する簡単な方法は、oracle.toplink.sessions.SessionEventAdapter
をサブクラス化することです。使用する特定のSessionEventListener
メソッドをオーバーライドするのみで済みます。
例109-54では、SessionEvent
メソッドgetProperty
は、ERROR_CODE
の引数の値を使用しています。このプロパティ名およびそのデータ・タイプは、StoredProcedureCall
メソッドaddNamedOutputArgument
で定義されています。
例109-54 outputParametersDetected EventのSessionEventListener
import oracle.toplink.sessions.SessionEventAdapter; import oracle.toplink.sessions.SessionEvent; public class OptimisticLockListener extends SessionEventAdapter { public OptimisticLockListener() { } public void outputParametersDetected(SessionEvent event) { DatabaseQuery query = event.getQuery(); if ((query != null) && query.isObjectLevelModifyQuery()) { Number rowcount = new Integer(1); if (event.getResult() instanceof Map) { rowcount = (Number)((Map)event.getResult()).get("O_ERROR_CODE"); } if (rowcount.longValue() <= 0) { if (query.isDeleteObjectQuery()) { DeleteObjectQuery deleteQuery = (DeleteObjectQuery)query; throw OptimisticLockException.objectChangedSinceLastReadWhenDeleting( deleteQuery.getObject(), deleteQuery); } else if (query.isWriteObjectQuery()) { WriteObjectQuery updateQuery = (WriteObjectQuery)query; throw OptimisticLockException.objectChangedSinceLastReadWhenUpdating( updateQuery.getObject(), updateQuery); } } } } }
セッション・イベント・マネージャにSessionEventListenerインスタンスを追加します(例109-55を参照)。
この手順はストアド・プロシージャを実行する前に行う必要があります。
詳細は、87.2.5項「セッション・イベント・マネージャによるセッション・イベントの管理」を参照してください。
問合せを実行します。
エラーおよびoutputParametersDetected
タイプのSessionEvent
が発生した場合、TopLinkではSessionEventListener
に通知します。
TopLinkでは、out引数(および入出力引数のoutの値)用のPL/SQLデータ・タイプを、表109-1に示すJavaデータ・タイプに自動的に変換します。
表109-1 TopLink PL/SQLのJavaデータ・タイプへの変換: out引数および入出力引数のoutの値
PL/SQLデータ・タイプ | OraclePLSQLTypes列挙タイプ | Javaタイプ |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Javaタイプのサイズと精度がPL/SQLタイプに適している場合、Javaタイプの値をPL/SQLのin引数(または入出力引数のin値)に使用できます。
注意: OUT またはINOUT パラメータを持つStoredProcedureCall を使用する際に、DatabaseQuery のメソッドbindAllParameters を使用する必要がなくなりました。ただし、すべてのOUT およびINOUT パラメータのJavaタイプを必ず指定する必要があります。これを指定しないと、デフォルトとしてタイプString が使用されることに注意してください。 |
TopLinkの式フレームワークを使用すると、複雑な問合せをオブジェクト・レベルで定義できます。アプリケーションでより複雑な問合せ、またはデータベースに付属の既存のストアド・ファンクションを起動する問合せを必要とする場合は、JDBCおよびPL/SQLの両方のデータ・タイプを使用してStoredFunctionCall
オブジェクトを定義し、DatabaseQuery
のコンテキストでストアド・ファンクションを起動できます。
すべてのデータベースがストアド・ファンクションを提供しているわけではありません。
例109-56では、StoredFunctionCall
のメソッドsetProcedureName
を使用してストアド・ファンクションの名前が設定されていることに注意してください。
例109-56 StoredFunctionCallの作成
StoredFunctionCall functionCall = new StoredFunctionCall(); functionCall.setProcedureName("CHECK_VALID_EMPLOYEE"); functionCall.addNamedArgument("EMP_ID"); functionCall.setResult("FUNCTION_RESULT", String.class); ValueReadQuery query = new ValueReadQuery(); query.setCall(functionCall); query.addArgument("EMP_ID"); Vector args = new Vector(); args.addElement(new Integer(44)); String valid = (String) session.executeQuery(query, args);
一般的に、ストアド・プロシージャとストアド・ファンクションのどちらにおいても、入力パラメータ、出力パラメータおよび入出力パラメータを指定できます。詳細は、109.5項「StoredProcedureCallの使用」を参照してください。ただし、ストアド・プロシージャでは値を返す必要はありませんが、ストアド・ファンクションでは必ず値を1つ返します。
StoredFunctionCall
クラスは、StoredProcedureCall
を拡張して新しく1つのメソッドsetResult
を追加したものです。このメソッドを使用して、ストアド・ファンクションの戻り値の、TopLinkによる保存先の名前(または名前とタイプの両方)を指定します。
TopLinkでStoredFunctionCall
をプリコンパイルする際には、そのSQLが検証され、次の場合にValidationException
がスローされます。
ストアド・ファンクションが現在のプラットフォームでサポートされていない場合。ストアド・ファンクションは、Oracleでのみサポートされています。
戻りタイプを指定しなかった場合。
TopLinkの式フレームワークを使用すると、複雑な問合せをオブジェクト・レベルで定義できます。
あるいは別の方法として、カスタムJPQL文字列をJPQLコール・オブジェクトに指定し、そのオブジェクトを任意の問合せに対して指定することもできます。詳細は、『EclipseLink Developer's Guide』の「Using Java Persistence Query Language (JPQL) Calls」(http://wiki.eclipse.org/Using_Basic_Query_API_%28ELUG%29#Using_Java_Persistence_Query_Language_.28JPQL.29_Calls
)を参照してください。
EISルート・ディスクリプタの場合は、EISインタラクションを定義してEISに対してメソッドを起動できます。
TopLinkでは、EISインタラクションを表すためにoracle.toplink.eis.interactions.EISInteraction
のインスタンスを使用します。これらのクラスにはCall
インタフェースが実装されているため、Call
を使用できるところではどこでもこれらのクラスを使用できます。
表109-2は、TopLinkがサポートするEISインタラクションのタイプを示します。
表109-2 EISインタラクション
EISインタラクションのタイプ | 説明 |
---|---|
|
索引付きレコードを使用するJCAインタラクションのコールの仕様定義です。位置による引数から入力レコードと出力レコードを作成します。 |
|
マップされたレコードを使用するJCAインタラクションのコールの仕様定義です。名前による引数から入力レコードと出力レコードを作成します。 |
|
EISプロジェクトに関連付けられたXMLスキーマ文書(XSD)によって定義されたXMLレコードを使用するJCAインタラクションのコールの仕様を定義する、 |
|
問合せ文字列を使用するJCAインタラクションのコールの仕様を定義する |
|
XQueryを使用するJCAインタラクションのコールの仕様を定義する |
TopLinkを使用して、基本的な永続性操作(insert
、update
、delete
、read object
、read all
またはdoes exist
)ごとに1つのインタラクションを定義できます。これにより、EISマップ・オブジェクトに問い合せてそれを変更する際に、TopLinkランタイムでは適切なEISインタラクションが使用されます。詳細は、76.5項「基本的な永続性操作用のカスタムEISインタラクションの構成」を参照してください。
また、TopLinkを使用して、読取り問合せおよびすべて読取り問合せのための名前付き問合せとしてインタラクションを定義することもできます。これらの問合せは、基本的な永続性操作の目的ではコールされません。これら追加の名前付き問合せは、特殊な目的のためにアプリケーション内からコールできます。詳細は、119.7.1.8項「名前付き問合せに関するEISインタラクションの作成」を参照してください。
問合せにおける例外は、ほとんどがデータベース操作の失敗によるデータベース例外です。書込み操作の場合は、オプティミスティック・ロックを使用するアプリケーションでの書込み、更新または削除操作で、OptimisticLockException
がスローされることもあります。これらの例外を捕捉するには、すべてのデータベース操作をtry
-catch
ブロック内で実行します。
try {
List employees = session.readAllObjects(Employee.class);
}
catch (DatabaseException exception) {
// handle exception
}
TopLinkアプリケーションの例外の詳細は、付録A「TopLinkアプリケーションのトラブルシューティング」を参照してください。
TopLinkでは、DataReadQuery
およびReadAllQuery
のサブクラスのすべてにuseCollectionClass
メソッドがあります。このメソッドを使用して問合せを構成し、結果を、Collection
またはMap
の任意の具象インスタンスとして返すようにします。問合せには、ArrayList、HashSet、HashMap、TreeSetなど、様々なコレクション・クラス・タイプを設定できます。
表109-3は、ReportQuery
が結果を返す方法を構成するために使用できるReportQuery
のメソッドを示します。
デフォルトでは、ReportQuery
によって、ReportQueryResult
オブジェクトのCollection
が返されます。
表109-3 レポート問合せの結果のオプション
メソッド | 問合せの戻り値 | 説明 |
---|---|---|
|
|
|
|
|
|
|
Object |
単一の値のみを返します。このオプションは、 |
詳細は、次を参照してください。