創(chuàng)建數(shù)據(jù)庫過程需要3個步驟:
1、使用sqlite3_open函數(shù)打開數(shù)據(jù)庫;
2、使用sqlite3_exec函數(shù)執(zhí)行Create?Table語句,創(chuàng)建數(shù)據(jù)庫表;
3、使用sqlite3_close函數(shù)釋放資源。
這個過程中使用了3個SQLite3函數(shù),它們都是純C語言函數(shù),通過Objective-C去調(diào)用C函數(shù)當然不是什么問題,但是也要注意Objective-C數(shù)據(jù)類型與C數(shù)據(jù)類型兼容性問題。
下 面我們使用SQLite技術(shù)實現(xiàn)備忘錄案例,與屬性列表文件實現(xiàn)一樣,我們只需要修改持久層工程(PersistenceLayer)中NoteDAO類 就可以了。首先我們需要添加SQLite3庫到工程環(huán)境中,有3個工程需要添加到哪個呢?應(yīng)該添加到可以運行的工程即表示層工程 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;
//查詢所有數(shù)據(jù)方法
-(NSMutableArray*)?findAll;
//按照主鍵查詢數(shù)據(jù)方法
-(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,@”數(shù)據(jù)庫打開失敗?!?;
}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方法用于創(chuàng)建數(shù)據(jù)庫,第1步打開數(shù)據(jù) 庫,代碼①行,語句是 sqlite3_open([writableDBPath?UTF8String],?&db),sqlite3_open函數(shù)的第1個參數(shù)是 數(shù)據(jù)庫文件完整的路徑,但是需要注意的是在SQLite3函數(shù)中接受的是char*的UTF-8類型數(shù)據(jù),需要將NSString*轉(zhuǎn)換為UTF-8,使 用NSString*的UTF8String方法可以轉(zhuǎn)換,sqlite3_open函數(shù)第2個參數(shù)sqlite3指針變量db的地址。該函數(shù)的返回值是 int類型,在SQLite3中定義了很多常量,返回值等于常量SQLITE_OK則說明操作成功。
第2步執(zhí)行建表語句,代碼第④行,語句 sqlite3_exec(db,[createSQL?UTF8String],NULL,NULL,&err)執(zhí)行建表的SQL。第1個參數(shù) 是sqlite3指針變量db的地址,第2個參數(shù)是要執(zhí)行的sql語句,第3個參數(shù)是要回調(diào)函數(shù),第4個參數(shù)是要回調(diào)函數(shù)的參數(shù),第5個參數(shù)是執(zhí)行出錯的 字符串。建表SQL語句是,如果表Note存在這不用創(chuàng)建。
CREATE?TABLE?IF?NOT?EXISTS?Note?(cdate?TEXT?PRIMARY?KEY,?content?TEXT)
第3步使用sqlite3_close函數(shù)釋放資源,代碼②、⑤、⑦行所示,在數(shù)據(jù)庫打開失敗、Create?Table執(zhí)行失敗和成功執(zhí)行完成時候調(diào)用。原則上無論正常結(jié)束還是異常結(jié)束必須使用sqlite3_close函數(shù)釋放資源。
查詢數(shù)據(jù)
數(shù)據(jù)查詢一般會帶有查詢條件,這個使用SQL語句where子句很容易實現(xiàn),但是在程序中需要動態(tài)綁定參數(shù)給where子句。執(zhí)行查詢數(shù)據(jù)步驟如下:
1、使用sqlite3_open函數(shù)打開數(shù)據(jù)庫;
2、使用sqlite3_prepare_v2函數(shù)預(yù)處理SQL語句;
3、使用sqlite3_bind_text函數(shù)綁定參數(shù);
4、使用sqlite3_step函數(shù)執(zhí)行SQL語句,遍歷結(jié)果集;
5、使用sqlite3_column_text等函數(shù)提取字段數(shù)據(jù);
6、使用sqlite3_finalize和sqlite3_close函數(shù)釋放資源。
NoteDAO.m中的按照主鍵查詢數(shù)據(jù)方法:

