iOS數據持久化-SQLite數據庫使用詳解
使用SQLite數據庫
創建數據庫
創建數據庫過程需要3個步驟:
1、使用sqlite3_open函數打開數據庫;
2、使用sqlite3_exec函數執行Create Table語句,創建數據庫表;
3、使用sqlite3_close函數釋放資源。
這個過程中使用了3個SQLite3函數,它們都是純C語言函數,通過Objective-C去調用C函數當然不是什麼問題,但是也要注意Objective-C數據類型與C數據類型兼容性問題。
下麵我們使用SQLite技術實現備忘錄案例,與屬性列表文件實現一樣,我們隻需要修改持久層工程(PersistenceLayer)中NoteDAO類就可以了。首先我們需要添加SQLite3庫到工程環境中,有3個工程需要添加到哪個呢?應該添加到可以運行的工程即表示層工程PresentationLayer。選擇工程PresentationLayer中TARGETS→PresentationLayer→Link Binary With Libraries,點擊左下角的“+”,彈出對話框選擇libsqlite3.dylib或libsqlite3.0.dylib,在彈出的對話框中點擊Add添加。
NoteDAO.h文件的修改:
#import ”Note.h” #import ”sqlite3.h” #define DBFILE_NAME @”NotesList.sqlite3″ @interface NoteDAO : NSObject { sqlite3 *db; } + (NoteDAO*)sharedManager; - (NSString *)applicationDocumentsDirectoryFile; - (void)createEditableCopyOfDatabaseIfNeeded; //插入Note方法 -(int) create:(Note*)model; //刪除Note方法 -(int) remove:(Note*)model; //修改Note方法 -(int) modify:(Note*)model; //查詢所有數據方法 -(NSMutableArray*) findAll; //按照主鍵查詢數據方法 -(Note*) findById:(Note*)model; @end
我們需要使用語句#import ”sqlite3.h”引入sqlite3頭文件,而且需要定義sqlite3*成員變量db。NoteDAO.m中的createEditableCopyOfDatabaseIfNeeded方法:
- (void)createEditableCopyOfDatabaseIfNeeded { NSString *writableDBPath = [self applicationDocumentsDirectoryFile]; if (sqlite3_open([writableDBPath UTF8String], &db) != SQLITE_OK) { ① sqlite3_close(db); ② NSAssert(NO,@”數據庫打開失敗。”); } else { char *err; NSString *createSQL = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS Note (cdate TEXT PRIMARY KEY, content TEXT);"]; ③ if (sqlite3_exec(db,[createSQL UTF8String],NULL,NULL,&err) != SQLITE_OK) { ④ sqlite3_close(db); ⑤ NSAssert1(NO, @”建表失敗, %s”, err); ⑥ } sqlite3_close(db); ⑦ } }
createEditableCopyOfDatabaseIfNeeded方法用於創建數據庫,第1步打開數據庫,代碼①行,語句是sqlite3_open([writableDBPath UTF8String], &db),sqlite3_open函數的第1個參數是數據庫文件完整的路徑,但是需要注意的是在SQLite3函數中接受的是char*的UTF-8類型數據,需要將NSString*轉換為UTF-8,使用NSString*的UTF8String方法可以轉換,sqlite3_open函數第2個參數sqlite3指針變量db的地址。該函數的返回值是int類型,在SQLite3中定義了很多常量,返回值等於常量SQLITE_OK則說明操作成功。
第2步執行建表語句,代碼第④行,語句sqlite3_exec(db,[createSQL UTF8String],NULL,NULL,&err)執行建表的SQL。第1個參數是sqlite3指針變量db的地址,第2個參數是要執行的sql語句,第3個參數是要回調函數,第4個參數是要回調函數的參數,第5個參數是執行出錯的字符串。建表SQL語句是,如果表Note存在這不用創建。
CREATE TABLE IF NOT EXISTS Note (cdate TEXT PRIMARY KEY, content TEXT)
第3步使用sqlite3_close函數釋放資源,代碼②、⑤、⑦行所示,在數據庫打開失敗、Create Table執行失敗和成功執行完成時候調用。原則上無論正常結束還是異常結束必須使用sqlite3_close函數釋放資源。
查詢數據
數據查詢一般會帶有查詢條件,這個使用SQL語句where子句很容易實現,但是在程序中需要動態綁定參數給where子句。執行查詢數據步驟如下:
1、使用sqlite3_open函數打開數據庫;
2、使用sqlite3_prepare_v2函數預處理SQL語句;
3、使用sqlite3_bind_text函數綁定參數;
4、使用sqlite3_step函數執行SQL語句,遍曆結果集;
5、使用sqlite3_column_text等函數提取字段數據;
6、使用sqlite3_finalize和sqlite3_close函數釋放資源。
NoteDAO.m中的按照主鍵查詢數據方法:
-(Note*) findById:(Note*)model { NSString *path = [self applicationDocumentsDirectoryFile]; if (sqlite3_open([path UTF8String], &db) != SQLITE_OK) { ① sqlite3_close(db); ② NSAssert(NO,@”數據庫打開失敗。”); } else { NSString *qsql = @”SELECT cdate,content FROM Note where cdate =?”; sqlite3_stmt *statement; //預處理過程 if (sqlite3_prepare_v2(db, [qsql UTF8String], -1, &statement, NULL) == SQLITE_OK) { ③ //準備參數 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; ④ [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSString *nsdate = [dateFormatter stringFromDate:model.date]; //綁定參數開始 sqlite3_bind_text(statement, 1, [nsdate UTF8String], -1, NULL); ⑤ //執行 if (sqlite3_step(statement) == SQLITE_ROW) { ⑥ char *cdate = (char *) sqlite3_column_text(statement, 0); ⑦ NSString *nscdate = [[NSString alloc] initWithUTF8String: cdate]; char *content = (char *) sqlite3_column_text(statement, 1); NSString * nscontent = [[NSString alloc] initWithUTF8String: content]; Note* note = [[Note alloc] init]; note.date = [dateFormatter dateFromString:nscdate]; note.content = nscontent; sqlite3_finalize(statement); sqlite3_close(db); return note; } } sqlite3_finalize(statement); ⑧ sqlite3_close(db); ⑨ } return nil; }
該方法執行了6個步驟,其中第1個步驟,代碼第①行所示,它與創建數庫的第1個步驟是一樣的,不用再介紹了。
第2個步驟,代碼第③行所示,語句sqlite3_prepare_v2(db, [qsql UTF8String], -1, &statement, NULL)是預處理SQL語句,預處理目的是將SQL編譯成二進製代碼,提高SQL語句執行的速度。sqlite3_prepare_v2函數的第3個參數-1代表全部sql字符串長度,第4個參數&statement是sqlite3_stmt指針的地址,它是語句對象,通過語句對象可以執行SQL語句,第5個參數是sql語句沒有被執行的部分語句。
第3個步驟,代碼第⑤行所示,語句sqlite3_bind_text(statement, 1, [nsdate UTF8String], -1, NULL)是綁定SQL語句參數。在SQL語句中帶有問號,這個問號就是要綁定的參數,問號是占位符。
NSString *qsql = @”SELECT cdate,content FROM Note where cdate =?”;
sqlite3_bind_text函數是綁定參數,第1個參數是statement指針,第2個參數為序號(從1開始),第3個參數為字符串值,第4個參數為字符串長度,第5個參數為一個函數指針。
第4個步驟sqlite3_step(statement)執行SQL語句,代碼第⑥行所示,sqlite3_step返回int類型,等於SQLITE_ROW說明還要其它的行沒有遍曆。
第5個步驟提取字段數據,代碼第⑦行所示,使用sqlite3_column_text(statement, 0)函數可以讀取字符串類型字段,第2參數是指定select字段的索引(從0開始)。同樣char*轉換成為NSString*類型,需要initWithUTF8String:構造方法。讀取字段函數采用與字段類型有關係,SQLite3的類似的常用函數還有:
sqlite3_column_blob()
sqlite3_column_double()
sqlite3_column_int()
sqlite3_column_int64()
sqlite3_column_text()
sqlite3_column_text16()
關於其它的API可以參考https://www.sqlite.org/cintro.html。
第6個步驟是釋放資源,創建數據庫過程不同,除了使用sqlite3_close函數關閉數據庫,代碼第⑧行所示,還要使用sqlite3_finalize函數釋放語句對象statement代碼第⑨行所示。
NoteDAO.m中的查詢所有數據方法:
-(NSMutableArray*) findAll { NSString *path = [self applicationDocumentsDirectoryFile]; NSMutableArray *listData = [[NSMutableArray alloc] init]; if (sqlite3_open([path UTF8String], &db) != SQLITE_OK) { sqlite3_close(db); NSAssert(NO,@”數據庫打開失敗。”); } else { NSString *qsql = @”SELECT cdate,content FROM Note”; sqlite3_stmt *statement; //預處理過程 if (sqlite3_prepare_v2(db, [qsql UTF8String], -1, &statement, NULL) == SQLITE_OK) { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; //執行 while (sqlite3_step(statement) == SQLITE_ROW) { char *cdate = (char *) sqlite3_column_text(statement, 0); NSString *nscdate = [[NSString alloc] initWithUTF8String: cdate]; char *content = (char *) sqlite3_column_text(statement, 1); NSString * nscontent = [[NSString alloc] initWithUTF8String: content]; Note* note = [[Note alloc] init]; note.date = [dateFormatter dateFromString:nscdate]; note.content = nscontent; [listData addObject:note]; } } sqlite3_finalize(statement); sqlite3_close(db); } return listData; }
查詢所有數據方法與按照主鍵查詢數據方法類似,區別在於本方法沒有查詢條件不需要綁定參數。遍曆的時候使用while循環語句,不是if語句。
while (sqlite3_step(statement) == SQLITE_ROW) {
… …
}
修改數據
修改數據包括:insert、update和delete語句。這3個SQL語句都可以帶有參數,關於參數的綁定與查詢where子句綁定的方式是一樣的。執行修改數據步驟如下:
1、使用sqlite3_open函數打開數據庫;
2、使用sqlite3_prepare_v2函數預處理SQL語句;
3、使用sqlite3_bind_text函數綁定參數;
4、使用sqlite3_step函數執行SQL語句;
5、使用sqlite3_finalize和sqlite3_close函數釋放資源。
修改數據的步驟與查詢數據的步驟相比少了一個提取字段數據步驟。下麵我們看看代碼部分。其它的步驟是一樣的。
NoteDAO.m中的插入Note方法:
-(int) create:(Note*)model { NSString *path = [self applicationDocumentsDirectoryFile]; if (sqlite3_open([path UTF8String], &db) != SQLITE_OK) { ① sqlite3_close(db); ② NSAssert(NO,@”數據庫打開失敗。”); } else { NSString *sqlStr = @”INSERT OR REPLACE INTO note (cdate, content) VALUES (?,?)”; sqlite3_stmt *statement; //預處理過程 if (sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) { ③ NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSString *nsdate = [dateFormatter stringFromDate:model.date]; //綁定參數開始 sqlite3_bind_text(statement, 1, [nsdate UTF8String], -1, NULL); ④ sqlite3_bind_text(statement, 2, [model.content UTF8String], -1, NULL); //執行插入 if (sqlite3_step(statement) != SQLITE_DONE) { ⑤ NSAssert(NO, @”插入數據失敗。”); } } sqlite3_finalize(statement); ⑥ sqlite3_close(db); ⑦ } return 0; }
第⑤行代碼sqlite3_step(statement)語句執行插入語句,常量SQLITE_DONE執行完成。
NoteDAO.m中的刪除Note方法:
-(int) remove:(Note*)model { NSString *path = [self applicationDocumentsDirectoryFile]; if (sqlite3_open([path UTF8String], &db) != SQLITE_OK) { sqlite3_close(db); NSAssert(NO,@”數據庫打開失敗。”); } else { NSString *sqlStr = @”DELETE from note where cdate =?”; sqlite3_stmt *statement; //預處理過程 if (sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSString *nsdate = [dateFormatter stringFromDate:model.date]; //綁定參數開始 sqlite3_bind_text(statement, 1, [nsdate UTF8String], -1, NULL); //執行插入 if (sqlite3_step(statement) != SQLITE_DONE) { NSAssert(NO, @”刪除數據失敗。”); } } sqlite3_finalize(statement); sqlite3_close(db); } return 0; } NoteDAO.m中的修改Note方法: -(int) modify:(Note*)model { NSString *path = [self applicationDocumentsDirectoryFile]; if (sqlite3_open([path UTF8String], &db) != SQLITE_OK) { sqlite3_close(db); NSAssert(NO,@”數據庫打開失敗。”); } else { NSString *sqlStr = @”UPDATE note set content=? where cdate =?”; sqlite3_stmt *statement; //預處理過程 if (sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSString *nsdate = [dateFormatter stringFromDate:model.date]; //綁定參數開始 sqlite3_bind_text(statement, 1, [model.content UTF8String], -1, NULL); sqlite3_bind_text(statement, 2, [nsdate UTF8String], -1, NULL); //執行插入 if (sqlite3_step(statement) != SQLITE_DONE) { NSAssert(NO, @”修改數據失敗。”); } } sqlite3_finalize(statement); sqlite3_close(db); } return 0; }
最後更新:2017-04-03 16:48:34