.15 2009
Tasaki です。
今回から、データベースを実際にコード中で操作してみます。
まずは、データベースからの読み込み部分を作成します。
これはアプリケーションの初期化時に呼び出すことになりますので、初めに実装しておくべき点です。当然、初めはデータが登録されていないどころか、データベースファイルが存在しないでしょうから、これを作成するところもコード化する必要があります。
ただ、前回のエントリで作成したテンプレートをそのままデータベースとして取り込めばいいわけですから、この部分に関しては楽ができます。
これを踏まえて、手順としては次のようになります。
①ドキュメントディレクトリ内にデータベースファイルがあるか確認する
②なければ、アプリケーションバンドルからデータベースのテンプレートファイルをドキュメントディレクトリにコピーする
③テンプレートファイルが見つからなければエラー終了
④データベースファイルからデータを読み込む
上記①~③のコード例を以下に示します。引数には、データベースファイルの絶対パスを与えます。なお、ドキュメントディレクトリの取得法については、ここでは触れません。
- ( BOOL ) initDatabase:( NSString *) path {
NSFileManager *fileManager = [ NSFileManager defaultManager ];
if([ fileManager fileExistsAtPath: path ] == NO ) {
// DB ファイルが無い場合は DB を作成
NSString *dbTemplatePath = [[[ NSBundle mainBundle ] resourcePath ] stringByAppendingPathComponent: @"mydatabase.sqlite" ];
if([ fileManager fileExistsAtPath: dbTemplatePath ] == NO ) {
// DB テンプレートが見当たらない場合は、エラー終了
NSLog( @"DB template file is not found." );
return NO;
}
// テンプレートをドキュメントディレクトリへコピー
NSError *error = nil;
if([ fileManager copyItemAtPath: dbTemplatePath toPath: path error: &error ] == NO ) {
// DB テンプレートをコピーできない場合は、エラー終了
NSLog( @"DB file could not be created with following message. '%@'", [ error localizedDescription ]);
return NO;
}
}
return YES;
}
さてこの後は、いよいよデータベースからデータを読み込みます。
以下のコードでは、引数 data に読み込んだデータの配列を、path には、データベースファイルの絶対パスを与えます。
返り値は、データベースへのポインタです。
- ( sqlite3 *) loadPersonData:( NSArray *) data FromDB:( NSString *) path {
sqlite3 *database = NULL;
if( sqlite3_open([ path UTF8String ], &database ) == SQLITE_OK ) {
// DB ファイルオープン成功
sqlite3_stmt *statement = NULL;
const char *allSelect = "SELECT * FROM person";
int status = sqlite3_prepare_v2( database, allSelect, -1, &statement, NULL );
if( status == SQLITE_OK ) {
while( sqlite3_step( statement ) == SQLITE_ROW ) {
// データが存在する場合は、配列に格納する
Person *person = [[ Person alloc ] initWithStatement: statement database: database ];
[ data addObject: person ];
[ person release ];
}
}
sqlite3_finalize( statement );
} else {
// DB ファイルオープン失敗
sqlite3_close( database );
NSLog( @"Failed to open databese with following message '%s'.", sqlite3_errmsg( database ));
database = NULL;
}
return database;
}
上記メソッド内に Person クラスとそのメソッドが確認できると思います。
名前から推察できる通り、データベースの person エンティティに対応するクラスです。
これは、CoreData を使う場合と違い、クラスおよびメソッドを自力で実装する必要があります。この辺りが現行バージョンで厄介な点です。
次回はこのクラスの概要と実装すべきメソッドを紹介します。
なお、sqlite3 という接頭辞がついた型または関数については、以前紹介したサイトなどを参考にしてください。(ここまでに出てきたものについては、非常によく使われていますのできちんと理解しておくことが求められます。)
.12 2009
Tasaki です。
SQLite の使い方2回目は、データベースの作り方をご紹介します。
Mac には、SQLite バージョン3が標準でインストールされており、ターミナルから実行することが可能です。
iPhone アプリで、SQLite データベースを使用する場合、テンプレートを予め Mac 側で作成しておくとコーディングが楽になります。
それでは、実際にやってみましょう。
ターミナルを開いて sqlite3 mydatabase.sqlite を実行すると、カレントディレクトリに mydatabase.sqlite ファイルが作成されます。既に同名のファイルが存在する場合は、既存のファイルが開かれ、SQLite が起動します。
新規にファイルを作成した場合、これでデータベースの初期化も行われます。
次にテーブルを作成します。
SQLite バージョン3では、以下の5種類のデータ型が定義されています。
型名 | 詳細 |
---|
INTEGER | 符号付き整数 |
REAL | 倍精度浮動小数点数 |
TEXT | Unicode文字列(UTF-8,UTF-16BE,UTF-16LE) |
BLOB | 不定形、バイナリデータ |
NULL | NULL値を表す |
今回は整数と文字列で構成される以下のテーブルを作成することにします。
テーブル名 person
{ id : 整数値、主キー
name : 文字列 }
この場合、SQLite に以下の操作を行います。
CREATE TABLE person ( 'id' INTEGER PRIMARY KEY, 'name' TEXT NOT NULL );
エラーメッセージが出なければ、無事テーブルが作成されているはずです。
テーブルの内容を確認する場合は、.schema コマンドを使用します。
試しに .schema person と入力するとテーブル名とデータ型が表示されると思います。
SQLite を終了する時は、.exit と入力します。
このようにして、データベースファイルのテンプレートを作成することができました。あとは、できあがったファイルをプロジェクトに追加して終了です。
次回は、データベースの読み込み部分を解説します。
.11 2009
Tasaki です。
突然ですが、今回から数回に渡って、SQLite 導入編を連載していこうと思います。
ちょうど OS の変わり目で書けるネタが少なくなってきたということもありますが、WWDC でのアナウンスを受けまして、iPod touch ユーザーがこぞって 3.0 にすぐさま移行するとは思えませんので、2.2.1互換の技術は使いどころがあると見込んでのことです。(iPhone ユーザーの中にも特別な理由があって、すぐさまアップデートしない人たちもいるでしょうし)
それでは、まず初めにライブラリのインポートですが、SQLite はフレームワークとして提供される訳ではありませんので、CoreGraphics を利用する場合などとは若干操作が異なってきます。
①プロジェクトウィンドウのグループとファイル一覧からコンテキストメニューを表示し、「既存のフレームワークを"<グループ名>"に追加」を選択
②<SDKのインストールディレクトリ>/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS<ベースSDKのバージョン>SDK/usr/lib/libsqlite3.0.dylibを選択し、追加
③ソースコードのヘッダ部にて、#import <sqlite3.h>と宣言
ひとまず、これで sqlite 関連の API が利用可能になりました。
なお、iPhone OS に搭載されている SQLite はバージョン3だということで、これらの API は全て sqlite3 という接頭辞を含んでいます。
また、API は C 言語で提供されており、日本語の資料なども Objective-C 関連のものに比べれば多数公開されていますので、比較的ハードルは低めといってよさそうです。
ちなみに僕の場合、以下のサイトにお世話になっています。
rktSQLite : SQLiteのSQLコマンド一覧SQLiteバージョン3のC/C++インタフェース - Third impact次回はデータベースの作り方をご紹介する予定です。
.20 2009
Tasaki です。
今回でひとまず CoreData の簡単な使い方の解説は終了とします。
最後のテーマは、データの抽出方法についてです。
前回、データベースからデータを読み込む手順を取り扱いましたが、あのままではあるエンティティの全てのデータを取り出してしまいます。
当然、データベースから全てのデータを取り出すなんてこと普通はありませんから、条件を満たしたデータだけを抽出する必要がありますよね。
このときに使用するのが、NSPredicate (述語)オブジェクトです。
前回出てきた受信要求 (NSFetchRequest) クラスに、このオブジェクトを設定することで抽出条件を指定することが可能です。
そのためのメソッドがこちらです。
- ( void ) setPredicate:( NSPredicate *) predicate;
また、述語オブジェクトの作成には以下のメソッドを使用します。
+ ( NSPredicate *) predicateWithFormat:( NSString *) format, ... ;
これは、printf()でおなじみの文字列と可変引数を与えて述語を作成します。この引数部分に、条件式を与えることになります。
例えば、MyData エンティティの floatValue が 0 より大きいもの、という条件にしたい場合は以下のように記述します。
NSFetchRequest *request = [[ NSFetchRequest alloc ] init ];
NSEntityDescription *entity = [ NSEntityDescription entityForName: @"Sites" inManagedObjectContext: managedObjectContext ];
[ request setEntity: entity ];
NSPredicate *predicate = [ NSPredicate predicateWithFormat: @"floatValue > 0.0" ];
[ request setPredicate: predicate ];
この後、前回のコードと同じように要求を送ると条件に合致したデータだけが返されます。
なお、条件式の内部では以下の単語が予約語として使われているようです。
AND, OR, IN, NOT, ALL, ANY, SOME, NONE, LIKE, CASEINSENSITIVE, CI, MATCHES, CONTAINS, BEGINSWITH, ENDSWITH, BETWEEN, NULL, NIL, SELF, TRUE, YES, FALSE, NO, FIRST, LAST, SIZE, ANYKEY, SUBQUERY, CAST, TRUEPREDICATE, FALSEPREDICATE
.19 2009
Tasaki です。
CoreData の紹介もついに 5 回目に突入しました。
今まで、これだけシリーズ化したテーマはなかったので、ちょっと食傷気味なところもありますが、がんばっていきたいと思います。
今回は、ついに(といいますか、やっと)データベースからのデータの読み出しについて解説します。
手始めに、登録されたデータをある属性について降順で取得した配列を返すメソッドを作成してみます。
データの読み出しは、以下の流れで行います。
①エンティティの指定
②受信要求の作成
③ソート順序の指定
④受信要求の実行
⑤配列からデータを表示
なお、今回も MyData エンティティを使用したサンプルコードを示します。
まずエンティティの指定ですが、これには以下のメソッドを使用します。
+ ( NSEntityDescription *) entityForName:( NSString *) name inManagedObjectContext:( NSManagedObjectContext *) context;
name には、エンティティ名を context には、管理オブジェクトコンテキストを指定します。
次の受信要求の作成については、alloc - init で行います。
この後で、エンティティを指定するんですが、これには以下のメソッドを使用します。
- ( void ) setEntity:( NSEntityDescription *) entity;
この引数として、「エンティティの指定」で紹介したメソッドの返り値を与える訳です。
ソート順序の指定には、以下のメソッドを使用します。
- ( void ) setSortDescriptors:( NSArray *) sortDescriptors;
これは、データベースから得られたデータ配列の順序を決めるために行いますが、複数の条件を設定できるように引数は配列で与えるようになっています。
今回は1つの条件のみでソートするので、要素数1の配列を作成しここへ渡します。
肝心のソートディスクリプタの作成には次のメソッドを使用します。
- ( id ) initWithKey:( NSString *) keyPath ascending:( BOOL ) ascending;
keyPath には、属性名を与えます。 2番目の引数は、昇順ソートなら YES、降順ソートなら NO を渡します。
受信要求の実行には、以下のメソッドを使用します。
- ( NSArray *) executeFetchRequest:( NSFetchRequest *) request error:( NSError **) error;
request には、作成した受信要求を与えます。2番目の引数は、以前にも登場したエラーオブジェクトへのポインタです。
このメソッドの返り値は要求の結果となっており、エラーが発生した場合は nil となります。
以上で、コーディングに必要なメソッドの紹介は終わりました。
一連のサンプルコードを以下に示します。
- ( NSMutableArray *) showData:( NSManagedObjectContext *) managedObjectContext {
NSMutableArray *results = nil;
if( managedObjectContext != nil ) {
// ローカルDBから位置情報を取得
NSFetchRequest *request = [[ NSFetchRequest alloc ] init ];
NSEntityDescription *entity = [ NSEntityDescription entityForName: @"MyData" inManagedObjectContext: managedObjectContext ];
[ request setEntity: entity ];
// 値の大きい順に降順ソート
NSSortDescriptor *sortDescriptor = [[ NSSortDescriptor alloc ] initWithKey: @"floatValue" ascending: NO ];
NSArray *sortDescriptors = [[ NSArray alloc ] initWithObjects: sortDescriptor, nil ];
[ request setSortDescriptors: sortDescriptors ];
[ sortDescriptor release ];
[ sortDescriptors release ];
NSError *error;
results = [[ managedObjectContext executeFetchRequest: request error: &error ] mutableCopy ];
if( mutableFetchResults == nil ) {
// エラー対処
} else {
[ results autorelease ];
}
[ request release ];
}
return results;
}