-(Note*)?findById:(Note*)model
{
NSString?*path?=?[self?applicationDocumentsDirectoryFile];
if(sqlite3_open([path?UTF8String],?&db)?!=?SQLITE_OK)?{?①
sqlite3_close(db);??②
NSAssert(NO,@”數(shù)據(jù)庫打開失敗?!?;
}else{
NSString?*qsql?=?@”SELECT?cdate,content?FROM?Note?where?cdate?=?”;
sqlite3_stmt?*statement;
//預(yù)處理過程
if(sqlite3_prepare_v2(db,?[qsql?UTF8String],?-1,?&statement,?NULL)?==?SQLITE_OK)?{?③
//準備參數(shù)
NSDateFormatter?*dateFormatter?=?[[NSDateFormatter?alloc]?init];?④
[dateFormatter?setDateFormat:@"yyyy-MM-dd?HH:mm:ss"];
NSString?*nsdate?=?[dateFormatter?stringFromDate:model.date];
//綁定參數(shù)開始
sqlite3_bind_text(statement,?1,?[nsdate?UTF8String],?-1,?NULL);?⑤
//執(zhí)行
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);
returnnote;
}
}
sqlite3_finalize(statement);?⑧
sqlite3_close(db);??⑨
}
returnnil;
}
該方法執(zhí)行了6個步驟,其中第1個步驟,代碼第①行所示,它與創(chuàng)建數(shù)庫的第1個步驟是一樣的,不用再介紹了。
第 2個步驟,代碼第③行所示,語句 sqlite3_prepare_v2(db,?[qsql?UTF8String],?-1,?&statement,?NULL)是預(yù)處理 SQL語句,預(yù)處理目的是將SQL編譯成二進制代碼,提高SQL語句執(zhí)行的速度。sqlite3_prepare_v2函數(shù)的第3個參數(shù)-1代表全部 sql字符串長度,第4個參數(shù)&statement是sqlite3_stmt指針的地址,它是語句對象,通過語句對象可以執(zhí)行SQL語句,第5 個參數(shù)是sql語句沒有被執(zhí)行的部分語句。
第3個步驟,代碼第⑤行所示,語句sqlite3_bind_text(statement,?1,?[nsdate?UTF8String],?-1,?NULL)是綁定SQL語句參數(shù)。在SQL語句中帶有問號,這個問號就是要綁定的參數(shù),問號是占位符。
NSString?*qsql?=?@”SELECT?cdate,content?FROM?Note?where?cdate?=?”;
sqlite3_bind_text函數(shù)是綁定參數(shù),第1個參數(shù)是statement指針,第2個參數(shù)為序號(從1開始),第3個參數(shù)為字符串值,第4個參數(shù)為字符串長度,第5個參數(shù)為一個函數(shù)指針。
第4個步驟sqlite3_step(statement)執(zhí)行SQL語句,代碼第⑥行所示,sqlite3_step返回int類型,等于SQLITE_ROW說明還要其它的行沒有遍歷。
第 5個步驟提取字段數(shù)據(jù),代碼第⑦行所示,使用sqlite3_column_text(statement,?0)函數(shù)可以讀取字符串類型字段,第2參數(shù) 是指定select字段的索引(從0開始)。同樣char*轉(zhuǎn)換成為NSString*類型,需要initWithUTF8String:構(gòu)造方法。讀取 字段函數(shù)采用與字段類型有關(guān)系,SQLite3的類似的常用函數(shù)還有:
sqlite3_column_blob()
sqlite3_column_double()
sqlite3_column_int()
sqlite3_column_int64()
sqlite3_column_text()
sqlite3_column_text16()
關(guān)于其它的API可以參考http://www.sqlite.org/cintro.html。
第6個步驟是釋放資源,創(chuàng)建數(shù)據(jù)庫過程不同,除了使用sqlite3_close函數(shù)關(guān)閉數(shù)據(jù)庫,代碼第⑧行所示,還要使用sqlite3_finalize函數(shù)釋放語句對象statement代碼第⑨行所示。
NoteDAO.m中的查詢所有數(shù)據(jù)方法:

