1004
技術社區[雲棲]
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
