使用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可以參考http://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;}