-(NSMutableArray*)?findAll
{
NSString?*path?=?[self?applicationDocumentsDirectoryFile];
NSMutableArray?*listData?=?[[NSMutableArray?alloc]?init];
if(sqlite3_open([path?UTF8String],?&db)?!=?SQLITE_OK)?{
sqlite3_close(db);
NSAssert(NO,@”數(shù)據(jù)庫打開失敗。”);
}else{
NSString?*qsql?=?@”SELECT?cdate,content?FROM?Note”;
sqlite3_stmt?*statement;
//預(yù)處理過程
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"];
//執(zhí)行
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);
}
returnlistData;
}
查詢所有數(shù)據(jù)方法與按照主鍵查詢數(shù)據(jù)方法類似,區(qū)別在于本方法沒有查詢條件不需要綁定參數(shù)。遍歷的時候使用while循環(huán)語句,不是if語句。
while?(sqlite3_step(statement)?==?SQLITE_ROW)?{
…?…
}
修改數(shù)據(jù)
修改數(shù)據(jù)包括:insert、update和delete語句。這3個SQL語句都可以帶有參數(shù),關(guān)于參數(shù)的綁定與查詢where子句綁定的方式是一樣的。執(zhí)行修改數(shù)據(jù)步驟如下:
1、使用sqlite3_open函數(shù)打開數(shù)據(jù)庫;
2、使用sqlite3_prepare_v2函數(shù)預(yù)處理SQL語句;
3、使用sqlite3_bind_text函數(shù)綁定參數(shù);
4、使用sqlite3_step函數(shù)執(zhí)行SQL語句;
5、使用sqlite3_finalize和sqlite3_close函數(shù)釋放資源。
修改數(shù)據(jù)的步驟與查詢數(shù)據(jù)的步驟相比少了一個提取字段數(shù)據(jù)步驟。下面我們看看代碼部分。其它的步驟是一樣的。
NoteDAO.m中的插入Note方法:

-(int)?create:(Note*)model
{
NSString?*path?=?[self?applicationDocumentsDirectoryFile];
if(sqlite3_open([path?UTF8String],?&db)?!=?SQLITE_OK)?{?①
sqlite3_close(db);??②
NSAssert(NO,@”數(shù)據(jù)庫打開失敗?!?;
}else{
NSString?*sqlStr?=?@”INSERT?OR?REPLACE?INTO?note?(cdate,?content)?VALUES?(?,?)”;
sqlite3_stmt?*statement;
//預(yù)處理過程
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];
//綁定參數(shù)開始
sqlite3_bind_text(statement,?1,?[nsdate?UTF8String],?-1,?NULL);??④
sqlite3_bind_text(statement,?2,?[model.content?UTF8String],?-1,?NULL);
//執(zhí)行插入
if(sqlite3_step(statement)?!=?SQLITE_DONE)?{?⑤
NSAssert(NO,?@”插入數(shù)據(jù)失敗?!?;
}
}
sqlite3_finalize(statement);??⑥
sqlite3_close(db);??⑦
}
return0;
}
第⑤行代碼sqlite3_step(statement)語句執(zhí)行插入語句,常量SQLITE_DONE執(zhí)行完成。
NoteDAO.m中的刪除Note方法:

-(int)?remove:(Note*)model
{
NSString?*path?=?[self?applicationDocumentsDirectoryFile];
if(sqlite3_open([path?UTF8String],?&db)?!=?SQLITE_OK)?{
sqlite3_close(db);
NSAssert(NO,@”數(shù)據(jù)庫打開失敗。”);
}else{
NSString?*sqlStr?=?@”DELETE??from?note?where?cdate?=?”;
sqlite3_stmt?*statement;
//預(yù)處理過程
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];
//綁定參數(shù)開始
sqlite3_bind_text(statement,?1,?[nsdate?UTF8String],?-1,?NULL);
//執(zhí)行插入
if(sqlite3_step(statement)?!=?SQLITE_DONE)?{
NSAssert(NO,?@”刪除數(shù)據(jù)失敗。”);
}
}
sqlite3_finalize(statement);
sqlite3_close(db);
}
return0;
}
NoteDAO.m中的修改Note方法:
-(int)?modify:(Note*)model
{
NSString?*path?=?[self?applicationDocumentsDirectoryFile];
if(sqlite3_open([path?UTF8String],?&db)?!=?SQLITE_OK)?{
sqlite3_close(db);
NSAssert(NO,@”數(shù)據(jù)庫打開失敗?!?;
}else{
NSString?*sqlStr?=?@”UPDATE?note?set?content=??where?cdate?=?”;
sqlite3_stmt?*statement;
//預(yù)處理過程
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];
//綁定參數(shù)開始
sqlite3_bind_text(statement,?1,?[model.content?UTF8String],?-1,?NULL);
sqlite3_bind_text(statement,?2,?[nsdate?UTF8String],?-1,?NULL);
//執(zhí)行插入
if(sqlite3_step(statement)?!=?SQLITE_DONE)?{
NSAssert(NO,?@”修改數(shù)據(jù)失敗?!?;
}
}
sqlite3_finalize(statement);
sqlite3_close(db);
}
return0;
}