iPhoneでSQLite3を使う6(DBのエラー処理)
今回はDBのエラー処理を実装していきます。エラー処理を放置していましたので作りこんで行きます。
基本方針として、メソッド毎に戻り値を見てエラー処理をするのは面倒なので、try catchを使ってエラーをさばいていくことにします。
try catchを使えば、try {}の間で、例外(Exception)が発生したら。catch {]に飛びその中でエラー処理することができます。
準備として、DB用の例外クラスを作成します。Exceptionのクラス型ごとにcatch節を書くことができるので、エラー処理を振り分けるためです。
NSExceptionを派生させて、エラーのコードど発生元のメソッド名を保持できるようにします。また、エラーコードはtypedefで定義しておきます。ついでにエラーコード対応したメッセージも戻せるようにしておきましょう。
- DBException.h
#import <Foundation/Foundation.h> #import <Foundation/NSException.h> // Exeption Codeは、DBで発生したエラーの種類をCodeで表したものです。 typedef enum _DBExceptionCode { DB_OPEN_ERROR = 0, DB_INSERT_ERROR, DB_UPDATE_ERROR, DB_DELETE_ERROR, DB_SELECT_ERROR, DB_PREPARE_ERROR, DB_FINALIZE_ERROR } DBExceptionCode; @interface DBException : NSException { // Exeption Codeを保持するメンバ変数 DBExceptionCode Code_; // エラー発生した場所(クラス名/メソッド名)を保持するメンバ変数 NSString* Method_; } @property (nonatomic) DBExceptionCode Code; -(NSString*)message; -(NSString*)method; +(id)exceptionWithCode:(DBExceptionCode) Code Name:(NSString*) Name Method:(NSString*) Method; -(id)initWithCode:(DBExceptionCode) Code Name:(NSString*) Name Method:(NSString*) Method; @end
- DBException.m
#import "DBException.h" @implementation DBException @synthesize Code = Code_; +(id)exceptionWithCode:(DBExceptionCode) Code Name:(NSString*) Name Method:(NSString*) Method { id exception = [[self alloc] initWithCode:(DBExceptionCode) Code Name:(NSString*) Name Method:(NSString*) Method]; return exception; } -(id)initWithCode:(DBExceptionCode) Code Name:(NSString*) Name Method:(NSString*) Method { self = [super initWithName:Name reason:nil userInfo:nil]; if (self) { self.Code = Code; Method_ = Method; } return self; } -(NSString*)message { // Exeption Code値に応じたメッセージを返す NSString* Message; switch(Code_){ case DB_OPEN_ERROR: Message = NSLocalizedString(@"DBOpen", @"message"); break; case DB_INSERT_ERROR: Message = NSLocalizedString(@"DBInsert", @"message"); break; case DB_UPDATE_ERROR: Message = NSLocalizedString(@"DBUpdate", @"message"); break; case DB_DELETE_ERROR: Message = NSLocalizedString(@"DBDelete", @"message"); break; case DB_PREPARE_ERROR: Message = NSLocalizedString(@"DBPrepare", @"message"); break; case DB_FINALIZE_ERROR: Message = NSLocalizedString(@"DBfinalyze", @"message"); break; default: Message = @""; break; } return Message; } -(NSString*)method { return Method_; } - (void)dealloc { [Method_ release]; [super dealloc]; } @end
dbOpenやその他のDB関連のメソッドを書き換えていきます。dbOpenは下記のようになります。他のメソッドは割愛。
-(void)dbOpen { NSLog(@"DB Open"); NSString* database_filename; NSString* template_path; NSString* database_path; NSString* work_path; // データベース名 database_filename = @"twitterDB.sqlite"; // データベースファイルを格納するために文書フォルダーを取得します。 work_path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; // データベースファイルのパスを取得します。 database_path = [NSString stringWithFormat:@"%@/%@", work_path, database_filename]; // 文書フォルダーにデータベースファイルが存在しているかを確認します。 NSFileManager* manager = [NSFileManager defaultManager]; if (![manager fileExistsAtPath:database_path]) { NSError* error = nil; // 文書フォルダーに存在しない場合は、データベースの複製元をバンドルから取得します。 template_path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:database_filename]; // バンドルから取得したデータベースファイルを文書フォルダーにコピーします。 if (![manager copyItemAtPath:template_path toPath:database_path error:&error]) { // データベースファイルのコピーに失敗した場合の処理です。 DBException* exception = [DBException exceptionWithCode:DB_OPEN_ERROR Name:@"DB Error" Method:[NSString stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]]; @throw exception; } } // 文書フォルダーに用意されたデータベースファイルを開きます。 if (sqlite3_open([database_path UTF8String], &db_) != SQLITE_OK) { // データベースファイルを SQLite で開くことに失敗しました。 DBException* exception = [DBException exceptionWithCode:DB_OPEN_ERROR Name:@"DB Error" Method:[NSString stringWithFormat:@"%@/%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]]; @throw exception; } }
書き換えが終わったら、DB編 第2回で作った骨格にエラー処理を埋め込みます。
- dbOpenに失敗したら、dbCloseは通らずログをはいて終了。・・・外側のtry catchで処理
- DB処理(InsertとかUpdate)に失敗したら、dbRollbackした後にdbCloseして終了。・・・内側のtry catchで処理
- 全て成功したら、dbCommitした後にdbCloseして終了。
という形に組むと下記のようになります。
@try { // DB接続 [self dbOpen]; @try { // トランザクションの開始 [self dbBegin]; // DB処理 ・ ・ ・ // DBの更新を確定する。 [self dbCommit]; } @catch (DBException* ex) { // DBエラーの処理を記述 NSLog(@"%@ %@(%@)", [ex name], [ex message], [ex method]); // DBをもとの状態に戻す。 [self dbRollback]; } // DB切断 [self dbClose]; // catch節に、@catch (DBException* ex)と書くと、DBExceptionを捕まえてくれます。 } @catch (DBException* ex) { // DB Openエラーの処理を記述 NSLog(@"%@ %@(%@)", [ex name], [ex message], [ex method]); // DBExceptionじゃない場合は、次のcatch節で処理されます。 } @catch (NSException* ex) { // その他エラーの処理を記述 NSLog(@"%@/%@",[ex name], [ex reason]); } @finally { // 必要なら実装 }
以上、エラー処理のざくっとしたイメージです。
※あくまでイメージです、机上で組んでるので、間違えているかもしれません。休みの日にでも実際に組んで動かしてみますので、動かなければごめんなさい。