Oracle® Mobile Application Framework Oracle Mobile Application Frameworkでのモバイル・アプリケーションの開発 2.3.3 E82940-01 |
|
前 |
次 |
この章では、RESTサービスからリソースを取得して、MAFアプリケーションのクライアント・データ・モデルにデータ・オブジェクトおよびサービス・オブジェクトを作成する方法について説明します。
この章の内容は次のとおりです。
RESTサービスに接続するMAFアプリケーションは、データ・オブジェクトからデータを取得します。Javaクラス、データ・オブジェクトおよびサービス・オブジェクトは、クライアント・データ・モデルの作成のために使用されます。このモデルでは、MAFアプリケーションがオフラインで使用するためにデータを永続化します。
クライアント・データ・モデルのMAFアプリケーションには、MAFアプリケーションのデータ・モデルを表すためのJavaクラスと関連ファイルが含まれています。MAFでは、オフラインでデータを使用できるようにSQLiteデータベースを使用してデータを格納し、データ・オブジェクト(エンティティ・オブジェクトとも呼ばれます)およびサービス・オブジェクト(エンティティCRUDサービス・オブジェクトとも呼ばれます)という2つのタイプのJavaクラスを使用してデータと対話します。データ・オブジェクトは、MAFアプリケーションの接続先のRESTサービスから取得したデータを保持します。MAFアプリケーションは、デバイス上のSQLiteデータベースに対してデータ・オブジェクトを格納および取得します。サービス・オブジェクトは、作成、読取り、更新、削除(CRUD)アクションに加え、データ・オブジェクトに対して機能する他のカスタム・アクションを実行します。通常、サービス・オブジェクトを使用してデータ・コントロールを作成し、そのデータ・コントロールから関連するメソッドおよびエンティティ・コレクションをAMXページにドラッグ・アンド・ドロップして、UIレイヤーを構築できます。MAFクライアント・データ・モデルでは、persistence-mapping.xml
ファイルを使用してオブジェクト関連マッピング情報を格納し、この情報によってデータベースの表および列をデータ・オブジェクトおよびデータ・オブジェクトの属性にマップする方法、さらにデータ・オブジェクトおよびデータ・オブジェクトの属性をRESTレスポンス・ペイロードの属性にマップする方法を識別します。
図6-1は、MAFクライアント・データ・モデルのランタイム・アーキテクチャで、RESTサービスからの部門情報の読取りおよび書込みを行うMAFアプリケーションの特定の実装を参照しています。
図6-1 MAFクライアント・データ・モデルのランタイム・アーキテクチャ
図6-1では、サービス・オブジェクト(DepartmentService
)はCRUDアクションに加え、Department
データ・オブジェクトに対して機能する他のカスタム・アクションも提供しています。
Department
データ・オブジェクトには、RESTサービス・リクエストおよびレスポンス・ペイロードの対応する属性にマップされている部門の属性(名前やIDなど)を対象としたgetterメソッドおよびsetterメソッドがあります。SQLiteデータベースのDEPARTMENTS
表には、同じデータ・オブジェクト属性にマップされる列があります。persistence-mapping.xml
ファイルには、様々な場所(データベース表の列、Javaクラス属性およびRESTペイロード)にある属性間の関係をマップする情報が格納されます。
MAFは、MAFアプリケーションにクライアント・データ・モデルを作成するために使用する数多くのウィザードをJDeveloperに提供します。「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」を参照してください。クライアント・データ・モデルを作成したら、クライアント・データ・モデルのサービス・オブジェクトからデータ・コントロールを生成できます。MAFには他にも、MAFクライアント・データ・モデルが生成するデータ・コントロールおよびデータ・モデルに基づいてMAFアプリケーションにユーザー・インタフェースを生成するウィザード(MAFユーザー・インタフェース・ジェネレータ)があります。「MAFクライアント・データ・モデルに基づくユーザー・インタフェースの作成」を参照してください。
ヒント:
RESTサービスへのアクセスにクライアント・データ・モデルを使用するMAFアプリケーションの作成を順を追って説明するチュートリアルは、Oracle MAFおよびクライアント・データ・モデル(CDM)によるREST/JSONサービスの使用および永続化を参照してください。RESTサービスからクライアント・データ・モデルを作成するには、データ・オブジェクトを検出し、そのオブジェクトの属性を構成し、それらのリレーションシップをマッピングし、CRUDアクションに使用するRESTリソースを特定し、モデルに組み込むアーティファクトを生成します。
このウィザードを使用すると、汎用的なRESTサービスまたはOracle Mobile Cloud Service (MCS)にホストされているRESTサービスに接続できます。接続すると、アプリケーションで使用するデータを識別して取得するというタスクを実行するためのダイアログがMAFに追加で表示されます。これらのタスクは次のとおりです。
MAFアプリケーションでの使用候補となるデータ・オブジェクトを検出します。MAFでは、次のデータ・オブジェクト・リソースからデータ・オブジェクトを検出できます。
サンプルRESTリソースURL
サンプル・リソース・ペイロード
RAMLファイル
MAFアプリケーションでの使用候補となるデータ・オブジェクトを検出すると、使用するデータ・オブジェクトを選択するためのダイアログがMAFに表示されます。また、MAFには新規データ・オブジェクトを作成するためのオプションが用意されています。
追加のダイアログでは、データ・オブジェクト属性を確認して変更できます。実行できるタスクには、デバイスに機密データを保持しないようにすることの他に、属性名、RESTサービス・ペイロードに表示される名前、各属性のJavaタイプおよびデータベース列のタイプを編集することなどがあります。また、キー属性を選択します。選択するキー属性が一意であることが重要です。
データ・オブジェクトの親子関係を指定します。
CRUDアクションに使用するRESTリソースおよび関連するHTTPメソッドを定義し、さらには問合せパラメータやパス・パラメータなどのリソース詳細を指定します。
CRUDアクションに使用するRESTリソースを特定したら、MAFアプリケーションの実行時動作を設定するためのダイアログがMAFに表示されます。ここでは、たとえば、オフライン・トランザクションを有効にしたり、バックグラウンドでのリモートの読取りおよび書込みを有効にしたり、Webサービス・エラーを表示します。
MAFに最後に表示されるダイアログでは、MAFがクライアント・データ・モデルのために生成したアーティファクトをアプリケーションのどこに配置するかを指定します。このダイアログを使用して、MAFが生成したJavaクラスのプロジェクトの場所およびパッケージ名を決定します。
「REST WebサービスからのMAFクライアント・データ・モデル」ウィザードを完了すると、MAFがMAFアプリケーションのクライアント・データ・モデルを生成します。ウィザードを再度起動して、生成されたクライアント・データ・モデルを編集できます。たとえば、RESTサービスでデータ・モデルに含める追加のデータ・オブジェクトを特定します。RESTサービスに接続することなく、追加のデータ・オブジェクトを取得するようにクライアント・データ・モデルを編集する場合は、「永続性マッピングの編集」ウィザードを起動します。この後者のウィザードは、「REST WebサービスからのMAFクライアント・データ・モデル」ウィザードとダイアログを共有しますが、RESTサービスへの接続が必要ありません。
また、クライアント・データ・モデルのサービス・オブジェクトからデータ・コントロールを生成することもできます。データ・コントロールを生成したら、データおよび操作を「データ・コントロール」パネルからAMXページにドラッグ・アンド・ドロップして、MAFアプリケーションのUIを作成できます。MAFユーザー・インタフェース・ジェネレータを使用してMAFアプリケーションのプロトタイプUIを生成する場合は、データ・コントロールの作成が前提条件となります。「MAFクライアント・データ・モデルに基づくユーザー・インタフェースの作成」を参照してください。
注意:
MCSでホストされたRESTサービスに接続する場合は、MCS Data Offline & Synchronization APIを使用しないでください。MAFクライアント・データ・モデルにより、MAFアプリケーションはオフラインで動作して、MAFアプリケーションがオンラインに戻ったときにはデータを同期できるようになります。RESTサービスに接続して、MAFアプリケーションのクライアント・データ・モデルで使用するために取得するデータ・オブジェクト・リソースを特定します。
(汎用的なサービスかMCSにホストされているサービスかに関係なく) RESTサービスに接続するには、「REST WebサービスからのMAFクライアント・データ・モデル」ウィザードを起動します。次を参照してください。
MCSに接続する場合は、「MCS無名アクセス・キーに関する必知事項」を参照してください。
このタスクは、MAFアプリケーションにクライアント・データ・モデルを生成する一連のタスクのうちの1つです。「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」を参照してください。
「新規ギャラリ」で「モバイル・アプリケーション・フレームワーク」の「REST WebサービスからのMAFクライアント・データ・モデル」オプションにアクセスして、RESTサービスに接続する手順を使用してください。取得用のRESTサービスでデータ・オブジェクトを検出して、MAFアプリケーションのクライアント・データ・モデルに使用します。
MAFアプリケーションがMCSで認証される前にMCSにアクセスすると、MAFはMCS無名アクセス・キー値を使用して認可ヘッダーを作成します。これは、たとえば、ユーザーがログインする前にMCSにstartSession
MCS分析イベントを送信する場合に便利です。
使用するアクセス・キー値が匿名キーである必要はありません。MCSモバイル・バックエンドのユーザー・レルムに定義されているMCSユーザーの認可キーを使用できます。これは、たとえば、MCS記憶域収集にアクセスする場合や、匿名ユーザーにアクセスできないその他のリソースにアクセスする場合に行います。
アクセス・キーに接頭辞Basic
を付ける必要はありません。MAFが必要に応じて接頭辞Basic
を追加または削除します。MAFアプリケーションで動的MCS接続をサポートする必要がある場合は、「MCSモバイル・バックエンドID」フィールドおよび「MCS無名アクセス・キー」フィールドにEL式を指定できます。MCSに対して認証されると、MAFがユーザーのログイン資格証明に基づいてすべてのRESTコールに認可ヘッダーを自動的に挿入します。つまり、MAFアプリケーションが認証されたら、MAFは入力フィールドのMCS無名アクセス・キー値を無視します。
MAFアプリケーションがRESTサービスに接続されると、アプリケーションのクライアント・データ・モデルに使用するデータ・オブジェクトを選択できます。
「REST WebサービスからのMAFクライアント・データ・モデル」ウィザードには、MAFアプリケーションで使用するデータ・オブジェクトを検出するために次のオプションが用意されています。
RESTリソースURL: RESTサービスを起動して候補データを返すには、このオプションを使用します。
「RESTリソースURLを使用してデータ・オブジェクトを検出する方法」を参照してください。
サンプルのリソース・ペイロード: サンプルのJSONまたはXMLペイロードを入力するには、このオプションを使用します。このオプションは、ライブRESTサービスの起動によって返された最初のアイテムに属性または子データがない場合に便利です。また、現時点でRESTサービスにアクセスできない場合や、アプリケーションでデータの取得ではなくデータのポストを行う場合にも便利です。
「サンプルのペイロードを使用してデータ・オブジェクトを検出する方法」を参照してください。
RAMLファイル: RAMLファイルには、アプリケーションがアクセスするRESTサービスについて記述します。MCSにAPIのエンド・ポイントを定義すると、MCSがRAMLファイルを自動的に作成します。
「RAMLファイルを使用してデータ・オブジェクトを検出する方法」を参照してください。
ADF BC REST記述ADF BC REST Describe: ADFビジネス・コンポーネントによって公開されたRESTサービスにアクセスするには、このオプションを使用します。
「ADF BC REST記述を使用してデータ・オブジェクトを検出する方法」を参照してください。
このタスクは、MAFアプリケーションにクライアント・データ・モデルを生成する一連のタスクのうちの1つです。「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」を参照してください。
RESTサービスを起動し、レスポンス・ペイロードの構造に基づいて候補データ・オブジェクトを返すには、HTTPメソッド(通常はGET)を含め1つ以上のRESTリソースを指定します。
図6-3は、ウィザードの「データ・オブジェクト・リソース」ダイアログを示しています。ここで、問い合せるRESTリソースを指定します。
図6-3 データ・オブジェクト・リソースのRESTリソースの問合せ
図6-3に示すように、「追加」ボタンおよび「削除」ボタンを使用して、「リソース」リストに入力するRESTリソースのリストを管理します。RESTリソースを起動するために特定のHTTPヘッダーが必要である場合には、「ヘッダーの設定」ボタンを使用してダイアログを起動し、ここでヘッダーとしてキーと値のペアを入力します。たとえば、RESTサービスから返されるペイロードがXMLではなくJSON形式になるようにする場合は、「ヘッダーの設定」をクリックし、表示されたダイアログにキーとしてContent-Type
、値としてapplication/json
を入力します。
「フラット化されたネスト・オブジェクト」チェック・ボックスの使用は、「フラット化されたネスト・データ・オブジェクト・オプションに関する必知事項」を参照してください。
「次へ」をクリックして、RESTサービスを問い合せて、レスポンス・ペイロードの構造に基づいて候補データ・オブジェクトを返す前に、次の点に注意してください。
MAFは、RESTサービスから返されたデータのコレクションにある最初のアイテムを調べます。たとえば、従業員のコレクションにある最初の従業員が営業担当者でなく、このため関連付けられたコミッション属性がない場合、従業員の候補データ・オブジェクトにはコミッション属性が含まれません。同様に、RESTサービスから返されたレスポンス・ペイロードが部門および従業員のマスター/ディテール構造である場合は、返された最初の部門に従業員が含まれていることを確認してください。そうでない場合、従業員は子データ・オブジェクトとして識別されません。ウィザードの後半のページでデータ・オブジェクトおよび属性を追加できます。ただし、可能な場合にはこの作業を回避することが最善です。
パス・パラメータを中カッコで囲んで入力した場合、「次へ」をクリックすると、パス・パラメータのサンプル値を入力するためのダイアログ(「URIパラメータ値の入力」)が表示されます。図6-3では、employeesList
リソースのパス・パラメータとして現在の部門IDを参照する{id}
を指定したため、そのダイアログが表示されます。データを返すサンプル値を入力するようにしてください。たとえば、ID値が20
の部門に従業員が含まれていない場合、従業員データ・オブジェクトが識別されることはありません。
このページで指定するリソースは、アプリケーションに作成して使用する候補データ・オブジェクトを識別するだけです。CRUDアクションに使用する正確なリソースを指定するには、後のページを使用します。ここで入力する値は、ウィザードの後半にある「CRUDリソース」ページで各データ・オブジェクトの「すべてのリソースの検索」入力フィールドのデフォルト値として使用されるだけです。様々なリソースを使用してデータ・オブジェクトおよび「すべてのリソースの検索」を検出するという状況では、「すべてのリソースの検索」値は使用可能な属性のサブセットを返します。たとえば、従業員の「すべてのリソースの検索」の場合、リスト・ビューに属性(employeeId、firstNameおよびlastName)が返されます。エンド・ユーザーがリスト・ビューの従業員をクリックして詳細画面に移動すると、MAFアプリケーションは選択された従業員のすべての属性を返す正規従業員RESTリソースを起動して、追加の従業員属性をロードします。これにより、データ・セットの規模が大きく各データ・オブジェクトに多数の属性が含まれているときのパフォーマンスが最適化されます。このようなシナリオでは、「データ・オブジェクト・リソース」ウィザード・ページですべての従業員属性を識別する正規RESTリソースを指定します。
候補データ・オブジェクトおよび属性を作成するためのサンプルのペイロードを指定します。
このオプションは、次の場合に便利です。
RESTサービスの起動によって返されたデータ・コレクションの最初のアイテムで一部の属性や子データが欠けている場合
バックエンド開発チームが作業を続けているため、RESTリソースがまだ使用できない場合
アプリケーションでデータの取得は行わず、新規データの作成(ポスト)のみを行う場合。つまり、解析するレスポンス・ペイロードを返すGETリソースがありません。この場合、新規データを作成するときにRESTコールで送信するすべての属性が含まれているサンプルのペイロードを指定します。新規データを作成するためのRESTコールの完全なシグネチャは、ウィザードの後で指定できます。
サンプルのペイロードを指定すると、入力したRESTリソースは起動されません。そのかわり、データ・オブジェクトのデフォルト名、およびウィザードの後半の「CRUDリソース」ページにある「すべてのリソースの検索」値のデフォルト値が導出されます。
図6-4は、「サンプル戻りペイロード」ダイアログを示しています。ここで、サンプルのペイロードを入力します。このダイアログを起動するには、「ペイロード」列内をクリックします。
図6-4 サンプルのペイロードを使用した候補データ・オブジェクトの検出
「OK」をクリックして「データ・オブジェクト・リソース」ダイアログに戻り、「次へ」をクリックしてウィザードの次ページに進みます。
RESTful API Modeling Language (RAML)ファイルを指定して、そのRAMLファイルでの説明に基づいて候補データ・オブジェクトおよび属性を作成します。
MAFでは、RAMLファイルの内容に基づいてデータ・オブジェクト、親子関係およびCRUDリソースをお薦めしています。つまり、ウィザードの後続のページでは、RAMLファイルの内容に基づいてデフォルト値が表示されます。
図6-5 RAMLファイルを使用した候補データ・オブジェクトの検出
RESTリソースを起動するために特定のHTTPヘッダーが必要である場合には、「ヘッダーの設定」ボタンを使用してダイアログを起動し、ここでヘッダーとしてキーと値のペアを入力します。たとえば、RESTサービスから返されるペイロードがXMLではなくJSON形式になるようにする場合は、「ヘッダーの設定」をクリックし、表示されたダイアログにキーとしてContent-Type
、値としてapplication/json
を入力します。
「フラット化されたネスト・オブジェクト」チェック・ボックスの使用は、「フラット化されたネスト・データ・オブジェクト・オプションに関する必知事項」を参照してください。
JSONペイロードが次のようになっているときは、フラット化されたネスト・データ・オブジェクト・チェック・ボックスを選択します。ここでは、独自のデータ・オブジェクトとしてデフォルトでmanagerを作成します。フラット化されたネスト・データ・オブジェクト・チェック・ボックスを選択すると、departmentデータ・オブジェクト内にmanagerの2つの属性(nameおよびemail)が含まれます。
[ { "departmentId" : 10, "departmentName" :"Administration", "locationId" : 1700, "manager": {"name" :"Steven" "email" : "steven@oracle.com" } } , ... ]
URIを指定して、ADFビジネス・コンポーネントによって公開されたRESTサービスを起動します。
Oracle ADFビジネス・コンポーネントでは、一連のビュー・オブジェクトを公開するアプリケーション・モジュールの上で機能する、REST APIの公開がサポートされています。/describe
リソースは、このタイプのREST APIを作成すると自動的に含まれます。『Oracle Application Development FrameworkによるFusion Webアプリケーションの開発』のアプリケーション・モジュールを使用したRESTful Webサービスの作成に関する項を参照してください。
/describe
リソースを含むURIへのMAFアプリケーション・リクエストは、RESTリソースを記述するメタデータを返します。このウィザードでは、そのメタデータを調べ、この検査に基づいてウィザードの後半でデータ・オブジェクトおよびRESTリソース・ページを移入します。
図6-6 ADFビジネス・コンポーネント記述の使用による候補データ・オブジェクトの検出
MAFが識別する候補データ・オブジェクトのリストから、MAFアプリケーションのクライアント・データ・モデルで使用するデータ・オブジェクトを選択します。また、オフラインのときにMAFアプリケーションがデータ・オブジェクトのインスタンスにアクセスできるように、永続化するデータ・オブジェクトを選択します。
「REST WebサービスからのMAFクライアント・データ・モデル」ウィザードのデータ・オブジェクトの選択ページおよび「データ・オブジェクト属性」ページでは、次のタスクを実行できます。
データ・オブジェクトを選択および永続化します。
詳細は、「データ・オブジェクトを選択および永続化する方法」を参照してください。
新しいデータ・オブジェクトを作成します。
詳細は、「新しいデータ・オブジェクトを作成する方法」を参照してください。
データ・オブジェクト属性を変更します。
詳細は、「データ・オブジェクト属性を変更する方法」を参照してください。
このタスクは、MAFアプリケーションにクライアント・データ・モデルを生成する一連のタスクのうちの1つです。詳細は、「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」を参照してください。
含めるデータ・オブジェクトを選択し、不要なデータ・オブジェクトのチェック・ボックスの選択を解除します。MAFがクライアント・データ・モデルを作成するときに有効なJavaクラス名を生成するように、「クラス名」列のデータ・オブジェクトの値を変更します。
MAFアプリケーションがオフライン・モードでデータにアクセスできるように、データを永続化するデータ・オブジェクトごとに「永続」チェック・ボックスを選択します。これにより、MAFはデータ・オブジェクトのSQLiteデータベース表を作成し、ここにはデータを取得するために後で指定するRESTリソースに基づいて実行時にデータが移入されます。
注意:
データが揮発性であるなどの理由で、オフライン・アクセス用にデータ・オブジェクトを永続化しないようにする場合でも、引き続きオフライン・トランザクションにMAFクライアント・データ・モデルを使用できます。トランザクションは別個のDATA_SYNCH_ACTIONS
表に格納され、同期はデータ・オブジェクト表から独立しています。図6-7に示すように、データ・オブジェクトの選択ページに表示される候補データ・オブジェクトのリストの正確性は、候補データ・オブジェクトの検出に使用する方法によって異なります。たとえば、RAMLファイルに基づいて提案されるデータ・オブジェクトのリストは通常、次のようなサンプルのRESTリソースから返されるリストよりも正確です。
[ { "departmentId": 10, "departmentName": "Administration", "locationId": 1700, "_relationships": [ { "_link": { "href": "http://localhost:7101/HRRest/persistence/v1.0/Model-1/entity/Department/10/employees1", "rel": "employees1" } }, { "_link": { "href": "http://localhost:7101/HRRest/persistence/v1.0/Model-1/entity/Department/10/employeesList1", "rel": "employeesList1" } } ], "employeesList1": [ { "_link": { "href": "http://localhost:7101/HRRest/persistence/v1.0/Model-1/entity/Employee/200", "method": "GET", "rel": "self" } } ] },
図6-7に示すように、前述のサンプルのRESTリソースはペイロードに含まれているリンク情報の候補データ・オブジェクトを示しています。データ・モデルにリンクに関する情報を含める可能性はほとんどないため、これらの候補データ・オブジェクトでは「選択」列のチェック・ボックスの選択を解除します。また、有効なJavaクラス名が表示されるように、「クラス名」の値を編集します。
図6-7 RESTリソースから取得された候補データ・オブジェクト
モバイル・デバイスにのみ存在し、かつMAFアプリケーションが接続するREST Webサービスによってデータが移入されないデータ・オブジェクトを作成します。
データ・オブジェクトの選択ページの「追加」ボタンをクリックして、データ・モデルに新しいデータ・オブジェクトを追加します。デフォルトで生成される名前(NewDataObject
)を一意の有効なJavaクラスに編集します。
MAFは、追加するデータ・オブジェクトごとにSQLiteデータベースにデータベース表を作成します。これらのデータベース表にデータを移入するには、ウィザードを完了するとMAFがデータ・オブジェクト用に生成するサービス・オブジェクトからCRUD操作を使用します。
MAFアプリケーションのクライアント・データ・モデルに含めるために選択したデータ・オブジェクトの属性を選択または変更します。
必要に応じて各属性のキー、必須、属性名、ペイロード内の名前、Javaタイプおよびデータベース列のタイプを設定または変更できます。機密データを保持する属性の場合、永続化しないようにすることもできます。これにより、アプリケーションがオフライン・モードで実行されるときには、属性値がnullになります。
キー属性が一意であることが重要です。永続性ランタイムは、選択したキー属性に基づいてデータ・オブジェクト・キャッシュを使用します。同じキーのデータ・オブジェクト・インスタンスが複数ある場合、それらはすべて同じデータベース表の行に書き込まれます。このため、1つのインスタンス(最後に処理されたインスタンスの属性を保持)のみがMAFアプリケーションのUIに表示されます。つまり、MAFではそれが同じインスタンスとみなされるため、すべてのインスタンスが1つにマージされます。子データ・オブジェクトの場合、親への参照属性がキーの一部になる場合があります。ペイロードにこのような参照属性が含まれていない場合は、それらをウィザードの「親子アクセサ」ページで作成できます。次に、このページ(「データ・オブジェクト属性」)に戻り、キー属性として親移入属性を選択します。
サンプルのリソースを使用して候補データ・オブジェクトを特定するときは、一般に属性のJavaタイプがjava.lang.String
と表示されるため、通常それらを変更する必要があります。属性によっては、ペイロード値を二重引用符で囲まないと、java.math.BigDecimal
と表示されることがあります。クラス・ピッカーを使用してJavaタイプを変更でき、選択したJavaタイプに基づいてデータベース列のタイプが自動的に更新されます。通常のタイプ変更では、数値属性をString
またはBigDecimal
からLong
またはInteger
に変更します。日付属性をjava.util.Date
に設定し、日付書式をJSONペイロードで使用するように指定する必要があります(たとえば、yyyy-MM-dd'T'HH:mm:ssZ
)。ウィザードの後でアクセスする「CRUDリソース」ページの「ペイロード日付書式」フィールドに日付書式を指定します。
RAMLファイルを使用してデータ・オブジェクトを検出した場合は、通常デフォルトの属性タイプが適切であり、変更する必要はありません。
注意:
「データ・オブジェクト」ドロップダウン・リストから各データ・オブジェクトを選択して、オブジェクトの属性に必要な変更を加えます。図6-8 データ・オブジェクト属性の変更
マスター/ディテール画面をレンダリングするためのMAFアプリケーションのUIが必要になるデータ・オブジェクトの親子関係を指定します。
図6-9は、このような関係を定義している「親子アクセサ」ページを示しています。部門と部門の従業員との間に存在するような関係を設定するときは、このダイアログを使用します。
図6-9 「親子アクセサ」ページ
同じペイロードで親および子データ・オブジェクトを返すRESTリソースを指定し、両方のデータ・オブジェクトを選択したときは、「アクセサ」ドロップダウン・リストに親子関係が移入されます。これは、RAMLファイルを使用してRESTリソースにアクセスした場合にも発生します。ただし、別個のRESTコールで子データ・オブジェクトを取得し、その子オブジェクトが親データ・オブジェクトのペイロードに含まれていない場合、「アクセサ」ドロップダウン・リストは空になります。このシナリオが発生した場合には、「データ・オブジェクトの親子関係を指定する方法」の説明に従って、「親子アクセサ」ページに親子関係を手動で定義します。
MAFが「親子アクセサ」ページに自動的に値を入力したのか、親子関係を手動で定義したのかに関係なく、親子関係ごとに属性マッピングを指定する必要があります。これを行うと、アプリケーションがオフライン・モードで実行されているときにMAFが1対多関係または1対1関係を復元します。データベースの観点から、基になる親と子のデータ・オブジェクト表間に外部キー関係を設定します。図6-9は、departmentID
およびdepartmentDepartmentId
を指定した例であり、「追加」ボタンを使用して「親子アクセサ」ページの下部にあるリストに両方の属性を追加しています。
子データ・オブジェクトにレスポンス・ペイロードにある外部キー属性が含まれている場合は、「親属性の選択」リストで親キー属性を選択し、「子属性の選択」リストで子データ・オブジェクト外部キー属性を選択し、「追加」をクリックして属性マッピングを追加します。ただし、この外部キー属性は、ペイロードに含まれなかったために子データ・オブジェクトに存在しない場合があります。これは通常、子データ・オブジェクトが親データ・オブジェクト・ペイロード内に入れ子になるために発生します。このシナリオでは、値がペイロードの階層的性質によって推定されるため、外部キー属性を含める必要はありません。MAFでは、オフライン・モード用に引き続き属性マッピングが必要であるため、一致する子属性を選択しなくても、親属性を選択し、「追加」ボタンをクリックできます。前述の図6-9は、このようなユースケースを示しており、「親属性の選択」リストでdepartmentID
を選択し、「追加」ボタンをクリックしています。これで、departmentDepartmentId
属性が従業員データ・オブジェクト(および基礎となるデータベース表の列)に追加されます。この属性は部門の従業員詳細を問い合せるときにはRESTペイロードに含まれませんが、実行時のコードが従業員詳細の取得対象である部門インスタンスに基づいてこの属性(および基礎となる列値)を移入します。
ヒント:
この親移入属性の名前は、選択した親属性名が接尾辞として付いたデータ・オブジェクトの名前です。これらの属性名をクリーンな属性名になるように変更できます。前述の例では、「戻る」ボタンをクリックしてこれを行い、部門データ・オブジェクトを選択し、departmentDepartmentId
属性の名前をdepartmentId
に変更しています。このタスクは、MAFアプリケーションにクライアント・データ・モデルを生成する一連のタスクのうちの1つです。詳細は、「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」を参照してください。
「REST WebサービスからのMAFクライアント・データ・モデル」ウィザードから「親子アクセサ」ページにアクセスします。
データ・オブジェクトの親子関係を指定するには:
「親子アクセサ」ページで、図6-10に示したプラス・アイコンをクリックし、表示されたダイアログのフィールドに値を入力します。
たとえば、図6-10に示したダイアログでは、ある部門のすべての従業員を取得する親子アクセサを定義しています。
図6-10 「親子アクセサ」ページ
注意:
MAFは、「データ・オブジェクト・リソース」ページで候補データ・オブジェクトの特定に使用したリソースを「子アクセサ・リソース」フィールドに移入します。この例では、これは/entity/Department/{id}/employeesList1
です。「子アクセサ属性」フィールドの値は、employees
に設定されています。ウィザードが完了すると、getEmployees
メソッドがdepartment
データ・オブジェクトに生成され、従業員コレクションが後で作成するデータ・コントロールの部門ノードの下に表示されます。 CRUDアクションを定義すると、MAFアプリケーションのエンド・ユーザーはMAFアプリケーションが接続先のRESTサービスから取得したデータ・オブジェクトを編集できるようになります。
このタスクは、MAFアプリケーションにクライアント・データ・モデルを生成する一連のタスクのうちの1つです。詳細は、「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」を参照してください。
MAFには「CRUDリソース」ページが用意されており、ここではMAFアプリケーションのクライアント・データ・モデルで使用するために取得したデータ・オブジェクトに対してどのアクションを実行するかを指定します。図6-11は、Departmentsデータ・オブジェクトに対して作成、更新、削除など数多くの操作を指定しているページを示しています。
図6-11 クライアント・データ・モデルのCRUD RESTリソースの定義
MAFは、以前に入力した値および候補データ・オブジェクトを検出するために使用した方法に応じて、「CRUDリソース」ページに値を移入します。たとえば、RAMLファイルを使用した場合、次のように、MAFはRESTのベスト・プラクティス規則に基づいてページに値を移入します。
POSTリソースがリソースを作成します。
PUTリソースまたはPATCHリソースがリソースを更新またはマージします。
DELETEリソースがリソースを削除します。
サンプルのリソースURLを使用してデータ・オブジェクトを検出した場合、「すべてのリソースの検索」はデフォルトでサンプルのリソースに設定されます。通常はこのデフォルトが適切ですが、RESTサービスがベスト・プラクティスに従っていない場合には、リソースやHTTPメソッドの値の変更が必要になることがあります。
「データ・オブジェクト」ドロップダウン・リストから各データ・オブジェクトを選択し、次のように入力フィールドに値を入力します。
リソースのクイック検索: 大規模なデータ・セットに有用です。大規模なデータ・セットの場合、アプリケーションがメモリーを使い果たしてしまう可能性があるため、通常はすべてのインスタンスを返す「すべてのリソースの検索」を実行しないようにします。そのような状況では、検索基準に一致するインスタンスのみを返すクイック検索機能をユーザー・インタフェースに定義します。小規模なデータ・セットの場合は、「すべてのリソースの検索」を使用して一度にすべてのインスタンスを返します。オンデバイスSQLiteデータベースに対して直接クイック検索フィルタを実行します。Webサービスが起動されないため、パフォーマンスは高速です。
正規リソース: データ・オブジェクトに数多くの属性があり、リストから特定のデータ・オブジェクト・インスタンスを選択したときにのみそのすべての属性を取り込むようにする場合は、正規リソースの値を指定します。
正規トリガー属性: 「正規トリガー属性」を指定した場合、MAFはgetterメソッドで属性の値を取得するときに正規RESTリソースを自動的に起動するコードをデータ・オブジェクト・クラスに生成します。
たとえば、Department
データ・オブジェクトの「すべてのリソースの検索」を選択するとリスト・ページに表示される部門IDと名前のリストが返され、department
を選択すると詳細ページに移動してすべての部門属性が表示されるようにする場合、「正規トリガー属性」をlocationId
に設定できます。詳細ページに移動すると、getLocationId
メソッドが起動します。MAFは、正規RESTリソースを自動的に起動するためのコードをgetLocationId
メソッド内に生成済です。
CRUDリソース(「リソースの作成」や「リソースの更新」など): このページで少なくとも1つのRESTリソースが指定されたデータ・オブジェクトごとに生成されるサービス・オブジェクトには、save[DataObjectName]
メソッドが含まれています。save[DataObjectName]
メソッドを、たとえば「データ・コントロール」パネルからドラッグ・アンド・ドロップすることで起動すると、MAFはデータ・オブジェクト状態に基づいて、コールするリソース(Create
、Update
またはMerge
)を決定します。MAFは、次のように、その決定を下します。
データ・オブジェクト状態がnewの場合、MAFはCreate
リソースをコールします。Create
リソースが指定されていない場合、MAFはMerge
リソースをコールします。いずれのリソースも指定されていない場合、MAFアプリケーションはRESTコールを行いません。
データ・オブジェクト状態がnewでない場合、MAFはMerge
リソースをコールします。Merge
リソースが指定されていない場合、MAFはUpdate
リソースをコールします。いずれのリソースも指定されていない場合、MAFアプリケーションはRESTコールを行いません。データ・コントロールCreate
操作で新しいデータ・オブジェクトを作成すると、データ・オブジェクト状態はNew
に自動的に設定されます。oracle.maf.api.cdm.persistence.model.Entity
のサブクラスを使用してプログラムにより新しいデータ・オブジェクト・インスタンスを作成する場合は、setIsNewEntity(true)
をコールします。
ソート順序: 属性名のカンマ区切りリストを定義します。属性名に接頭辞としてdesc
を付けると、属性を降順にすることができます。ここに指定したソート順序がユーザー・インタフェースでのデフォルトの順序になることに注意してください。永続性実行時コードでは、ソート順序を実行時に変更するためのUIコントロールを簡単に追加できます。
ペイロード日付書式: ペイロード内の日付属性文字列値をjava.util.Date
インスタンスに変換するためのJava日付パターンを指定します。
MAFアプリケーションに定義した様々なRESTリソースの問合せパラメータおよびパス・パラメータとそれらの値などリソース詳細を指定します。
MAFにはCRUDリソース詳細ページが用意されており、ここでリソース詳細を指定します。このウィザード・ページでは、フィールドにデフォルト値を移入します。これらのデフォルトが特定のアプリケーションで意味のあるものになっているかどうかをチェックするようにすると効果的です。図6-12は、このページを示しています。「リソース」ドロップダウンから、レビューまたはカスタマイズする追加のリソースを選択します。また、「リソース」ドロップダウン・メニューの横にあるプラス・アイコンをクリックして、カスタム・リソースを追加することもできます。詳細は、「カスタム・リソースの定義」を参照してください。
図6-12 CRUD RESTリソース詳細の指定
このページのオプションを理解するには、GET (読取り)リソースとGET以外(書込み)リソースを区別します。フィールドの中には、ある種のリソースにのみ適用されるものや、リソースのタイプに応じて意味が異なるものがあります。
「リソースの詳細」ページでは、次のタスクを実行できます。
GET (読取り)リソース詳細の指定
詳細は、「GET (読取り)リソース詳細を指定する方法」を参照してください。
GET以外(書込み)リソース詳細の指定
詳細は、「GET以外(書込み)リソース詳細を指定する方法」を参照してください。
カスタム・リソースの追加
詳細は、「カスタム・リソースを追加する方法」を参照してください。
問合せパラメータおよびパス・パラメータの指定
詳細は、「問合せパラメータおよびパス・パラメータを指定する方法」を参照してください。
HTTPヘッダー・パラメータの追加
詳細は、「HTTPヘッダー・パラメータを追加する方法」を参照してください。
このタスクは、MAFアプリケーションにクライアント・データ・モデルを生成する一連のタスクのうちの1つです。詳細は、「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」を参照してください。
GETリソース詳細を指定するには、次のメニュー・オプションを構成します。
このRESTリソースを実行する前に、SQLite DB内のローカル行を削除しますか。
「ローカル行の削除」チェック・ボックスを選択し、GETリソースを「すべてのリソースの検索」として使用している場合、RESTコールの実行後かつRESTレスポンスの処理前に、対応するデータ・オブジェクト用に作成された表のすべての行が削除されます。これは、GETレスポンス・ペイロードに含まれなくなる廃止された行が単に以前にダウンロードされたという理由でアプリケーションに表示されたままにならないようにする場合に便利です。「ローカル行の削除」チェック・ボックスを選択し、子データ・オブジェクトを取得するために「親子アクセサ」ダイアログでGETリソースを定義していた場合、RESTコールの実行直前にそのすべての子行が削除されます。
ペイロード・リスト要素名
「ペイロード・リスト要素名」は、レスポンス・ペイロード内のリソース・データ・オブジェクトを表すJSONオブジェクトの実際の配列を探す方法をMAFに指示します。たとえば、レスポンス・ペイロードが次のようになっているとします。
{"departmentList":[{"departmentId":10, "departmentName": "Accounting", "locationId": 1400, "managerId": 103}, {"departmentId":20, "departmentName": "Marketing", "locationId": 1400, "managerId": 102} ]}
この場合、「ペイロード・リスト要素名」をdepartmentList
に設定する必要があります。departmentList
は、トップレベルのJSONオブジェクト属性である必要はないことに注意してください。MAFは、この属性名のオブジェクトを見つけるまで、レスポンス・オブジェクト・ツリーを再帰的に移動します。レスポンス・ペイロードが直接必要な配列で始まる場合には、このフィールドを空白のままにすることができます。
ペイロード行要素名
「ペイロード行要素名」フィールドを使用すると、特定の配列インスタンス内で適切なJSONオブジェクトを探す方法をMAFに指示できます。たとえば、レスポンス・ペイロードが次のようになっているとします。
{"departmentList":["department":{"departmentId":10, "departmentName": "Accounting", "locationId": 1400, "managerId": 103}}, "department":{"departmentId":20, "departmentName": "Marketing", "locationId": 1400, "managerId": 102}) ]}
「ペイロード行要素名」をdepartment
に設定してください。MAFが解析する実際のJSONオブジェクトに到達するために深く入れ子になったオブジェクト・ツリーを移動する必要がある場合、このフィールドではドット表記を使用します。たとえば、departmentTopObject.department
とします。
GET以外リソース詳細を指定するには、次のメニュー・オプションを構成します。
シリアライズ・データ・オブジェクトをペイロードとして送信
「シリアライズ・データ・オブジェクトをペイロードとして送信」チェック・ボックスは通常、PUT、POST、PATCHのいずれかのリクエストを行うときに選択します。サーバー側での削除の実装方法によっては、DELETEリクエストでも選択する場合があります。このチェック・ボックスを選択すると、「データ・オブジェクト属性を変更する方法」で説明しているように、MAFは「データ・オブジェクト属性」ページで「ペイロード内の名前」プロパティが設定されているデータ・オブジェクト属性ごとにキーと値のペアでリクエスト・ペイロードを作成します。
{"departmentId":10, "departmentName": "Accounting", "locationId": 1400, "managerId": 103}
配列として送信
「配列として送信」チェック・ボックスは、次の例に示すように、キーと値のペアを保持するJSONオブジェクトをカッコで囲む場合に選択します。「シリアライズ・データ・オブジェクトをペイロードとして送信」チェック・ボックスを選択するまで、このチェック・ボックスは無効になっています。
[{"departmentId":10, "departmentName": "Accounting", "locationId": 1400, "managerId": 103}]
ペイロード行要素名
キーと値のペアを持つ実際のJSONオブジェクトを別のオブジェクト内に入れ子にするようにMAFに指示するために使用します。たとえば、リクエスト・ペイロードが次のようになっているとします。
{"department":{"departmentId":10, "departmentName": "Accounting", "locationId": 1400, "managerId": 103}}
この場合、「ペイロード行要素名」をdepartment
に設定する必要があります。
GETリソースとは異なるペイロード属性名がリクエスト・ペイロードで必要になる場合は、これらの書込み固有のペイロード属性名をここに指定することはできません。RestJSONPersistenceManager
をサブクラス化し、getPayloadKeyValuePairs
メソッドをオーバーライドする独自のリモート永続性マネージャを作成する必要があります。カスタムのリモート永続性マネージャは、ウィザードの次ページで登録することも、後で登録することもできます。「MAFアプリケーションでのクライアント・データ・モデルの編集」を参照してください。
除外する属性
リクエスト・ペイロードとして使用されるJSONオブジェクトにシリアライズしない属性名のカンマ区切りリストを指定します。たとえば、departmentId
を指定した場合、次のペイロードはそれをJSONオブジェクトへのシリアライズから除外します。
{"departmentName": "Accounting", "locationId": 1400, "managerId": 103}
「データ・オブジェクト属性」ページで「ペイロード内の名前」フィールドの値を無効にすることで同じことを実現できますが、その場合GETリクエストでdepartmentId
も無視されることになり、これは通常望ましいことではありません。このフィールドでは、データ・オブジェクト属性のペイロード名ではなく、これらの属性の名前を使用する必要があります。
カスタム・リソースは、「CRUDリソース」ページで指定した検索、作成、更新、削除のいずれのアクションにもマップしない独自のRESTコールです。
「リソースの詳細」ページでカスタム・リソースを追加できます。図6-13に示すように、緑のプラス・アイコンをクリックして「新規カスタム・リソース」ダイアログを起動し、ここでカスタム・リソースを定義できます。
図6-13 カスタム・リソースの追加
MAFがデータ・オブジェクト用に生成するサービス・クラスでは、「名前」フィールドに指定した名前でメソッドが追加されます。このメソッドをコールすると、RESTリソースが起動します。MAFクライアント・データ・モデルのデータ同期メカニズムにはカスタム・メソッドも含まれているため、オフライン・モードでカスタム・メソッドをコールした場合、それは保留中の同期アクションとして登録され、RESTコールはMAFアプリケーションが再度オンラインになったときに実行されます。
「CRUDリソース」ページまたは「親子アクセサ」ダイアログのRESTリソースに指定したカッコで囲まれたパス・パラメータが、デフォルトで「パラメータ」リストに表示されます。
追加の問合せパラメータを追加するには、「追加」ボタンを使用します。
図6-14 問合せパラメータおよびパス・パラメータの指定
MAFアプリケーションは、RESTリソースを実行するときに、選択された値プロバイダに基づいてリソース問合せパラメータ値およびパス・パラメータ値を自動的に移入します。
DataObjectAttribute: データ・オブジェクト属性の値をパラメータに移入します。この値プロバイダを使用するときは、「データ・オブジェクト属性」ドロップダウン・リストから値を選択する必要があります。属性がドロップダウン・リストに表示されるデータ・オブジェクトは、リソースの使用状況によって決まります。たとえば、上のリソースは部門内の従業員を返すため、従業員リストのコンテキストを設定するための属性を部門データ・オブジェクトから選択するものと想定しています。
SerializedDataObject: 保持する必要があるデータをリクエスト本文ではなく問合せパラメータで送信する場合は、この設定を選択します。この値プロバイダの場合、他の列は空のままにしてください。
LiteralValue: 「値」列に指定されているリテラル値をパラメータに移入します。
ELExpression: EL式の評価によって取得された値をパラメータに移入します。「値」列にEL式を指定します。EL式は任意のスコープ(applicationScope、pageFlowScope、viewScope、deviceScope、preferenceScope)で使用できますが、式をRESTサービス・コールの実行コンテキストで有効にすることは自己責任です。オフライン・モードでトランザクションを作成した場合、RESTサービスは後で起動され、EL式コンテキストはデータ同期をトリガーするタスク・フローおよびページによって決定されることに注意してください。
SearchValue: ユーザー・インタフェースで入力されたクイック検索値の値をパラメータに移入します。通常は、「CRUD RESTリソースの定義」で説明するように、「CRUDリソース」ページに定義している「リソースのクイック検索」でのみこの値プロバイダを使用します。
このウィザードの完了時に生成するMAFアプリケーションのランタイム・オプションを設定します。
このタスクは、MAFアプリケーションにクライアント・データ・モデルを生成する一連のタスクのうちの1つです。詳細は、「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」を参照してください。
図6-16は、生成されたMAFアプリケーションの実行時の動作を決定する「ランタイム・オプション」ページを示しています。
図6-16 MAFアプリケーションのランタイム・オプションの設定
図6-16に表示されるフィールドは、次のように構成します。
バックグラウンドでのリモート読取り: 選択した場合、データ・オブジェクトに対して指定されているGETリクエスト(通常、Find All、Find、Canonicalのいずれかのリソース)がバックグラウンド・スレッドで実行されます。MAFアプリケーションのパフォーマンスに対するエンド・ユーザーの認識が高まるため、このチェック・ボックスを選択することをお薦めします。MAFは、まずSQLiteデータベースに問い合せて、これらの結果を画面にただちに表示します(「データ・オブジェクトを選択および永続化する方法」で説明しているように、データ・オブジェクトを永続化したものとします)。RESTコールは、ユーザー・インタフェースをブロックすることなくバックグラウンドで行われます。MAFアプリケーションは、RESTレスポンスを受信すると、ユーザー・インタフェースを自動的に更新します。
バックグラウンドでのリモート書込み: 選択した場合、このデータ・オブジェクトに対して指定されているGET以外リクエスト(通常、Create、Update、Mergeのいずれかのリソース)がバックグラウンド・スレッドで実行されます。MAFアプリケーションのパフォーマンスに対するエンド・ユーザーの認識が高まるため、通常このチェック・ボックスは選択したままにします。RESTコールをたとえば「保存」ボタンによってトリガーした後も、エンド・ユーザーはアプリケーションを引き続き使用できます。RESTコールが存続するかぎり、ユーザー・インタフェースはブロックされません。また、RESTレスポンスに含まれている属性の中にサーバー側で値が更新されたものがある場合にも、MAFアプリケーションはユーザー・インタフェースを自動的にリフレッシュします。
自動問合せ: 選択した場合、MAFはデータ・オブジェクトのサービス・オブジェクト・クラスが初期化されるときに、「すべてのリソースの検索」によって指定されたすべての行やコール・リソースをオンデバイス・データベースに自動的に問い合せます。これは、サービス・オブジェクト・クラス用に作成したBeanデータ・コントロールを使用してAMXページを構築するときに便利です。Beanデータ・コントロールは、たとえばリスト・ビューまたはフォーム・ビューを作成するために、AMXページにドラッグ・アンド・ドロップできるコレクション要素を公開します。自動問合せを選択している場合、このコレクション要素はすべてのデータ・オブジェクトを返します。初期状態では、「すべてのリソースの検索」の実行完了後に、SQLiteデータベースに存在するものを返し、リモート・コレクションでリフレッシュされます。このチェック・ボックスの選択を解除した場合は、AMXページに移動する前に、タスク・フローでfinderメソッドを実行する必要があります。そうしないと、AMXページにはデータが表示されません。
「主キーの生成」: 選択した場合、新しいデータ・オブジェクトがSQLiteデータベースに挿入されたものの、主キー属性値がまだnullのときに、MAFアプリケーションは主キーを自動的に生成します。この機能は、主キー属性が数値属性であるときにのみ動作します。MAFは、現在の最大値をSQLiteデータベースに問い合せて、この値を1だけ増加します。
オフライン・トランザクションの有効化: 選択した場合、MAFアプリケーションがオフラインのときに、書込みRESTコールを起動できます。MAFは、トランザクション(作成、更新、削除またはカスタムのアクション)を保留中のデータ同期アクションとして登録します。MAFアプリケーションがオンラインになると、MAFはこれらのアクションを同期し、関連付けられたRESTコールを実行します。MAFアプリケーションがオンラインで、RESTリソースを起動するときにサーバーが使用できないか、またはサーバーが何かエラーをスローしたためにRESTコールが失敗した場合、トランザクションも保留中の同期アクションとして登録されることに注意してください。MAFは、次回MAFアプリケーションがRESTコールを行うときに実行を再試行します。このチェック・ボックスの選択を解除した場合は、MAFアプリケーションがオフラインか、またはRESTコールが失敗したときに、エラー・メッセージがエンド・ユーザーに表示されます。
Webサービス・エラーの表示: 選択した場合、RESTコールが失敗すると、エラー・メッセージのポップアップがユーザー・インタフェースに表示されます。これは、エラーを参照できるため、開発時に有用な設定です。本番にMAFアプリケーションを公開するときは、このチェック・ボックスの選択を解除することをお薦めします。通常は、RESTコールの失敗に関する技術的詳細をエンド・ユーザーに表示しないようにします。
リモート永続性マネージャ: 独自のリモート永続性マネージャを登録します。これは、MAFリモート永続性マネージャのデフォルトの動作を拡張する場合に便利です。
「ローカル永続性マネージャ」: 独自のローカル永続性マネージャを登録します。これは、MAFローカル永続性マネージャのデフォルトの動作を拡張する場合に便利です。
MAFがクライアント・データ・モデル用に生成するデータ・オブジェクトのプロジェクト名およびパッケージ名を指定します。
このタスクは、MAFアプリケーションにクライアント・データ・モデルを生成する一連のタスクのうちの1つです。詳細は、「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」を参照してください。
図6-17は、クライアント・データ・モデル内のオブジェクトの場所およびパッケージ名を決定する「ジェネレータ設定」ページを示しています。
このページのフィールドは、次のように構成します。
プロジェクトへのクラスの追加: Javaクラスを生成するアプリケーション内のプロジェクトを選択します。MAFでは、デフォルトのApplicationControllerプロジェクトの使用をお薦めしています。これにより、アプリケーション・スコープのマネージドBeanおよびアプリケーション・ライフサイクル・メソッドのクラスを使用できます。また、これにより、複数のアプリケーション機能で同じデータ・オブジェクトを使用したときに、クラス・ローダーの問題が回避されます。各MAFアプリケーション機能はそれぞれ独自のクラス・ローダーを備えていますが、すべてがApplicationControllerプロジェクトの親クラス・ローダーを共有します。
データ・オブジェクト・パッケージ: データ・オブジェクトごとに生成されるJavaクラスで使用するJavaパッケージ名を指定します。
サービス・オブジェクト・パッケージ: サービス・オブジェクトごとに生成されるJavaクラスで使用するJavaパッケージ。データ・オブジェクトに少なくとも1つのRESTリソースが関連付けられている場合に、このようなクラスが生成されます。
データ・オブジェクト・クラスの上書き: 選択した場合、MAFは既存のデータ・オブジェクト・クラスを上書きします。以前の生成後にこれらのクラスにカスタム・コードを追加した場合は、このコードが失われます。
サービス・オブジェクト・クラスの上書き: 選択した場合、MAFは既存のサービス・オブジェクト・クラスを上書きします。以前の生成後にこれらのクラスにカスタム・コードを追加した場合は、このコードが失われます。ペイロード構造が変更されてもサービス・オブジェクト・クラスは通常変更する必要がないため、このチェック・ボックスはデフォルトでは選択が解除されています。また、このクラスには、生成後に追加したカスタム・コードが含まれる可能性が最も高くなります。
図6-17 クライアント・データ・モデルの生成
MAFアプリケーションに以前に生成したクライアント・データ・モデルを変更できるウィザードの起動方法について説明します。
MAFは、MAFアプリケーションでのクライアント・データ・モデルの反復開発をサポートしています。クライアント・データ・モデルは、最初に作成した後で拡張および改良できます。次の2つのオプションのいずれかを使用します。
RESTサービスからのクライアント・データ・モデルウィザードを再実行して、新しいデータ・オブジェクトを検出するか、または既存のデータ・オブジェクトを編集します。このウィザードを再起動して、新しいデータ・オブジェクトを検出できます。既存のデータ・オブジェクトを変更しない場合は、関連する「選択」チェック・ボックスの選択を解除します。そうすると、最後のジェネレータ設定ウィザード・ページでの設定に関係なく、既存のデータ・オブジェクトが後続のウィザード・ページに表示されず、これらのデータ・オブジェクト用にJavaクラスが再生成されなくなります。
図6-18に示した永続性マッピングの編集ウィザードを実行します。このウィザードは、クライアント・データ・モデルが含まれているプロジェクトを右クリックすると表示される永続性マッピングの編集コンテキスト・メニュー・エントリから起動します。
「永続性マッピングの編集」ウィザードには、RESTサービスに接続するためのページや新しいデータ・オブジェクトを検出するためのページを除き、RESTサービスからのクライアント・データ・モデルウィザードと同じページが表示されます。このため、このウィザードは通常、既存のデータ・オブジェクトを編集するために使用します。ただし、RESTサービスに対するデータの読取りや書込みにRESTコールを必要としないデータ・オブジェクトを作成してSQLiteデータベースに格納する場合にも使用できます。
図6-18 クライアント・データ・モデルの編集
MAFクライアント・データ・モデルでは、MAFアプリケーション内でオンデバイスSQLiteデータベースにアクセスするためのAPI (oracle.maf.api.cdm.persistence.manager.DBPersistenceManager
)が提供されます。
MAFクライアント・データ・モデルでは、SQLiteデータベースとのすべての相互作用がDBPersistenceManager
に委任されます。データベースへの接続方法で示されている下位レベルJDBC文を記述するかわりに、DBPersistenceManager
を使用します。クライアント・データ・モデルでのDBPersistenceManager
の役割の詳細は、MAFアプリケーションのクライアント・データ・モデルの概要を参照してください。DBPersistenceManager
、およびそれが公開するメソッドの詳細は、Oracle Mobile Application Framework Java APIリファレンスを参照してください。
次の例では、DBPersistenceManager
のインスタンスを取得し、データ・オブジェクトに対するいくつかの検索操作およびその他の操作(挿入、更新および削除)を実行する方法を示します。次の例で示すコンストラクタの使用だけでなく、サービス・オブジェクトのインスタンスからgetLocalPersistenceManager()
を呼び出すことでDBPersistenceManager
のインスタンスを取得することもできます。クライアント・データ・モデルのランタイム・オプションの設定で説明されている、クライアント・データ・モデル・ウィザードの「ランタイム・オプション」ページでカスタム・ローカル永続性マネージャを登録した場合は、これにより、DBPersistenceManager
のインスタンス、またはサブクラスが返されます。
/* Use the default constructor to get an instance of DBPersistenceManager.*/ DBPersistenceManager pm = new DBPersistenceManager(); /* findAll takes a class or class name as an argument and returns a list of all data objects.*/ List<Department> departments = pm.findAll(Department.class); /* The overloaded find method returns a filtered list of data objects. * * There are various ways to specify your filter conditions, as demonstrated by the following examples. * * A quick search method that returns all employees where at least one of the String * attributes (firstName, lastName, emailAddress, and so on) starts with "king" (case insensitive). * This translates to use of the SQL LIKE operator where the search value is suffixed with %. */ List<Employee> emps = pm.find(Employee.class, "king"); /* A search method which allows you to specify the search attributes. It returns all employees where at least * one of the attributes passed in as list in the third argument starts with "king" (case insensitive). */ List<String> searchAttrs = new ArrayList<String>(); searchAttrs.add("firstName"); searchAttrs.add("lastName"); List<Employee> emps = pm.find(Employee.class, "king", searchAttrs); /* This is a search method which allows you to specify separate values for each * attribute you want to search on, it will return all employees where the firstName * equals "Steven" and the lastName equals "King". The query is case sensitive. */ Map<String, String> searchAttrs = new HashMap<String, String>(); searchAttrs.put("firstName", "Steven"); searchAttrs.put("lastName", "King"); List<Employee> emps = pm.find(Employee.class, searchAttrs); /* The findByKey method returns one data object based on its primary key. * It first checks the data object cache. If the department with id 10 does not exist in the cache, it queries * the database. If you do not want to check the cache, add a third boolean argument checkEntityCache, as demonstrated * by the second example */ Department dep = (Department) pm.findByKey(Department.class, new Object[] { 10 }); Department dep = (Department) pm.findByKey(Department.class, new Object[] { 10 }, false); /* Insert, updates and remove a row for a data object instance. * MAF automatically commits the change if the second argument is true. If false, you * need to call pm.commit() later. * MAF generates the primary key attribute for you when calling insertEntity if you selected * the Generate Primary Key checkbox in the Runtime Options page of the MAF client data model wizard. */ pm.insertEntity(department, true); pm.updateEntity(department, true); pm.removeEntity(department, true);
カスタムSQL文の使用
前述のデータ・オブジェクトベースのAPIで、必要なSQL文を実行するためのオプションが提供されない場合は、カスタムSQL文を指定できます。SQL SELECT文とSQL DML文とは区別します。
SELECT文の結果をデータ・オブジェクトまたはデータ・オブジェクト・リストに変換する必要がある場合は、次の例で示すようにコードを使用します。
DBPersistenceManager pm = new DBPersistenceManager(); ClassMappingDescriptor descriptor = ClassMappingDescriptor.getInstance(Dealer.class); StringBuffer sql = pm.getSqlSelectFromPart(descriptor); sql.append(" WHERE SALES_ACCOUNT_ID not in (SELECT DEALER_ID FROM PRIORITY_ASSIGNMENT WHERE PRIORITY_ID=" + priority.getId() + ")"); sql = pm.constructOrderByClause(sql, descriptor); ResultSet set = pm.executeSqlSelect(sql.toString(), null); List<Dealer> dealerList = pm.createEntitiesFromResultSet(set, descriptor.getAttributeMappingsDirect());
結果をデータ・オブジェクト・リストに変換する必要があるため、SELECT句にすべての列を含み、FROM句で、データ・オブジェクトに対応するテーブル名を使用する必要があります。getSqlSelectFromPart
という便利なメソッドを使用することで、persistence-mapping.xml
ファイルからデータ・オブジェクト・クラス・ディスクリプタを使用してSELECT句およびFROM句が自動的に作成されます。その後、SQL文にカスタムWHERE句を追加できます。カスタムORDER BY句を追加することもできますが、データ・オブジェクト・クラス・ディスクリプタで登録されたとおりのデフォルトorder byを使用する場合は、前述の例で示した便利なメソッドconstructOrderByClause
を使用できます。SQL SELECT文を作成したら、JDBC RowSet
オブジェクトを返すexecuteSqlSelect
メソッドを使用してそれを実行できます。次に、createEntitiesFromResultSet
メソッドを使用してJDBC RowSet
をデータ・オブジェクト・リストに変換します。
データ・オブジェクト・リストに変換できないすべてのSQL SELECT結果については、SQL文全体の定義、またJDBC RowSet
の処理が可能です。次に、集約問合せの例を示します。
DBPersistenceManager pm = new DBPersistenceManager(); String sql = "SELECT AVG(SALARY) FROM EMPLOYEE"; ResultSet set = pm.executeSqlSelect(sql, null); try { set.first(); int averageSalary = set.getInt(1); } catch (SQLException e) { sLog.severe("Error executing SQL statement: "+e.getLocalizedMessage()); }
単一行の挿入、更新または削除の場合は、通常は、前述のデータ・オブジェクトベースのAPIを使用します。ただし、一度に複数の行を挿入、更新または削除する必要がある場合は、executeSqlDml
メソッドを使用できます。次に、すべての事務員の給料を10%増加させる例を示します。
DBPersistenceManager pm = new DBPersistenceManager(); String sql = "UPDATE EMPLOYEE SET SALARY = SALARY * 1.1 WHERE JOB_ID='CLERK'"; pm.executeSqlDml(sql, null, true);
3番目のdoCommit
引数がtrue
の場合、バッチ更新が自動的にコミットされます。それがfalse
に設定されている場合は、後でpm.commit()
を呼び出す必要があります。
MAFクライアント・データ・モデルでサポートされている標準のCRUDリソースに直接マップしないカスタムRESTリソースをMAFアプリケーションのクライアント・データ・モデルに追加する方法について説明します。
「リソースの詳細」ウィザード・ページでは、「CRUD RESTリソース詳細の指定」で説明しているように、カスタム・リソースを追加できます。これを行うと、MAFはカスタム・リソースと同じ名前の追加のメソッドを次のシグネチャでサービス・オブジェクトに生成します。
public void doSomething(Department department) { invokeCustomMethod(department, "doSomething"); }
このアプローチの利点は、実装が高速かつ容易であることです。MAFアプリケーションがオフラインの場合や、サーバー・エラーのためにRESTコールが失敗した場合には、カスタム・リソース・アクションは保留中のデータ同期アクションとして登録されます。MAFは、後でMAFアプリケーションがオンライン・モードに戻ったときにコールを行います。つまり、「MAFアプリケーションからのオフライン・トランザクションの同期」で説明しているように、オフライン・モードの標準のCRUDトランザクションと同じように処理されます。
このアプローチの短所は、問合せパラメータおよびパス・パラメータを提供する方法およびリクエスト・ペイロードを提供する方法が制限されることです(これはシリアライズされたデータ・オブジェクトのみが可能)。問合せパラメータおよびパス・パラメータの値は、「CRUD RESTリソース詳細の指定」で説明しているオプションを使用して宣言的に定義できます。問合せパラメータおよびパス・パラメータによるURIパスの構築方法に完全な柔軟性を持たせる必要がある場合は、プログラムによるアプローチを採用し、RESTコールをJavaでコーディングした方が効率的です。
RESTリソースを起動するには、リモート永続性マネージャ(RestJSONPersistenceManager
か、またはOracle Mobile Cloud Service (MCS)に接続する場合にはMCSPersistenceManager
)でinvokeRestService
メソッドを使用します。これらのクラスおよびメソッドの詳細は、Oracle Mobile Application Framework Java APIリファレンスを参照してください。
また、「WebサービスにアクセスするためのRESTサービス・アダプタの作成」で説明しているように、RESTServiceAdapter
を使用することもできます。ただし、invokeRestService
メソッドには、次の利点があります。
1行のコードでRESTコールを行います。
200-299
の範囲のHTMLステータス・コードですべてのレスポンスを正常に処理します。RESTServiceAdapter
のようにステータス・コード201-299
で例外がスローされることはありません。
Webサービス・ロギングを有効にした場合は、RESTコール詳細を簡単に表示できます。
MCSに接続する場合は、Oracle-Mobile-Backend-Id
および(匿名) Authorization
ヘッダー・パラメータを指定する必要はありません。
invokeRestService
メソッドのシグネチャは次のとおりです。
public String invokeRestService(String connectionName, String requestType, String requestUri, String payload, Map<String, String> headerParamMap, int retryLimit, boolean secured)
注意:
最後の引数(secured
)は使用されません。後方互換性のために含まれています。通常、invokeRestService
メソッドを起動するサービス・オブジェクトにメソッドを追加します。その後、AMXページにメソッドをドラッグ・アンド・ドロップして、ユーザー・インタフェースからRESTコールを行うことができます。次に、このようなRESTコールを行うサンプルのメソッドを示します。
public void invokeSomeRestResource(String pathParamValue,String queryParamValue) { if (isOnline()) { RestJSONPersistenceManager rpm = new RestJSONPersistenceManager(); String uri = "/someResourcePath/"+pathParamValue+"?someQueryParam="+queryParamValue; String result = rpm.invokeRestService("MyRESTConn", "GET", uri, null, null, 0, false); // do something with the result } }
バックグラウンドでRESTコールを実行する場合、コードは次のようになります。
public void invokeSomeRestResource(String pathParamValue,String queryParamValue) { TaskExecutor.getInstance().execute(true, () -> { if (isOnline()) { RestJSONPersistenceManager rpm = new RestJSONPersistenceManager(); String uri = "/someResourcePath/"+pathParamValue+"?someQueryParam="+queryParamValue; String result = rpm.invokeRestService("MyRESTConn", "GET", uri, null, null, 0, false); // do something with the result } }); }
標準の「すべてのリソースの検索」のようにレスポンスをエンティティのリストに変換してSQLiteデータベースに格納する必要がある場合は、リモート永続性マネージャでhandleReadResponse
メソッドを使用して、これをすべて自動的に行います。次に、このメソッドのシグネチャを示します。
public <E extends Entity> List<E> handleReadResponse(String jsonResponse, Class entityClass, String collectionElementName, String rowElementName, List<BindParamInfo> parentBindParamInfos, boolean deleteAllRows)
collectionElementName
引数およびrowElementName
引数は、標準のGETリソース用に指定している「ペイロード・リスト要素名」および「ペイロード行要素名」にマップされます。レスポンス・ペイロードの構造に基づいてこれらの引数の値を設定する方法は、「CRUD RESTリソース詳細の指定」を参照してください。レスポンスがトップレベルのオブジェクトとして配列を返す場合は、collectionElementName
の値としてルートを指定する必要があります。
parentBindParamInfos
引数はnull
のままにすることができ、deleteAllRows
引数は廃止されています(後方互換性を維持するために含まれています)。レスポンス・ペイロードを処理する前にすべてのローカル行を削除する場合は、そのためのコードを自分で追加する必要があります。
次のコード・サンプルでは、レスポンス・ペイロードを処理してエンティティ・リストを作成するように前述の例を拡張する方法を示しています。
public void invokeSomeRestResource(String pathParamValue,String queryParamValue) { if (isOnline()) { RestJSONPersistenceManager rpm = new RestJSONPersistenceManager(); String uri = "/someResourcePath/"+pathParamValue+"?someQueryParam="+queryParamValue; String result = rpm.invokeRestService("MyRESTConn", "GET", uri, null, null, 0, false); List<Employee> emps = rpm.handleReadResponse(result, Employee.class, "root", null, null, false); setEntityList(emps); } }
ローカルおよび/またはリモート永続性マネージャに対してCRUD操作を実行するためにMAFがサービスクラス内で生成するメソッドを説明します。MAFでは、「REST WebサービスからのMAFクライアント・データ・モデル」ウィザードからこれらのメソッドを生成します。
たとえば、部門サービス・クラスにはfindAllDepartment
、saveDepartment
およびremoveDepartment
などのメソッドがあります。これらのメソッドでは、間接的に、対応するRESTコールが行われます(「REST WebサービスからのMAFクライアント・データ・モデル」ウィザードでこれらのコールを構成してある場合)。そのため、RESTレスポンスに基づいてカスタム・ロジックを実行する必要がある場合は、これらのメソッドの最後にロジックを追加する可能性があります。ただし、これらのRESTコールをバックグラウンドで実行した場合(「ランタイム・オプション」ページで「バックグラウンドでのリモート読取り」および/または「バックグラウンドでのリモート書込み」チェック・ボックスを選択した場合)、これはうまくいきません。このシナリオでは、カスタム・ロジックは、別個のバックグラウンド・スレッドでRESTコールがまだ進行中の間にすでに実行されています。
必ずRESTコールがバックグラウンドで完了した後にカスタム・ロジックが実行されるよう、別のアプローチを取る必要があります。読取りRESTコール(GET
)の後にカスタム・ロジックを追加する必要がある場合は、サービス・クラス内の次のメソッドのいずれかをオーバーライドできます。
executeRemoteFindAll
executeRemoteFindAllInParent
executeGetCanonical
executeRemoteFindAll
をオーバーライドする、次の例で示すように、super
へのコールの後にカスタム・ロジックを追加します。
@Override protected List<Department> executeRemoteFindAll() { // call super to get the new list of departments from REST call List<Department> result = super.executeRemoteFindAll(); // do some custom logic here ... // return department list return result; }
書込みRESTコール(POST
、PUT
、PATCH
、DELETE
)の後にカスタム・ロジックを実行する必要がある場合は、リモート永続性マネージャのサブクラスを作成し、次のメソッドのいずれかをオーバーライドします。
insertEntity
updateEntity
mergeEntity
removeEntity
「永続性マッピングの編集」ウィザードを使用することで、サブクラス化する必要があるリモート永続性マネージャ・クラスを見つけます。図6-19で示す「ランタイム・オプション」ページに移動し、正しいデータ・オブジェクトを選択し、「リモート永続性マネージャ」フィールドからクラス名をコピーします。このクラスからサブクラス化した新しいJavaクラスを作成し、前にリストしたメソッドのうち1つまたは複数をオーバーライドします。MAFアプリケーション・ランタイムでカスタム・リモート永続性マネージャが使用されるようにするには、図6-19で示す「リモート永続性マネージャ」フィールドにサブクラスの完全修飾クラス名を入力します。「ランタイム・オプション」ページにアクセスするための「永続性マッピングの編集」ダイアログの起動の詳細は、MAFアプリケーションでのクライアント・データ・モデルの編集を参照してください。
図6-19 リモート永続性マネージャ
MAFによってサポートされるオフライン・トランザクションが原因で、書込みメソッドのリモート永続性マネージャをサブクラス化する必要があります。オフライン・モードでは、RESTコールは実行されませんが、かわりに、保留中の同期アクションとして登録されます。再度オンラインになったときに、MAFアプリケーションで、サービス・クラスをバイパスし、リモート永続性マネージャ・サブクラスを使用して直接RESTコールが実行されます。
RESTコールからの未加工のレスポンスに基づいてカスタム・ロジックを実行する必要がある場合は、MAFで処理が実行される前に、リモート永続性マネージャ内の次のメソッドをオーバーライドします。
public String invokeRestService(String connectionName, String requestType, String requestUri, String payload, Map111String,String222 headerParamMap, int retryLimit, boolean secured)
このメソッドは、MAF内のすべてのRESTコールを処理し、RESTコールのレスポンスを返します。super
を呼び出した後、getLastResponseStatus()
およびgetLastResponseHeaders()
を呼び出してレスポンスに関する情報をさらに取得することもできます。
サービス・オブジェクトのインスタンスへのプログラムによるアクセスを実現する方法について説明します。
通常、「データ・コントロール」パネルからのドラッグ・アンド・ドロップを使用してMAFアプリケーションのユーザー・インタフェースを作成するには、サービス・オブジェクトのデータ・コントロールを作成します。マネージドBeanまたはライフサイクル・リスナー・メソッドからサービス・オブジェクトにアクセスする場合は、次のように、サービス・オブジェクトのインスタンスを作成します。
DepartmentService service = new DepartmentService();
DepartmentService
は、oracle.maf.impl.cdm.persistence.service.EntityCRUDService
から拡張したものです。
「クライアント・データ・モデルのランタイム・オプションの設定」で説明しているように「ランタイム・オプション」ページで「自動問合せ」を選択し、「CRUD RESTリソースの定義」で説明しているように「すべてのリソースの検索」の値を指定した場合には、これがRESTコールをトリガーします。作成したインスタンスに対してプログラムによりこの自動問合せを実行しないようにする場合は、autoQuery
をブール引数として取る次のコンストラクタを使用します。
DepartmentService service = new DepartmentService(false);
自動問合せを実行する場合は、次のようにコードを記述します。
DepartmentService service = new DepartmentService(); List<Department> deps = service.getDepartment(); // do something with the departments
「ランタイム・オプション」ページで「バックグラウンドでのリモート読取り」チェック・ボックスを選択してRESTコールをバックグラウンドで実行する場合には、これが確実に機能するとはかぎりません。その場合、RESTコールがバックグラウンド・スレッドで実行されるため、getDepartment()
メソッド・コールはローカル・データベースに格納されている部門のみを返します(ある場合)。したがって、最も安全な方法は次のコンストラクタを使用することです。
public DepartmentService(boolean doRemoteReadInBackground, boolean doRemoteWriteInBackground) { super(false); setDoRemoteReadInBackground(doRemoteReadInBackground); setDoRemoteWriteInBackground(doRemoteWriteInBackground); }
このコンストラクタは、autoQuery
引数をfalse
に設定してsuper
をコールし、コンストラクタ・コールの指定に従ってremoteReadInBackground
プロパティおよびremoteWriteInBackground
プロパティを設定します。つまり、このコンストラクタを使用すると、「ランタイム・オプション」ページで行った(その結果persistence-mapping.xml
ファイルに格納されている)選択の内容を無視するインスタンスを取得できます。
DepartmentService service = new DepartmentService(false,false); // get latest department list from server by making synchronous REST call service.findAllDepartmentRemote; // get a handle in the department list List<Department> deps = service.getDepartment(); // do something with the department list
マネージドBeanコードでは、データ・コントロールで使用されるサービス・オブジェクト・インスタンスへのアクセスが必要になる場合があります。これを行うには、次の便利なメソッドを使用します。
DepartmentService service = (DepartmentService) EntityUtils.getEntityCRUDService(Department.class);
このメソッドは、サービス・オブジェクト・クラスの名前でデータ・コントロール・インスタンスを検索します。ただし、データ・コントロールをまだインスタンス化しないうちにこのメソッドを使用した場合(つまり、AMXページで使用した場合)、前に説明したように、デフォルトのコンストラクタを使用して新規インスタンスを作成するため、不要なRESTコールをトリガーする場合があることに注意してください。データ・コントロール・インスタンスは、アプリケーション機能のコンテキストに存在します。2つの異なるアプリケーション機能のAMXページで同じデータ・コントロールを使用した場合は、基礎となるサービス・オブジェクトのインスタンスが2つ存在することになります。getEntityCRUDService
メソッドを実行するアプリケーション機能コンテキストによって、どのインスタンスが返されるかが決まります。
マネージドBeanコードでデータ・コントロール・インスタンスを操作する必要性を減らすため、可能なかぎり多くのロジックをサービス・クラスに移動します。多数の値バインディング式およびメソッド・バインディング式を評価し、その後でこれらの多数のメソッド・バインディングを実行する場合は、コーディング方針を見直すことをお薦めします。
最後に、カスタムJavaコードでローカルSQLiteデータベースにのみアクセスする必要があり、RESTコールを行う必要がない場合は、サービス・オブジェクト・インスタンスを作成する必要はありません。かわりに、oracle.maf.api.cdm.persistence.manager.DBPersistenceManager
のインスタンスを使用して、データ・オブジェクトの問合せまたは操作を行います。詳細は、Oracle Mobile Application Framework Java APIリファレンスを参照してください。
SQLiteデータベース内の行を一意に識別できるようにするには、すべてのデータ・オブジェクトに主キーが必要となります。また、MAFでは、データ・オブジェクト・キャッシュでのデータ・オブジェクト作成を最小限に抑えるため、およびデータ・オブジェクトの複数のインスタンスが作成されないようにするために、主キーが使用されます。
キャッシュから特定のデータ・オブジェクト・インスタンスを取得するには、次のメソッドを使用します。
EntityCache.getInstance().findByUID(Class entityClass, Object[] key)
キャッシュから、またはデータ・オブジェクトがキャッシュされていない場合にデータベースからデータ・オブジェクトを取得するには、DBPersistenceManager
クラスから次のメソッドを使用します。
findByKey(Class entityClass, Object[] key)
主キーは、複数の属性/列で構成される複合キーにできます。これは、前述のメソッドのkey
引数がオブジェクト配列を取るためです。主キーが単一の数値属性である場合は、クライアント・データ・モデルのランタイム・オプションの設定で説明されている「ランタイム・オプション」ページ内の「主キーの生成」チェック・ボックスを選択してあると、MAFで自動的に主キーが生成されます。フレームワークまたはカスタム・コードによってDBPersistenceManager
のinsertEntity
メソッドが呼び出されると、MAFにより、SQLiteデータベースに現在の最大値が照会され、この値が1
増分されます。
次のメソッドを使用することで、自分で主キーを生成することもできます。
EntityUtils.generatePrimaryKey(Entity entity, int increment)
注意:
後者のメソッドは、データ・オブジェクトに対して「主キーの生成」チェック・ボックスを選択した場合のみ機能します。これは、それによってpersistence-mapping.xml
ファイル内のこのフラグがチェックされるためです。サーバーで生成された主キー値のMAFによる管理の理解
新しいデータ・オブジェクト・インスタンスのために作成または生成した主キー値は、一時主キーとみなされます。もちろん、この主キーがサーバー側で一意であることは保証されません。通常は、データ・オブジェクトをリモート・サーバーに挿入するRESTサービスを起動すると(ウィザードで指定した「リソースの作成」)、リモート・サーバーによって一意の主キーが生成されます。
データ・オブジェクトを挿入するRESTコールで、レスポンスで、サーバーによって生成された新しい値があるデータ・オブジェクトが返された場合、MAFクライアント・データ・モデルにより、サーバーで生成された主キーで一時主キーが自動的に更新されます。MAFクライアント・データ・モデルにより、一時主キーを含むデータベース行が削除され、サーバーで生成されたキーを含む新しい行が挿入されます。また、データベース・オブジェクト・キャッシュが更新されます。
このMAFクライアント・データ・モデル機能を役立てるには、「リソースの作成」エンドポイントにおいてレスポンスでデータ・オブジェクト全体が返されることを確認してください。
SQLite自動増分機能
MAFでは、データ・オブジェクト・キャッシュ内のインスタンスを識別するため、およびMAFアプリケーションでサーバーから最新データがフェッチされるときにSQLiteデータベース内の既存行を更新するために主キーが使用されるため、SQLiteの自動増分機能を使用しないようにしてください。この理由から、SQLiteの自動増分機能ではなく、リモート・サーバーからのペイロードに含まれる1つまたは複数のデータ・オブジェクト属性を使用して主キーを指定することをお薦めします。
サーバーから取得されるデータ・オブジェクト・インスタンスがないデータ・オブジェクトのためにSQLiteの自動増分機能を使用するのはかまいません。つまり、ここでは、SQLiteデータベースにデータを読み込むために呼び出されるGETリソースはありません。
SQLiteの自動増分機能の詳細は、SQLiteのドキュメントを参照してください。
MAFアプリケーションのUIでのフィルタされたリストの作成を容易にするためにMAFクライアント・データ・モデルが生成するメソッドを説明します。
MAFでは、データ・オブジェクト・インスタンスのコレクションを返す、データ・オブジェクトのCRUDサービス・クラス内のゲッター・メソッドが生成されます。たとえば、従業員データ・オブジェクトのサービス・クラス(EmployeeService
)には次のメソッドがあります。
public List<Employee> getEmployee() { return getEntityList(); }
EntityCRUDService
スーパークラスでは、返されたリストはプライベート・メンバー変数(private EntityList<E> entityList
)に格納されます。このリストには、ローカルSQLiteデータベースから、および/またはクライアント・データ・モデルのランタイム・オプションの設定で説明されているように「ランタイム・オプション」ページ内の「自動問合せ」チェック・ボックスを選択した場合にはFind All RESTリソースへのコールから取得されたすべてのデータ・オブジェクト・インスタンスが含まれています。
ユーザー・インタフェース内のクイック検索フィールドに基づいてこのリストをフィルタするには、データ・オブジェクトCRUDサービス・クラスで生成される、find[entityName]
メソッドを使用します。たとえば、次のメソッドは、従業員データ・オブジェクトのために生成されます。
public void findEmployee(String searchValue) { super.find(searchValue); }
検索フィールドおよびコマンド・ボタンをレンダリングしてfind[entityName]
メソッドを呼び出すには、メソッドを「データ・コントロール」パネルから自分のAMXページにドラッグ・アンド・ドロップします。エンド・ユーザーがそのコマンド・ボタンをタップしてfind[entityName]
メソッドを呼び出すと、find[entityName]
メソッドはDBPersistenceManager
クラス内のfind
メソッドに委任します。DBPersistenceManager
クラスの詳細は、MAFクライアント・データ・モデルDBPersistenceManagerの使用によるSQLiteデータベースへのアクセスおよびOracle Mobile Application Framework Java APIリファレンスを参照してください。find
メソッドから返される結果は、entityList
変数の新しいコンテンツとして格納されます。エンド・ユーザーがコマンド・ボタンをクリックした後、リストを正しくリフレッシュするために、データ変更イベントがユーザー・インタフェースに送信されます。
エンティティ・リストをフィルタするカスタム・メソッドを記述することもできます。次の例では、従業員のリストを事務員のみにフィルタします。
public void filterClerks() { DBPersistenceManager pm = new DBPersistenceManager(); Map<String,String> searchAttrs = new HashMap<String,String>(); searchAttrs.put("jobId","CLERK"); List<Employee> clerks = pm.find(Employee.class,searchAttrs); setEntityList(clerks); }
setEntityList
を呼び出すとentityList
変数が更新され、setEntityList
が呼び出されると、必要なデータ変更イベントがユーザー・インタフェースに送信されます。
注意:
このメソッドがUIアクションを介して呼び出されるか、バックグラウンド・スレッドでJavaコードを介して呼び出されるかは問題ではありません。たとえば、アプリケーションのユーザー・インタフェースで、フィルタされたリストを一度に複数表示する必要があるとします。たとえば、従業員のリストと事務員のリストを表示する必要があるとします。このシナリオでは、entityList
によってすでにすべての従業員がユーザー・インタフェースに表示されているため、filterClerks
メソッドではentityList
メンバー変数は更新できません。解決策は、事務員のリストを返すゲッター・メソッドの追加です。これにより、事務員のリストを表示するAMXページ上にドラッグ・アンド・ドロップできる、事務員コレクション属性がEmployeeService
データ・コントロールに追加されます。次のコード例では、そのようなゲッター・メソッドを示します。
public List<Employee> getClerks() { DBPersistenceManager pm = new DBPersistenceManager(); Map<String,String> searchAttrs = new HashMap<String,String>(); searchAttrs.put("jobId","CLERK"); List<Employee> clerks = pm.find(Employee.class,searchAttrs); return clerks; } @Override protected void setEntityList(List<Employee> entityList) { super.setEntityList(entityList); // we also need to refresh the clerks list getPropertyChangeSupport().firePropertyChange("clerks", null, getClerks()); getProviderChangeSupport().fireProviderRefresh("clerks"); }
前の例では、Find All RESTリソースを呼び出すことでバックグラウンドで最新の従業員リストを取得するという状況に対処するよう、setEntityList
メソッドをオーバーライドしています。MAFアプリケーションでバックグラウンドでFind All RESTコールを実行した場合、レスポンスが返されるとsetEntityList
が呼び出され、データベースが、レスポンス・ペイロードに含まれる最新のデータ・オブジェクト・インスタンス・セットで更新されます。この例では、ユーザー・インタフェースに、すべての従業員のリストと事務員のリストが示されます。RESTコールが完了したときに、両方のリストが更新されるようにする必要があります。setEntityList
メソッドのオーバーライドによって、RESTサービスから最新の従業員セットが取得されるとgetClerks
メソッドが再度実行され、事務員リストをリフレッシュするためにデータ変更イベントが送信されるようになります。
クライアント・データ・モデルを生成したMAFアプリケーションでは、開発作業の簡素化のためにMAFから提供される数多くの機能を利用できます。
クライアント・データ・モデルの作成後、通常、クライアント・データ・モデルが生成したエンティティ・サービス・クラスごとにデータ・コントロールを作成します。これらのデータ・コントロールには、JDeveloperの「データ・コントロール」パレットから操作およびコレクションをドラッグ・アンド・ドロップしてMAF AMXページを構築できるようにするメソッドが多数含まれています。詳細は、「クライアント・データ・モデルからデータ・コントロールを作成する方法」を参照してください。
「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」で説明しているようにクライアント・データ・モデルを作成した場合は、MAFアプリケーションのユーザー・インタフェース・レイヤーを構築およびテストする際の生産性を高めるためにMAFに用意されている次の機能を利用できます。
「MAFユーザー・インタフェース・ジェネレータ」ウィザードを使用して、クライアント・データ・モデルをテストし、MAFアプリケーションのユーザー・インタフェースのプロトタイプを作成します。このウィザードは、タスク・フロー、AMXページ、データ・バインディングなどを備えた完全なMAFアプリケーション機能を生成します。このウィザードを使用するには、データ・コントロールを作成し、そのデータ・コントロールからアプリケーション機能を生成します。詳細は、「MAFユーザー・インタフェース・ジェネレータを使用する方法」を参照してください。
MAFに付属している再利用可能なデータ同期アプリケーション機能を使用して、保留中のデータ同期アクションを表示します。あるいは、データ同期サービス・データ・コントロールを使用して独自のAMXページを作成します。
MAFアプリケーションが実際にはインターネットに接続されている間もオフラインであるかのように動作するようにします。
ビジュアル・インジケータを追加して、MAFアプリケーションがバックグラウンドで作業を実行中であることをエンド・ユーザーに示します。このビジュアル・インジケータは通常、MAFアプリケーションがリモート・サーバーに対してデータの読取りまたは書込みを実行中であることをエンド・ユーザーに通知するために使用します。
MAFに付属しているWebサービス・コールというアプリケーション機能を使用して、MAFアプリケーションが行うRESTコールを調べます。
MAFアプリケーションのクライアント・データ・モデルの生成時に作成されるサービス・オブジェクトからデータ・コントロールを作成する方法について説明します。
「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」で説明しているようにクライアント・データ・モデルの作成が完了したら、通常、後者のタスク中に生成されたサービス・オブジェクトからデータ・コントロールを作成します。これらのデータ・コントロールにより、アプリケーションのユーザー・インタフェースの作成が容易になります。JDeveloperの「データ・コントロール」パネルからコレクション、属性または操作をドラッグしてAMXページにドロップできます。このとき、MAFにより、AMXページにデータまたはUIコントロールをレンダリングするのに適したAMXコンポーネントを選択するように求められます。あるいは、「MAFユーザー・インタフェース・ジェネレータを使用する方法」で説明しているように、「MAFユーザー・インタフェース・ジェネレータ」ウィザードを使用してデータ・コントロールからアプリケーション機能を生成できます。
クライアント・データ・モデルからデータ・コントロールを作成するには:
「アプリケーション」ウィンドウで、クライアント・データ・モデル・ウィザードが生成したサービス・オブジェクトに移動します。
サービス・オブジェクトの場所は、クライアント・データ・モデル・ウィザードで選択したオプションによって異なります。たとえば、図6-20は、MAFアプリケーションのApplicationControllerプロジェクトのDepartmentServiceサービス・オブジェクトおよびEmployeeServiceサービス・オブジェクトを示しています。
図6-20 クライアント・データ・モデルのサービス・オブジェクト
「アプリケーション」ウィンドウで、サービス・オブジェクト(たとえば、DepartmentService.java
)を右クリックし、表示されたコンテキスト・メニューから「データ・コントロールの作成」を選択します。
表示された「Beanデータ・コントロールの作成」ウィザードのフィールドに入力し、「終了」をクリックします。
MAFは、AMXページにドラッグ・アンド・ドロップできる様々な要素および操作を使用して、サービス・オブジェクトからデータ・コントロールを生成します。
このデータ・コントロールは、「MAFユーザー・インタフェース・ジェネレータを使用する方法」で説明しているように、「MAFユーザー・インタフェース・ジェネレータ」ウィザードでアプリケーション機能を作成する場合にも使用できます。
図6-21 クライアント・データ・モデルのサービス・オブジェクトから作成されたデータ・コントロール
MAFアプリケーションにクライアント・データ・モデルを生成した場合は、「MAFユーザー・インタフェース・ジェネレータ」ウィザードを使用して、タスク・フロー、AMXページおよびデータ・バインディングを備えたMAFアプリケーション機能を作成します。
このウィザードでは、クライアント・データ・モデルをテストし、MAFアプリケーションのプロトタイプのユーザー・インタフェースを作成できます。ウィザードを使用する前に、「MAFアプリケーションでのクライアント・データ・モデルの作成の概要」で説明しているように、クライアント・データ・モデルを作成します。クライアント・データ・モデルを作成すると作成されるサービス・オブジェクトからデータ・コントロールを生成します。MAFアプリケーション機能を生成するデータ・コントロールがJDeveloperの「データ・コントロール」ウィンドウに表示されることを確認してください。「リフレッシュ」アイコンをクリックすることが必要になる場合があります。
ウィザードは、データ・コントロールが公開するデータ・オブジェクトごとにリスト・ビュー・ページおよびフォーム・ページを生成します。リスト・ビュー・ページに公開するデータ・オブジェクト属性を選択できます。フォーム・ページには、すべてのデータ・オブジェクト属性が表示されます。
「MAFユーザー・インタフェース・ジェネレータ」ウィザードを使用するには:
メイン・メニューで、「ファイル」を選択し、「ギャラリから」を選択します。
「新規ギャラリ」の「クライアント層」カテゴリの「モバイル・アプリケーション・フレームワーク」ノードで、「MAFユーザー・インタフェース・ジェネレータ」をダブルクリックします。
ようこそページの「次へ」をクリックし、「データ・コントロールの選択」ページで、MAFアプリケーション機能を生成するデータ・コントロールを選択します。
「MAFアプリケーションの保護」で説明しているようにMAFアプリケーションのセキュリティをすでに構成している場合は、「データ・コントロールの選択」ページの「機能セキュリティの有効化」チェック・ボックスを選択できます。このタスクは後で実行できます。
「次へ」をクリックして、図6-22に示す「データ・オブジェクトUI設定」ページに移動し、次のように値を構成します。
データ・オブジェクト: ドロップダウン・リストからデータ・オブジェクトを選択し、このオブジェクトの表示タイトルと、エンド・ユーザーが実行できるようにユーザー・インタフェースに公開する操作(オブジェクトの作成や更新や削除など)を構成します。「CRUD RESTリソースの定義」で説明しているようにCRUD RESTリソースを定義した場合は、「作成可能」、「更新可能」および「削除可能」のチェック・ボックスを選択します。
親ページに表示: 親データ・オブジェクトのフォーム・ページ内に子データ・オブジェクトのリスト・ビューを含める子データ・オブジェクトを選択します。別個のページに表示するチェック・ボックスの選択を解除します。このチェック・ボックスを使用する場合は、「データ・オブジェクトの親子関係の指定」で説明しているように、親子関係を構成しておくことが前提となります。
リスト属性: リスト・ビュー・ページに表示するデータ・オブジェクト属性を指定します。リスト区切りを選択することもできます。
図6-22 MAFユーザー・インタフェース・ジェネレータによって生成されたページのユーザー・インタフェースの構成
「終了」をクリックして、ユーザー・インタフェースを含むMAFアプリケーション機能を生成します。
「MAFユーザー・インタフェース・ジェネレータ」ウィザードを完了すると、MAFはMAFアプリケーションに次の変更を加えます。
maf-feature.xml
ファイルに新規に作成したMAFアプリケーション機能を追加します。
Web Content
ディレクトリのサブディレクトリにタスク・フローを生成します。サブディレクトリの名前は、データ・オブジェクト(たとえば、Department
)の名前から導出されます。
生成したタスク・フローにビュー・アクティビティごとにAMXページを生成し、生成したAMXページのAMXページ定義を生成します。
adfc-mobile-config.xml
ファイルにGoToFeature
およびConnectivity
マネージドBeanを追加します。生成したAMXページがこれらのマネージドBeanを使用します。
新規に作成したMAFアプリケーション機能への参照に加え、クライアント・データ・モデルを生成するとMAFに含まれるその他の2つのアプリケーション機能への参照も追加します。これらは、データ同期アプリケーション機能とWebサービス・コール・アプリケーション機能です。これらのアプリケーション機能の使用の詳細は、「MAFアプリケーションからのオフライン・トランザクションの同期」および「MAFアプリケーションでのWebサービス・コールの検査」を参照してください。
MAFアプリケーションでは、アプリケーションがオフライン・モードのときに、エンド・ユーザーがトランザクションを実行できるようにすることができます。MAFは、アプリケーションが次にオンライン・モードになったときに、トランザクションごとにデータ同期アクションを実行します。
このコンテキストでのトランザクションは、RESTコールによるデータの作成、変更または削除を伴う、サービス・オブジェクトに対するメソッドの起動です。saveDepartment(Department)などサービス・オブジェクトのメソッドを起動すると、MAFはコール可能な対応するRESTリソースがあるかどうかをチェックします。対応するRESTリソースがない場合、MAFはオンデバイスSQLiteデータベースに対して適宜メソッドを起動します。コール可能な対応するRESTリソースがある場合、MAFはデータ同期アクションを作成します。データ同期アクションは、実行するリソースのタイプ(挿入、更新、削除またはカスタム・アクション)およびトランザクションが必要とするデータ・オブジェクト・インスタンスのすべてのデータを保持します。
MAFアプリケーションがオンライン・モードになると、MAFは同期アクションを起動し、対応するRESTリソースを起動します。MAFアプリケーションがオフライン・モードで、データ・オブジェクトに対するオフライン・トランザクションを有効にした場合、MAFはデータ同期アクションを登録し、それを保留中の同期アクションとして格納します。MAFは、MAFアプリケーションが次にオンライン・モードになったときに、アクションを同期します。MAFは、同期するときにトランザクションがコミットされる正確な順序を保持します。
データ・オブジェクトのオフライン・トランザクションを有効にするには、「クライアント・データ・モデルのランタイム・オプションの設定」で説明しているように、オフライン・トランザクションの有効化チェック・ボックスを選択します。データ・オブジェクトに対するオフライン・トランザクションを無効にした場合、エンド・ユーザーがデータ・オブジェクトに対してオフライン・トランザクションを実行しようとすると、MAFは「デバイスはオフラインです。」
という例外をスローします。この例外を防ぐには、MAFアプリケーションがオフライン・モードのときにはUIコントロールを無効にして、エンド・ユーザーが例外をスローするトランザクションを作成できないようにします。
MAFは、SQLiteデータベースのPENDING_SYNCH_ACTIONS
表にデータ同期アクションを格納します。データ・オブジェクトのSQLiteデータベース表には、データ同期アクションに関連する情報は格納されません。たとえば、MAFアプリケーションがオフライン・モードのときに、エンド・ユーザーが部門を削除したとします。このアクションは、SQLiteデータベースのDEPARTMENTS
表から対応する行を削除し、対応するデータ同期アクションをPENDING_SYNCH_ACTIONS
表に追加します。MAFアプリケーションが次にオンライン・モードになったときに、部門の削除というデータ同期アクションに関連付けられたRESTアクションが実行されます。これにより、関連付けられたRESTコールが実行されたかどうかに関係なく、エンド・ユーザーによって実行された最新のトランザクションがローカルSQLiteデータベース表に反映されるようになります。
MAFアプリケーションがオフライン・モードからオンライン・モードに戻った場合、MAFはアプリケーションがRESTコールを起動するのを待機します。このイベントが発生すると、MAFはRESTコールを処理する前に保留中のデータ同期アクションを同期します。この同期により、MAFアプリケーションに対する後続のRESTコールおよびレスポンスでは廃止されたデータが使用されなくなります。自動同期をトリガーするRESTコールがない場合でも、MAFアプリケーションの任意のサービス・オブジェクトからsynchronize(boolean)メソッドを起動することで、MAFアプリケーションがオンライン・モードに戻ったときに、この自動同期を明示的に起動できます。synchronize(boolean)
メソッドは、すべてのサービス・オブジェクト・クラスの拡張元となるoracle.maf.impl.cdm.persistence.service.EntityCRUDService
クラスに用意されています。このメソッドを任意のサービス・オブジェクト・クラスから一度起動すると、MAFアプリケーションで保留中のすべてのデータ同期アクションに対して自動同期が実行されます。synchronizeのブール引数によって、同期がバックグラウンドで行われるか(true
)、フォアグラウンドで行われるか(false
)が決まります。お薦めしませんが、MAFアプリケーションのデフォルトの動作を無効にして、RESTコールを起動する前に保留中の同期アクションを同期することもできます。詳細は、「自動同期の無効化に関する必知事項」を参照してください。
MAFアプリケーションでは、オンライン・モードに戻ってデータを同期したときに、データ同期の競合が発生するかどうかを検出することはできません。たとえば、MAFアプリケーションがオフライン・モードのときに、MAFアプリケーションのエンド・ユーザーが部門を更新したとします。別の場所で、同じデータ・セットにアクセスするWebアプリケーションの別のユーザーが同じ部門情報を変更します。MAFでは、この後者の変更を検出できません。MAFアプリケーションがオンラインに戻ったときに、MAFはPENDING_SYNCH_ACTIONS
表での変更を同期しようとします。今説明した問題を解決し、回避するには、MAFアプリケーションがアクセスするデータ・セットにすべてのアプリケーション(モバイルやWebなど)がアクセスする場所でデータ同期の競合を特定して解決する必要があります。
MAFが対応するRESTリソースをコールして保留中の同期アクションを同期しようとしたときに、サーバーが停止しているなどの理由で、RESTコールがアクションに対してエラー・レスポンスを返すことがあります。そうなった場合、MAFはPENDING_SYNCH_ACTIONS
表にデータ同期アクションを保持します。また、同期試行および同期エラーのタイムスタンプでアクションを更新します。1つ以上のアクションが失敗しても、MAFは表にある残りのデータ同期アクションの処理を続行します。MAFは、次回同期を実行するときに、これらの保留中のデータ同期アクションを再試行します。これらの保留中の同期アクションをエンド・ユーザーに公開して、まだ同期されていないアクションを表示してどのように対処するかを決定できるようにすることができます。詳細は、「保留中の同期アクションを表示する方法」を参照してください。また、同期アクションの完了時にMAFアプリケーションで実行するカスタム・ロジックを記述することもできます。たとえば、同期の失敗に応じて実行するコードを記述できます。詳細は、「失敗した同期アクションを処理するためのカスタム・ロジックを追加する方法」を参照してください。
エンド・ユーザーが保留中の同期アクションを表示および削除できるアプリケーション機能を表示するには、MAFアプリケーションにDataSyncFeature.jar
機能アーカイブを追加します。
追加すると、保留中の同期アクションを表示するためのメニューを備えたアプリケーション機能がMAFアプリケーションに組み込まれます。エンド・ユーザーは、各保留中の同期アクションをタップして、詳細を表示し、アクションを削除するか、またはMAFが同期を再試行するまでそのままにしておくかを決定できます。図6-23は、この機能アーカイブが追加されたMAFアプリケーションのメニュー・エントリと保留中の同期アクション画面の合成イメージを示しています。
図6-23 保留中の同期アクションの表示
注意:
「MAFユーザー・インタフェース・ジェネレータ」ウィザードは、完了時にこのFARをMAFアプリケーションに自動的に追加します。詳細は、「MAFクライアント・データ・モデルに基づくユーザー・インタフェースの作成」を参照してください。DataSyncFeature.jar
FARを追加するには:
メイン・メニューで、「アプリケーション」および「アプリケーション・プロパティ」を選択します。
「アプリケーション・プロパティ」ダイアログで、「ライブラリとクラスパス」ページに移動し、「JAR/ディレクトリの追加」をクリックします。
表示された「アーカイブまたはディレクトリの追加」ダイアログで、JDeveloperインストールのjdeveloper/jdev/extensions/oracle.maf/FARs/CDM
ディレクトリに移動し、MAFアプリケーションのクラスパス・エントリの下に表示されるようにDataSynchFeature.jar
ファイルを選択します。
「OK」をクリックしてダイアログを閉じます。
maf-application.xml
ファイルの概要エディタの「機能参照」ページで、図6-24に示すように、機能参照の挿入ダイアログからデータ同期機能を選択してアプリケーションに追加します。
図6-24 保留中の同期アクションを表示するためのFARの追加
失敗した同期アクションをプログラムにより処理する場合は、データ同期が完了したらMAFアプリケーションで実行するカスタム・コードを記述します。
MAFアプリケーションでJavaコードを記述して登録するには、次の手順を実行します。
oracle.maf.impl.cdm.persistence.service.DataSynchManager
から拡張するJavaクラスを作成します。
データ同期アクションの完了後にカスタム・ロジックを追加するように、dataSynchFinished
メソッドをオーバーライドします。
protected void dataSynchFinished(java.util.List<DataSynchAction> succeededDataSynchActions, java.util.List<DataSynchAction> failedDataSynchActions)
dataSynchFinished
メソッドには、引数が2つあります。正常に完了した同期アクションのリストと、失敗した同期アクションのリストです。このメソッドを使用すると、たとえば、1つ以上のトランザクションが失敗し、後で再試行が必要であることをエンド・ユーザーに警告したり、すべての保留中のデータ同期アクションが正常に処理されたことを通知できます。
同期ポリシーを導入するためのJavaコードをこのメソッドに追加します。次の簡単な例では、正常に完了した同期アクションと失敗した同期アクションの数をユーザーに通知しています。
package application.model.service; import java.util.List; import oracle.adfmf.framework.exception.AdfException; import oracle.maf.impl.cdm.util.MessageUtils; import oracle.maf.api.cdm.persistence.service.DataSynchAction; import oracle.maf.impl.cdm.persistence.service.DataSynchManager; public class MyDataSynchManager extends DataSynchManager { public DataSynchManager() { super(); } @Override protected void dataSynchFinished(List<DataSynchAction> succeededDataSynchActions, List<DataSynchAction> failedDataSynchActions) { int ok = succeededDataSynchActions.size(); int fails = failedDataSynchActions.size(); int total = ok + fails; MessageUtils.handleMessage(AdfException.INFO, total + " data synch actions completed. Successful: " + ok + ", Failed: " + fails); } }
次の例に示すように、アプリケーションのmobile-persistence-config.properties
ファイルにクラスを登録します。
datasync.manager.class=application.model.service.MyDataSynchManager
mobile-persistence-config.properties
ファイルは、ApplicationRootDirectory/ApplicationController/src/META-INF/
フォルダにあります。
MAFアプリケーションからのトランザクションの自動同期を無効にできます。
MAFアプリケーションからのトランザクションの自動同期を無効にするには:
oracle.maf.impl.cdm.persistence.service.EntityCRUDService
を拡張する新しい抽象Javaクラスを作成します。
synchronize
メソッドが何も実行しないように、このメソッドをオーバーライドし、super.synchronize
へのコールをコメント・アウトします。
EntityCRUDService
から拡張するのではなく、先ほど作成したサブクラスから拡張するように、サービス・オブジェクト・クラスを変更します。
自動同期を無効にする場合は、エンド・ユーザーが同期アクションを明示的に起動できるようにMAFアプリケーションを構成します。データ同期を明示的にトリガーするには、次の文を使用します。
new DataSynchService().synchronize(true);
ブール引数によって、同期がバックグラウンドで行われるか(true
)、フォアグラウンドで行われるか(false
)が決まります。DataSynchService
クラスは、oracle.maf.api.cdm.persistence.service
パッケージにあります。詳細は、Oracle Mobile Application Framework Java APIリファレンスを参照してください。
トランザクションの自動同期を無効にすると、古くなったデータがアプリケーションに存在し、ユーザー操作性が混乱する可能性があるため、無効にしないことをお薦めします。次の例のユーザー(John)のユースケースは、この点を示しています。Johnは、自分のアプリケーションで次のアクションを実行します。
アプリケーションをオンライン・モードで起動するときに、部門の最新のリストを取得します。
オフライン・モードで部門10の名前を変更します。
オフライン・モードで部門20
を削除します。
オフライン・モードで新規部門280
を作成します。
アプリケーションを終了します。
Johnは、再度アプリケーションをオンライン・モードで起動し、部門の最新のリストをサーバーから取得しますが、3個の保留中の同期アクションがまだ処理されていません。
Johnには現在、部門10
の古い部門名が表示されています。
Johnは部門20
をすでに削除しましたが、現在再び表示されています。
MAFアプリケーション開発者がデータ・オブジェクトの「すべてのリソースの検索」に対する「ローカル行の削除」チェック・ボックスを選択した場合には、Johnが作成した新規部門(280
)は二度と表示されません。
Johnが同期アクションを手動で起動した場合にのみ、(ユーザー・インタフェース・コントロールまたはアプリケーションの再起動によって)サーバーから部門リストを最新のデータで再びリフレッシュすると、オフライン・モードで加えた変更を含む最新のデータが再び表示されます。
基礎となるデータ・コレクションでのデータ変更イベントに応えてMAFアプリケーションのユーザー・インタフェースをリフレッシュするためにMAFクライアント・データ・モデルで使用されるAPIを説明します。
MAFクライアント・データ・モデルによって生成されるデータおよびサービス・クラスは、MAFでサポートされている両方のタイプの変更イベント(プロパティおよびプロバイダ)ですぐに利用できます。これは、このようなタイプのクラスの拡張元のEntity
およびEntityCRUDService
クラスが、両方ともChangeEventSupportable
クラスから拡張されるためです。MAFでサポートされているデータ変更イベントの詳細は、データ変更イベントの使用を参照してください。
データまたはサービス・クラスからデータ変更イベントを送信する必要がある場合は、対応するゲッター・メソッドを呼び出して、変更イベントに送信するインスタンスを取得します。
getPropertyChangeSupport()
getProviderChangeSupport()
データまたはサービス・クラスにpropertyChangeSupport
またはproviderChangeSupport
の独自のインスタンスを含めるのではなく、必ず前述のゲッター・メソッドを使用してください。これにより、MAFアプリケーションのユーザー・インタフェースをリフレッシュするためにMAFクライアント・データ・モデルで使用される、組込みのランタイム・コードが壊れることを防ぎます。
デフォルトでは、各サービス・クラスには、データ・オブジェクトのリストを返すゲッター・メソッドがあります。たとえば、DepartmentService
クラスには、次のような生成されたメソッドが含まれています。
public List<Department> getDepartment() { return getEntityList(); }
MAFアプリケーションでRESTコールを行い、最新の部門リストを取得すると、MAFによって、サービス・クラスのEntityCRUDService
スーパークラスからsetEntityList
メソッドを呼び出すことで自動的にユーザー・インタフェースがリフレッシュされます。このメソッドにより、fireProviderRefresh
メソッド・コールで使用されるプロパティ名を見つけるために、サービス・クラス内の別の生成済メソッド(getEntityListName
)が呼び出されます。次の例では、DepartmentService
クラスの、生成されたgetEntityListName
メソッドを示します。
protected String getEntityListName() { return "department"; }
前述の実装は、次のことを意味します。
部門リストのコンテンツを変更するカスタム・ロジックを記述する場合は、setEntityList
を呼び出して、コンテンツ変更内容でユーザー・インタフェースをリフレッシュできます。
生成されたメソッドgetDepartment
の名前をより適切な複数形名getDepartments
に変更する場合は、メソッドgetEntityListName
を、department
ではなくdepartments
を返すよう変更する必要もあります。そうしないと、標準のMAFクライアント・データ・モデル・リフレッシュ・コードが機能しなくなります。
データ・オブジェクト・インスタンスのセットに複数のフィルタされたビューを提供するよう、サービス・クラスに独自のゲッター・リスト・メソッドを追加した場合は、RESTコールの完了時にユーザー・インタフェースにおいてフィルタ済リストもリフレッシュされるよう、setEntityList
メソッドをオーバーライドできます。詳細は、フィルタされたエンティティ・リストの使用を参照してください。
データ変更イベントへのレスポンスでのフォームのリフレッシュ
データ・オブジェクト・インスタンスが1つのみ表示されるフォーム・レイアウト内のユーザー・インタフェースをリフレッシュするには、プロパティ変更イベントを使用する必要があります。これは、プロバイダ変更イベントではリスト・ビューのみがリフレッシュされるためです。各データ・クラスには、前述のようにプロパティ変更イベントを送信するためにPropertyChangeSupport
インスタンスが含まれますが、データ・オブジェクト・インスタンス内の生成されたセッター・メソッドでは、デフォルトでは変更イベントは送信されません。getPropertyChangeSupport()
メソッドを使用しさえすれば、自分でこのコードを追加できます。
public void setName(String name) { String oldValue = this.name; this.name = name; getPropertyChangeSupport().firePropertyChange("name", oldValue, name);; }
データ・オブジェクト・インスタンスのすべてのプロパティ(属性)をリフレッシュする必要がある場合は、MAFの便利なメソッドEntityUtils.refreshEntity
を使用することもできます。これは、その唯一の引数としてデータ・オブジェクト・インスタンスを取ります。
バックグラウンド・スレッドでのデータ変更の送信
バックグラウンド・スレッドでデータ変更イベントを送信する場合、AdfmfJavaUtilities.flushDataChangeEvent
を呼び出すことで、これらのデータ変更イベントをユーザー・インタフェース・レイヤーにフラッシュする必要があります。さらに、MAFでは、バックグラウンド・スレッドでのデータ変更イベントの送信時のデッドロックを防止するために、MafExecutorService
を使用することが求められます。次の例では、MafExecutorService
から実行メソッドを使用して、データ変更イベントを送信し、完了後にイベントをフラッシュする方法を示します。次の例では、Java 8 Lambda式を使用してコードを渡します。
public void setName(String name) { String oldValue = this.name; this.name = name; if (AdfmfJavaUtilities.isBackgroundThread()) { MafExecutorService.execute(() -> { getPropertyChangeSupport().firePropertyChange("name", oldValue, name); AdfmfJavaUtilities.flushDataChangeEvent(); }); } else { getPropertyChangeSupport().firePropertyChange("name", oldValue, name); } }
リフレッシュする属性ごとに前述のコードを記述するかわりに、MAFクライアント・データ・モデルのEntity
クラスによって提供されるrefreshUI
メソッドを使用して、プロパティ変更イベントを送信できます。このメソッドは、次の例で示すように、データ変更イベントを送信する属性のリストを取ります。
List<String> attrs = new ArrayList<String>(); attrs.add("name"); attrs.add("managerId"); refreshUI(attrs);
refreshUI
メソッドは、前述のMafExecutorService
を使用し、バックグラウンド・スレッドでの実行時にデータ変更イベントをフラッシュします。
子データ・コレクションからのデータ変更によるユーザー・インタフェースのリフレッシュ
デフォルトでは、親データ・コレクションを返すRESTコールがMAFアプリケーションで実行されるときに、MAFクライアント・データ・モデルで子データ・コレクションへのデータ変更によりユーザー・インタフェースがリフレッシュされることはありません。このデフォルト動作により、RESTコールをトリガーするページに親データ・オブジェクトしか表示される可能性がないときに、子データ・オブジェクトがデータベースから読み取られてメモリーに読み込まれることがなくなるため、アプリケーションのパフォーマンスが最適化されます。データ・オブジェクトのクラス内のEntity
クラスからrefreshUI
メソッドをオーバーライドすることで、子データ・コレクションを含むようリフレッシュを拡張できます。次の例では、従業員の子データ・コレクションをリフレッシュする必要があるDepartment
クラス内でこれを行う方法を示します。
@Override public void refreshUI(List attrsToRefresh) { getProviderChangeSupport().fireProviderRefresh("employees"); super.refreshUI(attrsToRefresh); }
MAFでは、MAFアプリケーションを、それが実行されているデバイスがインターネットへの接続時にオフラインであるかのように構成できます。
これは、低速なネットワーク接続でのRESTコールを防止するために役立ちます。たとえば、それを使用することで、RESTコールを3G接続では防止し、Wi-Fi接続では許可するようにできます。
オフライン・モードを強制するには、次のメソッドを使用します。
new ConnectivityBean().setForceOffline(true);
注意:
ConnectivityBean
の新しいインスタンスを作成すると同時に、forceOffline
フラグの値が、すべてのインスタンスにわたり共有される、静的変数に保存されます。これにより、ConnectivityBean
クラスをマネージドBeanとして使用して、ユーザー・インタフェースからオフライン・モードを強制/強制解除できるようになります。これは、MAFのデータ同期機能を示すデモで役立つ可能性があります。これを行うには、次のように、adfc-mobile-config.xml
ファイル内でマネージドBeanを定義します。
<managed-bean id="__3"> <managed-bean-name>Connectivity</managed-bean-name> <managed-bean-class>oracle.maf.api.cdm.controller.bean.ConnectivityBean</managed-bean-class> <managed-bean-scope>application</managed-bean-scope> </managed-bean>
その後、次のように、AMXページにオフライン強制トグル・オプションを追加できます。
<amx:commandLink id="menFo" text="#{Connectivity.forceOffline ? 'Unforce offline' : 'Force offline'}"> <amx:setPropertyListener id="menfospl" from="#{!Connectivity.forceOffline}" to="#{Connectivity.forceOffline}"/> </amx:commandLink>
MAFユーザー・インタフェース・ジェネレータを使用する方法で説明したとおり、「MAFユーザー・インタフェース・ジェネレータ」ウィザードで、図6-25に示すようにこのオプションを表示するメニュー・エントリを生成します。
図6-25 MAFユーザー・インタフェース・ジェネレータからのオフライン強制メニュー・エントリ
ネットワーク接続の程度に基づいてオフライン・モードを強制する必要がある場合は、MAFに事前にインストールされている、Cordovaネットワーク・プラグインを使用して、ConnectivityBean.forceOffline
メソッドを呼び出すJavaScriptコールバック・ハンドラを設定します。
エンド・ユーザーが自分のMAFアプリケーションでバックグラウンド・タスクが実行されていることを把握できるようエンド・ユーザーに対してビジュアル・インジケータをレンダリングする方法を説明します。
MAFアプリケーションでスレッド・プール内の1つ以上のタスクが実行されている場合、MAFでは、Booleanフラグを保持する、スレッド・プールを介したすべてのバックグラウンドRESTコールでTrue
が返されるようにします。このBooleanフラグの値にアクセスするには、次のEL式を使用します。
#{applicationScope.maf_bgtask_running}
MAFユーザー・インタフェース・ジェネレータを使用する方法で説明したとおり、「MAFユーザー・インタフェース・ジェネレータ」ウィザードにより、MAFアプリケーションでバックグラウンドでタスクが処理されるときに図6-26に示すようにビジュアル・インジケータを表示するよう作成したAMXページに次のエントリを生成します。
<amx:image id="bgRunImg" source="/images/reloading.gif" inlineStyle="margin-right:5px;" rendered="#{applicationScope.maf_bgtask_running}"/>
図6-26 MAFユーザー・インタフェース・ジェネレータからのバックグラウンド・タスクのためのビジュアル・インジケータ