何となく画面の作り方が分かってきたので、次はデータベースを触りたいなという事で調べてみると、Titanium.Database.DBというインターフェースが用意されています。なんですがexecute位しか無くて、ローレベル過ぎて面倒くさいです。iPhoneのネイティブアプリ開発ですらCoreDataが使えるというのに。
まぁ僕はCoreDataはアンチなんですが、ORMは必須なゆとりプログラマなので、なんか無いのと思って調べてみたところjoli.jsというのが見つかりました。
ソースを眺めてみると全体で900行くらいの超軽量ORMで、当然Webフレームワークなんかと比べると全然機能は少ないですけど、SQL生で処理するよりは全然楽なので、試してみる事にしました。
早速ですが以下がサンプルコードになります。本と著者のテーブルを作って、データベースに突っ込んでから、テーブルビューに表示しています。
なお本題とは関係ありませんが、SQLの結果を処理するのにundersocre.jsを使っています。underscore.jsだろうと、backbone.jsだろうとincludeしとけば動くようです。便利ですね。
// app.js Ti.include('lib/underscore-min.js'); var win = Ti.UI.createWindow({ backgroundColor:'white', tabBarHidden:true }); var Joli = require('joli'); var db = Joli.connect('myapp'); // モデル定義 var Authors = new db.model({ table: 'authors', columns: { id: 'INTEGER PRIMARY KEY AUTOINCREMENT', name: 'TEXT NOT NULL' } }); var Books = new db.model({ table: 'books', columns: { id: 'INTEGER PRIMARY KEY AUTOINCREMENT', author_id: 'REFERENCES authors(id)', title: 'TEXT NOT NULL' } }); db.models.initialize(); var q = new db.query().select().from('authors').count(); if (q.execute() == 0) { // データのインサート var a1 = Authors.newRecord({name: '村上春樹'}); var a2 = Authors.newRecord({name: 'アガサ・クリスティ'}); a1.save(); a2.save(); var b1 = Books.newRecord({title: '1Q84', author_id: a1.id}); var b2 = Books.newRecord({title: 'ねじまき鳥クロニクル', author_id: a1.id}); var b3 = Books.newRecord({title: 'そして誰もいなくなった', author_id:a2.id}); var b4 = Books.newRecord({title: 'オリエント急行の殺人', author_id:a2.id}); b1.save(); b2.save(); b3.save(); b4.save(); } // データの取得 var tableRows = []; q = new db.query().select().from('authors'); _(q.execute()).each(function(item) { var group = Ti.UI.createTableViewSection({headerTitle:item.name}); var queryBooks = new db.query().select().from('books').where('author_id=' + item.id, ''); _(queryBooks.execute()).each(function(item) { group.add(Ti.UI.createTableViewRow({title: item.title})); }); tableRows.push(group); }); // データの取得(Inner Join) var group = Ti.UI.createTableViewSection({headerTitle: 'InnerJoin'}) q = new db.query().select().from('authors, books').where('authors.id=books.author_id', ''); _(q.execute('array')).each(function(item) { group.add(Ti.UI.createTableViewRow({'title': item.name + ' ' + item.title})); }); tableRows.push(group); // テーブルビューの作成と表示 var tv = Ti.UI.createTableView({ data:tableRows, style: Titanium.UI.iPhone.TableViewStyle.GROUPED }); win.add(tv); var tab = Ti.UI.createTab({window:win, title:'Joli Test'}); var tg = Ti.UI.createTabGroup({tabs:[tab]}); tg.open();
簡単な処理しかしてないのですが、幾つか注意点があります。まずモデル定義では制約をカラム毎にしかつけられません。なのでPRIMARY KEYやFIREIGN KEYはカラム定義と同時に行います。GitHubでconstraints制約をつけれるようにしたpull requestが上がっているのですが、まだ取り込まれていませんので、欲しい人は自分も欲しい!とコメントしておきましょう。
またJoinのサポートがイマイチでjoinメソッドが用意されているものの、使用すると必ずLeft Joinになります。普通はInner Joinじゃね?と思わなくはないです。クエリを組み立てる際にfromとwhereを少し工夫すれば一応Inner Joinも可能です。結局指定したパラメータがSQLにマッピングされているだけなので、以下のようにInner Join風に指定すれば良いわけですね。
ただしこの場合、execute時にarrayを指定しなければなりません。そうしないとDBから取得した結果をfromで指定したオブジェクトに変換しようとして、’authors, books’なんていうテーブルは無いので、エラーになります。現時点では複数テーブルが想定されていないんですね。arrayを指定するとオブジェクトにならずにハッシュで返ってきますので問題ありません。
q = new db.query().select().from('authors, books').where('authors.id=books.author_id', ''); q.execute('array')
制約の書き方に制限があるものの、モデルの定義がコード上でできるのはいいですね。無駄にスキーマファイルとかを管理しなくて良いので嬉しいです。コードを読んでいるとmigration機能もついているようなので、また必要になり次第記事を書こうと思います。
あと、とりあえずTitaniumでiPhoneアプリを作ってからAndroidに対応しようと思います。なので上記のサンプルコードはiPhoneでしか動きません。Androidの開発経験が無いので、Titaniumもワカラン、Androidもワカランというのでは全然開発が進まない気がしたので、まずはTitanium力を向上させてからAndroidを倒そうとそういう風に思っています。