Box)の裏方として活躍しているデータベース「SQLite」についてどのように活用しているかをご紹介したいと思います。ADBでは、RSSリーダーやおすすめ情報などの受信したデータは全てSQLiteに格納するようにしています。SQLiteに格納してしまえばオフライン状態でもデータを表示することができ、使い勝手を向上させることができます。SQLiteは、MySQLやPostgreSQLなどのデータベースに比べると扱えるデータ型などが限られているので複雑なものを格納するには適していませんが、クライアントの一時的なデータ保管場所としては十分使えるデータベースです。AIRでは、このSQLiteを利用するためのAPIが用意されているので、データベースへのINSERTやUPDATE、SELECTなどの操作が行えます。APIでは、昔ながらの手順でデータベースへのコネクションを取得し、SQLStatementで生成したクエリを発行して結果を取得するという流れになります。これでは、画面側のロジックでSQL文の生成やコネクションなどデータベースのことを意識しなければいけなくなってしまいます。そこで今回ADBを開発する中でSQLiteをもっと手軽に効率よく操作するため、AIR向けのO/Rマッピングライブラリ「AirRecord(エアーレコード)」を作りました。AirRecordは、マーチン・ファウラーのデザイン・パターンの1つ「ActiveRecord」にもとづき設計しています。ActiveRecordのパターンは「アプリケーション側のモデル構造とDB側のテーブル構造を1:1で対応させる」といったシンプルな考え方で必要なクラス数も比較的少なくおさえることができ、ソースコード量も抑えることができます。RailsやCakePHPなどのフレームワークの中でも機能的に組み込まれています。AirRecordを使うことで、モデル側でデータベースの操作を集約できるので画面とロジック、モデル(データベース)をうまく分離して開発することができます。それでは、AirRecordの仕組みと使い方を説明していきましょう。AirRecordの仕組みと特徴AirRecordは、次のような特徴があります。SQL文を書くことなく非常に短い記述でレコードの抽出や書き換えが行えます。テーブル間の関連を表現でき、1回の検索で紐付くデータを抽出することができます。今回のADBの場合、RSSのフィードに関連したエントリー(記事)を取得したいときに、「feed.find(id)」と書くだけで全ての情報が連想配列として返すことができます。このことにより、画面を担当している開発者は、あまりデータベース周りを気にすることなくデータを操作できるので、スムーズに開発を進めることができます。(今回のADBでは、実際にできたと思います)AirRecordでは次のような仕組みでデータベースを操作することができるようになっています。Codeにて公開しています。AirRecordプロジェクトのページインストールは、ダウンロードしたアーカイブ(最新は、airrecord-1.1.zip)の中のairrecord.swcをAIRプロジェクトのlibsにコピーするだけです。ここからは、AirRecordプロジェクトページからダウンロードできるサンプルソースをベースに説明していきます。データベースの接続アプリケーションの初期処理の中でデータベースへの接続を行います。AirRecordでは、シングルトンのARDatabaseクラスがコネクションを管理しています。ARDatabaseのインスタンスに対してaddメソッドでデータベースの接続情報を追加し、connectメソッドで接続します。db.connect();addメソッドでは、下記のパラメータを設定します。DBファイルのパス(デフォルトは、app-storage:/)AirRecordでは、dbNameで指定した名前でSQLiteのファイルを生成します。dbNameにadbを指定するとadb.dbというDBファイルが作られます。(既にファイルが存在している場合は、DBファイルは生成されません)DBファイルの生成場所は、pathパラメータでも指定できますが、デフォルトで「app-storage:/」になります。(AIRアプリのストレージ格納場所)ちなみにAIRアプリのストレージ格納場所は、下記のようになります。Store|また、接続時にはddlパラメータで指定したSQL文の配列を順番に流しこむようになっています。TABLEのSQLを書いておき、テーブルを生成できるようにしておきます。EXISTSを指定することで既にテーブルが存在する場合は実行しないようにできます。KEYを指定しておきます。することでidに通番を自動的にふることができます(auto-incrementなフィールドになる)。それと、これはテーブル設計時のルールなのですが、テーブル名は複数形に統一するようにしています。bookならbooksとし、noteならnotesとします。これはテーブルは、複数レコードが集まったものなので名称は複数形にするべきという考えだからです。テストなどでデータベース接続時に必ずDBファイルを初期化したい場合は、connectメソッドを次のように記述します。true);こうすることで、起動するたびに一旦DBファイルを削除してから新しいDBファイルを生成することができます。DBファイルの中を確認する方法として筆者の場合、PupSQLiteというWindowsのツールを利用しています。PupSQLiteを使えば簡単にSQLiteのデータをGUIで確認できるので便利です。モデルクラスの作成テーブルの数と同じ分だけモデルクラスを作成します。モデルクラスは、ARModelを継承し、コンストラクタで__tableにテーブル名を指定します。booksテーブルに対するモデルクラスは下記のようになります。}驚くことに、たったこれだけでモデルクラスとして機能します。レコードの追加レコードを追加する場合は、モデルクラスのinsertメソッドを使用します。モデルクラスのインスタンスを生成しておきます。result.lastInsertRowID;insertメソッドには、フィールド名と値の連想配列(配列オブジェクトとも言う)を引数で渡します。レコードの追加の場合は、主キーであるidは指定しないでおきます。することでidにインクリメントされた番号が自動的に割り当てられます。インクリメントされた番号を取得するには、insertの戻り値のSQLResultの中のlastInsertRowIDを使います。AirRecordでは、連想配列で指定するのが好きではない人のために、別のinsertの方法があります。モデルクラスに、フィールドに対応したメンバー変数を作っておきます。}insertする前にモデルのインスタンスに対して値を設定しておきます。(idの場合は、Number型なのでNaNを指定する)そして、引数を指定せずにinsertメソッドを実行します。book.insert();どちらでも同じことができますが、モデルクラスにメンバー変数を作っておくほうが堅実なやり方です。レコードの検索レコードを検索する場合は、モデルクラスのfindメソッドを使用します。);findメソッドの1つ目の引数に検索条件を指定します。検索条件は、文字列で指定する方法と連想配列で指定する方法があります。連想配列の場合は、フィールド名とマッチさせる値の組み合わせを指定するとAND検索することができます。上記の例では、下記のようなSQLが発行されることになります。10'文字列の場合は、WHERE句に指定する条件文を下記のように直接書きます。B)でない場合は文字列で指定します。);findメソッドの2つ目の引数はソート条件(ORDER句)になり、3つ目は取得件数(LIMIT句)が指定できます。引数を指定しない場合は、全件を取得することができます。検索結果は、次のようなArrayオブジェクトで返ってきます。'ccc'}1件だけを取得したい場合は、findOneメソッドを使う方が便利です。1});findOneメソッドでは、連想配列として結果が返ってきます。}複雑なSQLで検索したい場合は、queryメソッドを使用してください。queryメソッドでは、SQL文をそのまま発行してSQLResultで結果を返します。'My%'");レコードの更新レコードを更新する場合は、モデルクラスのupdateメソッドを使用します。);1つ目の引数に更新レコードの条件を連想配列もしくは文字列で指定します。(条件指定の方法は、findと同じです)2つ目の引数に更新対象のフィールドと値のセットを連想配列で指定します。insertと同じようにモデルクラスに値をセットして更新することもできます。その場合は2つ目の引数を省略します。);但し、この場合は全てのフィールドをモデルにセットしておかなければいけないので注意してください。特定のフィールドのみを更新するには連想配列で指定してください。レコードの削除レコードを削除する場合は、モデルクラスのdelメソッドを使用します。1});引数に削除レコードの条件を連想配列もしくは文字列で指定します。(条件指定の方法は、findと同じです)アソシエーションAirRecordでは、アソシエーション(関連付け)を記述することで関連したテーブルの情報を自動的に取得できます。booksテーブルとnotesテーブルが1:nの関係で関連しているのであれば、下記のようにモデルクラスのコンストラクタでアソシエーションを定義しておきます。}関連が1:nの場合は__hasManyに連想配列でキーとARAssociationオブジェクトをセットします。(キーを変えて複数指定することができます)ARAssociationオブジェクトをnewする際に、1つ目の引数で対象のモデルクラス名、2つ目の引数に外部キーとなっているフィールド名を指定します。これで、Bookのモデルクラスからレコードの検索を行うと、検索結果の連想配列にnotesというキーでNoteモデルクラスに対応したnotesテーブルの関連データが自動的にセットされるようになります。この状態で、booksから1件だけデータを取得すると下記のような結果が得られます。}同様に関連が1:1の場合、親となるモデルでは__hasOneを指定し、子のモデル側では__belongsToを指定します。n:nの場合は__hasAndBelongsToManyを指定します。(この辺はサンプルソースを見てもらうほうが理解が早いと思います)アソシエーションの考え方は少し難しい部分もありますが、慣れると1回の検索で関連データが一気に取得できるので非常に便利です。何階層まで関連データを取得するかは、__recursiveで指定できます(デフォルトは1)。__recursiveが2の場合は2階層先までの関連データを取得してきます。-1にすると最下層まで取得するようにもできます。他にも関連データを取得する際に件数を制限したり検索条件を加えたりしたい場合は、ARAssociationオブジェクトをnewする時の引数を変えることで実現できます。アソシエーションには2つだけ注意点があります。1つめが、関連データを自動的に検索するため、アソシエーションを定義しすぎるとボトルネックになるということです。1回の検索でどこまでデータを取得したいか利用シーンを考えながら最低限の設定にしておくほうがよいでしょう。2つめが、アソシエーション定義したモデルクラスがコンパイル対象にならないということです。FlexやAIRアプリケーションをコンパイル(ビルド)すると、内部で使用されていないクラスは除外されてしまいます。アソシエーションで動的にクラス名を指定しているだけの場合は、下記のようなモデルクラスを参照するだけのModelClassesクラスを作っておき、どこかの場所でこのModelClassesを参照するようにしておくと全てのモデルをコンパイル対象にすることができます。}複数データベースの対応AirRecordでは、1つのAIRアプリケーションで複数のデータベースを操作することもできます。その場合は、まずデータベースの接続時の引数でnameの部分を別の名前で登録しておきます。db.connect("db2");そして、モデルクラスで__dbNameにどのデータベースが対象か指定します。}これでデータベースファイルを分けることができます。ファイルを分割したほうがパフォーマンスがアップする場合もあるので、そこは用途に応じて試してみてください。データベースを分割した場合にアソシエーションは大丈夫か?と心配になるかもしれませんが、現時点ではアソシエーション定義されたテーブルは別々に検索しているので問題ありません。まとめ第4回では、AIRアプリケーションで簡単にSQLiteを扱うための方法として自作ライブラリ「AirRecord」をご紹介しました。AirRecordは、他言語のActiveRecordの実装と比べて機能的には十分ではありませんが、できるだけ軽くシンプルにすることを目標にしているので、たった7つのクラスだけで実装しています。オープンソースとして公開していますので、見てもらえると何かヒントになるかもしれません。バグがあったら修正してもらえるとうれしいです。SQLiteをうまく使いこなすことでRIA、クライアントアプリケーションの新たなる可能性が出てくると思います。Boxは、まだまだベータ版ですが、パワーアップさせていきますのでご期待ください!!地域を選択してください地域を選択すると、アドビサイトで使用する言語と内容が変わります。