中級
リレーショナルデータベース内のデータを管理する LiveCycle Data Services アプリケーションをプログラムで作成できます。Adobe LiveCycle Data Services データ管理サービスは、AdobeFlex クライアントアプリケーションと中間層との間のデータ同期を自動化します。データ同期、オンデマンドデータページング、臨時に接続されるアプリケーションサービスを提供するアプリケーションを構築できます。また、大規模なデータコレクションや、1 対 1 や多対 1 の関連付けなど、ネストされたデータの関係を管理することもできます。
この記事で開発されたアプリケーションは、サーバーサイド Java クラスを手動で作成します。つまり、Assembler クラスなどのクラスは、Java IDE を使用して作成され、LiveCycle Data Services をホストするアプリケーションサーバーにデプロイされます。サーバーサイドクラスを手動で作成する代わりに、アドビのモデリング技術を使用できます。アドビのモデリング技術を使用すると、Java IDE を使用して手動で作成することなく、サーバーサイド Java クラスを作成できます。生成される Java クラスは、モデルに基づいています。モデリングテクノロジーを使用する利点の 1 つは、必要な Java プログラミングの量を減らすことです。同様に、データ管理アプリケーションを手動で作成する利点は、サーバーサイドクラスをより細かく制御できることです。例えば、既存のカスタム Java DAO アプリケーションロジックを使用できます。
サーバーサイド Java クラスを作成するだけでなく、Flash Builder4 を使用して構築されたクライアントアプリケーションのアプリケーションロジックを作成することもできます。次の図は、Flash Builder を使用して構築されたサーバーサイド Java クラスとクライアントアプリケーションを視覚的に表したものです。
手順 | 説明 |
1 | Flash Builder を使用して構築されたクライアントアプリケーションは、Assembler クラスによって公開された操作を使用します。例えば、Assembler クラスの Fill メソッドは、特定のテーブル内のすべてのデータを返します。データは、データグリッドコントロールなどのコントロールに表示できます。(このクライアントアプリケーションの図をこの表の後に示します)。 |
2 | サーバーサイド Java クラスが、LiveCycle Data Services をホストするアプリケーションサーバーにデプロイされます。Data Services をホストするサーバーにデプロイする Data Service クラスを手動で作成できます。 |
3 | Java サーバーサイドクラスは、リレーショナルデータベースに対してデータベース操作を実行します。例えば、データを取得して、すべてのクライアントアプリケーションにプッシュできます。 |
この記事で使用されるデータベースは、Product テーブルに基づいています。この表は、LiveCycle Data Services が使用できるサンプルデータベースにあります。Product テーブルは、次のフィールドで構成されます。
- PRODUCT_ID:このアイテムの一意の ID 値を指定します(このフィールドは PK として機能します)。
- NAME:製品名を指定します。
- CATEGORY:製品が属するカテゴリを指定します。
- PRICE:アイテムの価格を格納します
- IMAGE:アイテムを表示する画像ファイルを指定します
- DESCRIPTION:アイテムの説明を指定します
- QTY_IN_STOCK:アイテムの在庫数を指定します
次の図に、Product テーブル内のレコードを表示する Flash Builder を使用して構築されたクライアントアプリケーションを示します。この開発記事では、このクライアントアプリケーションの作成方法を順を追って説明します。
Eclipse などの Java IDE で Java サーバーサイドクラスを作成します。LiveCycle Data Services JAR ファイルをプロジェクトのクラスパスに追加します。次のディレクトリにある flex-messaging-*.jar ファイルを含めます。
[Install Directory]/lcds/tomcat/webapps/lcds-samples/WEB-INF/lib
[Install Directory] は、LiveCycle Data Services のインストール場所です。次のサーバーサイド Java クラスが作成されます。
- Product:Product テーブル内のデータを表します
- ProductAssembler:Assembler クラスを表します。このクラスは必須の LiveCycle Data Services クラスです
- Connection:データベース接続を表します
- DAOException:データベースエラーが発生した場合にスローされる例外を表します
- ProductDAO:データベース操作を実行するアプリケーションロジックを表します。
注意:
この開発記事のこれらのクラスはすべて、LiveCycle Data Services をホストするサーバーに作成され、デプロイされます。
Product クラスは、Product テーブル内のデータを表します。Product クラスには、Product テーブルの各フィールドのデータメンバーが含まれます。データメンバーのデータ値は、フィールドのデータ型と一致します。つまり、データタイプが VarChar である Description フィールドについて考えてみましょう。
次の例に示すように、Product クラスの Description データメンバーのデータ型は String です。
private String description ;
Product クラスの残りの部分は、各データメンバーの setter メソッドと getter メソッドで構成されます。Product クラスは次のインターフェイスを実装します
java.io.Serializable
次のコード例は、Product クラスを表しています。
package flex.samples.product; import java.io.Serializable; public class Product implements Serializable { static final long serialVersionUID = 103844514947365244L; private int productId; private String name; private String description; private String image; private String category; private double price; private int qtyInStock; public Product() { } public Product(int productId, String name, String description, String image, String category, double price, int qtyInStock) { this.productId = productId; this.name = name; this.description = description; this.image = image; this.category = category; this.price = price; this.qtyInStock = qtyInStock; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getImage() { return image; } public void setImage(String image) { this.image = image; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getProductId() { return productId; } public void setProductId(int productId) { this.productId = productId; } public int getQtyInStock() { return qtyInStock; } public void setQtyInStock(int qtyInStock) { this.qtyInStock = qtyInStock; } }
ProductAssembler クラスは、flex.data.assemblers.AbstractAssembler クラスを継承し、クライアントアプリケーションで使用されるメソッドを含みます。アセンブラークラスには、少なくとも fill メソッドが含まれます。このメソッドはクエリを実装します。各メソッドは、オブジェクトのコレクションをクライアントアプリケーションに返します。クライアントアプリケーションがオブジェクトを更新する場合、アセンブラークラスには sync メソッド、または個々の create、update、delete メソッドを含める必要があります。
この開発記事で作成したクライアントアプリケーションは、ProductAssembler クラス内の fill メソッドを呼び出します。fill メソッドは、java.util.Listinstance を入力パラメーターとして受け付けます。Product テーブル内のデータを表す java.util.Collection が返されます。
通常、Assembler クラスにあるメソッドを直接呼び出す必要はありません。ただし、fill メソッドは例外です。つまり、クライアントアプリケーションがこのメソッドを呼び出します。このクラスにある create、update、delete メソッドを呼び出すことはできますが、LiveCycle Data Services がアイテムのコレクションを管理するので、このメソッドは不要です。つまり、更新操作を実行して、コレクション内のアイテムを操作できます。変更は、自動的にコミット(autoCommit=true の場合。デフォルト)されるか、または commit メソッドの呼び出し時に変更がサーバーに送信されます。
LiveCycle Data Services は、コレクション内のアイテムに対する変更を追跡します。変更は、revertChanges メソッドに応じてサーバーにコミットされるか、ロールバックされます。このアクションは、autoCommit フラグの設定(デフォルトでは true)に依存します。
次のコード例は、ProductAssembler クラスを表しています。
package flex.samples.product; import java.util.List; import java.util.Map; import java.util.Collection; import flex.data.DataSyncException; import flex.data.assemblers.AbstractAssembler; public class ProductAssembler extends AbstractAssembler { public Collection fill(List fillArgs, PropertySpecifier ps) { try { ProductDAO service = new ProductDAO(); return service.getProducts(); } catch (Exception e) { e.printStackTrace(); } return null; } public Object getItem(Map identity) { ProductDAO service = new ProductDAO(); return service.getProduct(((Integer) identity.get("productId")).intValue()); } public void createItem(Object item) { ProductDAO service = new ProductDAO(); service.create((Product) item); } public void updateItem(Object newVersion, Object prevVersion, List changes) { ProductDAO service = new ProductDAO(); boolean success = service.update((Product) newVersion); if (!success) { int productId = ((Product) newVersion).getProductId(); throw new DataSyncException(service.getProduct(productId), changes); } } public void deleteItem(Object item) { ProductDAO service = new ProductDAO(); boolean success = service.delete((Product) item); if (!success) { int productId = ((Product) item).getProductId(); throw new DataSyncException(service.getProduct(productId), null); } } }
Connection クラスは、データベース接続を表します。このクラスは、java.sql.* パッケージにある Java クラスを使用します。次のコードは、Connection クラスを表しています。このクラスは、flex.samples という名前のパッケージにあります。このクラスのインスタンスは、ProductDAO クラスに作成され、データベースへの接続を作成します。
package flex.samples; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class ConnectionHelper { private String url; private static ConnectionHelper instance; private ConnectionHelper() { try { Class.forName("org.hsqldb.jdbcDriver"); url = "jdbc:hsqldb:hsql://localhost:9002/flexdemodb"; } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { if (instance == null) { instance = new ConnectionHelper(); } try { return DriverManager.getConnection(instance.url); } catch (SQLException e) { throw e; } } public static void close(Connection connection) { try { if (connection != null) { connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } }
DAOException クラスは、エラーが発生した場合にスローされる例外を表します。このクラスは、flex.samples という名前のパッケージにあります。次の Java コードは、DAOException クラスを表しています。
package flex.samples; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class ConnectionHelper { private String url; private static ConnectionHelper instance; private ConnectionHelper() { try { Class.forName("org.hsqldb.jdbcDriver"); url = "jdbc:hsqldb:hsql://localhost:9002/flexdemodb"; } catch (Exception e) { e.printStackTrace(); }
ProductDAO クラスには、データベース操作を実行する Java アプリケーションロジックが含まれます。Assembler クラスのメソッドは、操作を実行するために、このクラスにあるメソッドを呼び出します。このクラスで SQL コマンドを使用して、データベース操作を実行できます。例えば、次の Java アプリケーションロジックは、Product テーブルからデータを取得する getProducts というメソッドを表します。
public List getProducts() throws DAOException { List list = new ArrayList(); Connection c = null; try { c = ConnectionHelper.getConnection(); Statement s = c.createStatement(); ResultSet rs = s.executeQuery("SELECT * FROM product ORDER BY name"); while (rs.next()) { list.add(new Product(rs.getInt("product_id"), rs.getString("name"), rs.getString("description"), rs.getString("image"), rs.getString("category"), rs.getDouble("price"), rs.getInt("qty_in_stock"))); } } catch (SQLException e) { e.printStackTrace(); throw new DAOException(e); } finally { ConnectionHelper.close(c); } return list;
ConnectionHelper.getConnection メソッドは、データベース接続を表す Connection のインスタンスを返します。SQL 文を使用して、Product テーブルからすべてのフィールドを取得します。レコードは名前順に並べられ、結果は java.util.Listinstance に配置されます。ProductAssembler クラスにある fill メソッドは、getProducts メソッドを呼び出します。
ProductDAO クラスには次のメソッドが含まれます。
- getProducts:Product テーブル内のデータを返します。
- getProductsByName:アイテムの名前に基づいてデータを返します。
- getProduct::アイテムの一意の ID 値に基づいてデータを返します。
- create:レコードを作成します。
- update:既存のレコードを更新します。
- remove:アイテムをデータベースから削除します。
次のコードの例は、ProductDAO クラスを表しています。
package flex.samples.product; import java.util.ArrayList; import java.util.List; import java.sql.*; import flex.samples.ConnectionHelper; import flex.samples.DAOException; public class ProductDAO { public List getProducts() throws DAOException { List list = new ArrayList(); Connection c = null; try { c = ConnectionHelper.getConnection(); Statement s = c.createStatement(); ResultSet rs = s.executeQuery("SELECT * FROM product ORDER BY name"); while (rs.next()) { list.add(new Product(rs.getInt("product_id"), rs.getString("name"), rs.getString("description"), rs.getString("image"), rs.getString("category"), rs.getDouble("price"), rs.getInt("qty_in_stock"))); } } catch (SQLException e) { e.printStackTrace(); throw new DAOException(e); } finally { ConnectionHelper.close(c); } return list; } public List getProductsByName(String name) throws DAOException { List list = new ArrayList(); Connection c = null; try { c = ConnectionHelper.getConnection(); PreparedStatement ps = c.prepareStatement("SELECT * FROM product WHERE UPPER(name) LIKE ? ORDER BY name"); ps.setString(1, "%" + name.toUpperCase() + "%"); ResultSet rs = ps.executeQuery(); while (rs.next()) { list.add(new Product(rs.getInt("product_id"), rs.getString("name"), rs.getString("description"), rs.getString("image"), rs.getString("category"), rs.getDouble("price"), rs.getInt("qty_in_stock"))); } } catch (SQLException e) { e.printStackTrace(); throw new DAOException(e); } finally { ConnectionHelper.close(c); } return list; } public Product getProduct(int productId) throws DAOException { Product product = new Product(); Connection c = null; try { c = ConnectionHelper.getConnection(); PreparedStatement ps = c.prepareStatement("SELECT * FROM product WHERE product_id=?"); ps.setInt(1, productId); ResultSet rs = ps.executeQuery(); if (rs.next()) { product = new Product(); product.setProductId(rs.getInt("product_id")); product.setName(rs.getString("name")); product.setDescription(rs.getString("description")); product.setImage(rs.getString("image")); product.setCategory(rs.getString("category")); product.setPrice(rs.getDouble("price")); product.setQtyInStock(rs.getInt("qty_in_stock")); } } catch (Exception e) { e.printStackTrace(); throw new DAOException(e); } finally { ConnectionHelper.close(c); } return product; } public Product create(Product product) throws DAOException { Connection c = null; PreparedStatement ps = null; try { c = ConnectionHelper.getConnection(); ps = c.prepareStatement("INSERT INTO product (name, description, image, category, price, qty_in_stock) VALUES (?, ?, ?, ?, ?, ?)"); ps.setString(1, product.getName()); ps.setString(2, product.getDescription()); ps.setString(3, product.getImage()); ps.setString(4, product.getCategory()); ps.setDouble(5, product.getPrice()); ps.setInt(6, product.getQtyInStock()); ps.executeUpdate(); Statement s = c.createStatement(); // HSQLDB Syntax to get the identity (company_id) of inserted row ResultSet rs = s.executeQuery("CALL IDENTITY()"); // MySQL Syntax to get the identity (product_id) of inserted row // ResultSet rs = s.executeQuery("SELECT LAST_INSERT_ID()"); rs.next(); // Update the id in the returned object. This is important as // this value must get returned to the client. product.setProductId(rs.getInt(1)); } catch (Exception e) { e.printStackTrace(); throw new DAOException(e); } finally { ConnectionHelper.close(c); } return product; } public boolean update(Product product) throws DAOException { Connection c = null; try { c = ConnectionHelper.getConnection(); PreparedStatement ps = c.prepareStatement("UPDATE product SET name=?, description=?, image=?, category=?, price=?, qty_in_stock=? WHERE product_id=?"); ps.setString(1, product.getName()); ps.setString(2, product.getDescription()); ps.setString(3, product.getImage()); ps.setString(4, product.getCategory()); ps.setDouble(5, product.getPrice()); ps.setInt(6, product.getQtyInStock()); ps.setInt(7, product.getProductId()); return (ps.executeUpdate() == 1); } catch (SQLException e) { e.printStackTrace(); throw new DAOException(e); } finally { ConnectionHelper.close(c); } } public boolean remove(Product product) throws DAOException { Connection c = null; try { c = ConnectionHelper.getConnection(); PreparedStatement ps = c.prepareStatement("DELETE FROM product WHERE product_id=?"); ps.setInt(1, product.getProductId()); int count = ps.executeUpdate(); return (count == 1); } catch (Exception e) { e.printStackTrace(); throw new DAOException(e); } finally { ConnectionHelper.close(c); } } public boolean delete(Product product) throws DAOException { return remove(product); } }
Java サーバーサイドクラスを作成したら、それらを LiveCycle Data Services をホストするサーバーにデプロイします。Java IDE で Java クラスをコンパイルし、.CLASS ファイルをデプロイします。デプロイ先は、アプリケーションの名前に応じて異なります。この開発記事では、アプリケーションの名前が lcds-samples であると想定しています。Product.class、ProductAssembler.class、ProductDAO.class はすべて flex.samples.product パッケージの一部なので、これらのクラスファイルを次のフォルダーに配置します。
[Install Directory]\lcds\tomcat\webapps\lcds-samples\WEB-INF\classes\flex\samples\product
Connection.class と DAOException.class は flex.samples パッケージに属しているので、これらのクラスを次のフォルダーに配置します。
[Install Directory]\lcds\tomcat\webapps\lcds-samples\WEB-INF\classes\flex\samples
Java サーバーサイドクラスを正しく使用するには、data-management-config.xml ファイルを設定します。このファイル内に、データ管理サービスの宛先を作成します。宛先は、データを交換してクライアントアプリケーションでデータの配布と同期機能を提供するために使用するエンドポイントです。宛先はクライアントアプリケーションで参照されます。
デフォルトでは、data-management-config.xml ファイルは、LiveCycle Data Services を含む Web アプリケーションの WEB_INF/flex ディレクトリにあります。data-management-config.xml ファイルは、最上位レベルの services-config.xml ファイルで参照されます。次の例は、inventory という名前の宛先を示しています。ソース要素が flex.samples.product.ProductAssembler を参照していることに注意してください。同様に、item-class 要素は flex.samples.product.Product を参照します。
<destination id="inventory"> <properties> <source>flex.samples.product.ProductAssembler</source> <scope>application</scope> <item-class>flex.samples.product.Product</item-class> <metadata> <identity property="productId"/> </metadata> <network> <paging enabled="false" pageSize="10"/> </network> </properties> </destination>
Application scope は、すべてのクライアントアプリケーションに対してアセンブラーのインスタンスが 1 つだけ存在することを意味します。identity property 要素は、Product クラスのデータメンバーをマッピングします。データ管理を使用する場合、すべての永続的エンティティに、各管理対象インスタンスを一意に識別する 1 つ以上の identity property が必要です。
ページングを使用すると、クエリ時間を短縮し、コンシューマーメモリの量を減らすことで、アプリケーションのパフォーマンスを向上できます。LiveCycle Data Services は、クライアントツーサーバーページング、サーバーツーデータソースページング、アソシエーションページングの 3 種類のページングをサポートしています。この例では、ページングは有効になっていません。
J2EE アプリケーションサーバーをホストする LiveCycle Data Services を開始します。
- 「開始」をクリックし、「すべてのプログラム」、「Adobe」、「LiveCycle Data Services ES3.1」、「LiveCycle Data Services サーバーを開始する」をクリックします。
startdb.bat を実行して、サンプルデータベースを開始します。このファイルは次のディレクトリにあります。[Install directory]lcds3/sampledb
クライアントアプリケーションを作成するために使用する、Flash Builder プロジェクトを作成します。このプロジェクトでは、J2EE アプリケーションサーバーをホストする LiveCycle Data Services を参照します。つまり、プロジェクトを作成する場合、Application Server の種類として J2EE を選択し、アプリケーションサーバーとして LiveCycle Data Services を選択します。データソースを作成したコンテキストルートをプロジェクトが参照していることを確認します。プロジェクトを作成すると、J2EE アプリケーションサーバーとのやり取りに必要なすべてのクライアントライブラリが、プロジェクトのクラスパスに自動的に追加されます。
次の MXML コードは、データグリッドを作成します。ユーザーが「データを取得」ボタンをクリックすると、ProductAssembler クラスで定義されている fill メソッドが呼び出されます。宛先プロパティに inventory が割り当てられ、data-management-config.xml ファイルで定義されている宛先を参照します。
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" backgroundColor="#FFFFFF"> <mx:ArrayCollection id="products"/> <mx:DataService id="ds" destination="inventory"/> <mx:DataGrid dataProvider="{products}" editable="true" width="100%" height="100%"> <mx:columns> <mx:DataGridColumn dataField="name" headerText="Name"/> <mx:DataGridColumn dataField="category" headerText="Category"/> <mx:DataGridColumn dataField="price" headerText="Price"/> <mx:DataGridColumn dataField="image" headerText="Image"/> <mx:DataGridColumn dataField="description" headerText="Description"/> </mx:columns> </mx:DataGrid> <mx:Button label="Get Data" click="ds.fill(products)"/> </mx:Application>
また、Product テーブル内のデータを表す ProductActionScript クラスを作成する必要もあります。Product.as ファイルは、メインの MXML ファイルと同じパッケージ内にあります。次の ActionScript クラスは、Product.as ファイルを表しています。
package { [Managed] [RemoteClass(alias="flex.samples.product.Product")] public class Product { public function Product() { } public var productId:int; public var name:String; public var description:String; public var image:String; public var category:String; public var price:Number; public var qtyInStock:int; } }
RemoteClass タグは flex.samples.product.Product を参照していることに注意してください 。flex.samples.product.Product クラスで定義されているすべてのデータメンバーは、Product.as クラスで定義されています。また、Product.as で定義されるデータ型は、flex.samples.product.Product で定義されるデータメンバーと一致しています。
Flash Builder4 を使用してクライアントアプリケーションを作成するには、次の手順を実行します。
- Flash Builder4 を開始するには、「開始」、「すべてのプログラム」、「Adobe Flash Builder4」の順にクリックします。
- 「プロジェクト名」ボックスで、プロジェクトの名前を指定します。
- 「アプリケーションの種類」で、「Web」を選択します。
- FlexSDK バージョンのバージョン 3.5 を指定します。
- 「アプリケーションサーバー」リストで、「J2EE」を選択します。
- 「リモートアクセスサービスを使用」チェックボックスをオンにします。
- 「LiveCycle Data Services」チェックボックスをオンにします。
- 「ルートフォルダー」ボックスで、ルートフォルダーの値を指定します。例えば、C:\lcds\tomcat\webapps\lcds-samples と指定します。
- 「ルート URL」ボックスで、ルート URL フォルダーの値を指定します。例えば、http://localhost:8400/lcds-samples/ と指定します。
- 「コンテンツルート」ボックスで、コンテキストのルート値を指定します。例えば、/lcds-samples と指定します。
- 出力フォルダーのデフォルト値をそのまま使用します。
- 「完了」をクリックします。
- Product.as クラスを作成します。
- この節の最初に示すアプリケーションロジックを MXML ファイルに追加します。