在iOS開發之數據存儲一中已對偏好設置、歸檔等存儲方式進行了介紹,以上幾種方式都有一個致命的缺點,那就是無法存儲大批量的數據。本文重點介紹SQLite在iOS開發中的使用。
Start
在iOS中使用SQLite3,首先要添加庫文件libsqlite3.dylib和導入主頭文件.
#import <sqlite3.h>
libsqlite.png
swift需要建立橋接文件
引入頭文件.png
1.OC中使用SQLite
操作數據庫之前必須先創建數據庫文件和要操作的表,所以使用SQLite3,首先要打開數據庫文件,然后指定或創建一張表。
1.1 創建并打開數據庫
// 打開數據庫
- (void)openDatabase:(NSString *)SQLiteName {
//1. 獲取數據庫存儲路徑
NSString *dbName = [SQLiteName documentDir];
//2. 打開數據庫
// 如果數據庫不存在,怎新建并打開一個數據庫,否則直接打開
if (sqlite3_open(dbName.UTF8String, &_db) != SQLITE_OK) {
NSLog(@"創建/打開數據庫失敗。");
}
//3. 創建表
if ([self createTable]) {
NSLog(@"創建表成功");
} else {
NSLog(@"創建表失敗");
}
}
/**
* 創建數據表
*/
- (BOOL)createTable {
NSString *sql = @"CREATE TABLE IF NOT EXISTS t_person (id integer PRIMARY KEY AUTOINCREMENT,name text,age integer)";
return [self execSql:sql];
}
1.2 封裝執行sql語句方法
使用 sqlite3_exec() 方法可以執行任何SQL語句,比如創表、更新、插入和刪除操作。但是一般不用它執行查詢語句,因為它不會返回查詢到的數據。
/**
* 執行除查詢以外的sql語句
*/
- (BOOL)execSql:(NSString *)sql {
if (sqlite3_exec(_db, sql.UTF8String, nil, nil, nil) != SQLITE_OK) {
return NO;
}
return YES;
}
1.3執行查詢語句
前面說過一般不使用 sqlite3_exec() 方法查詢數據。因為查詢數據必須要獲得查詢結果,所以查詢相對比較麻煩。有以下幾個步驟:
- sqlite3_prepare_v2() : 檢查sql的合法性(準備數據)
- **sqlite3_step() **: 逐行獲取查詢結果,不斷重復,直到最后一條記錄
- sqlite3_coloum_xxx() : 獲取對應類型的內容,iCol對應的就是SQL語句中字段的順序,從0開始。根據實際查詢字段的屬性,使用sqlite3_column_xxx取得對應的內容即可。
- sqlite3_finalize() : 釋放stmt
**
* 返回指定sql查詢語句運行的結果集
*
* @param sql sql
*
* @return 結果集
*/
- (NSArray *)execRecordSql:(NSString *)sql {
// 1. 評估準備SQL語法是否正確
sqlite3_stmt *stmt = NULL;
/**
* 準備: 理解為預編譯SQL語句, 檢測里面是否有錯誤等等, 它可以提供性能
*
* @param db 已經開打的數據庫對象
* @param cSQL 需要執行的SQL語句
* @param -1 需要執行的SQL語句的長度, 傳入-1系統自動計算
* @param stmt 預編譯之后的句柄, 已經要想取出數據, 就需要這個句柄
* @param NULL 一般傳NULL
*
* @return
*/
if (sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL) != SQLITE_OK) {
NSLog(@"準備數據失敗");
}
NSMutableArray *records = [NSMutableArray array];
// 2.查詢數據
// sqlite3_step代表取出一條數據, 如果取到了數據就會返回SQLITE_ROW
while (sqlite3_step(stmt) == SQLITE_ROW) {
// 3. 獲取/顯示查詢結果
// sqlite3_column_xxx方法的第二個參數與sql語句中的字段順尋一一對應(從0開始)
// 獲取一條記錄的數據
NSDictionary *dict = [self recordWithStmt:stmt];
[records addObject:dict];
}
// 4. 釋放句柄
sqlite3_finalize(stmt);
// 返回查詢到的數據
return records;
}
/**
獲取一條記錄的值
- parameter stmt: 預編譯好的SQL語句
- returns: 字典
*/
- (NSDictionary *)recordWithStmt:(sqlite3_stmt *)stmt {
//1.拿到當前這條數據的所有列
int count = sqlite3_column_count(stmt);
// 定義字典存儲查詢到的數據
NSMutableDictionary *record = [[NSMutableDictionary alloc] init];
for (int index = 0; index < count; index++) {
// 2. 拿到每一列的名稱
NSString *name = [NSString stringWithCString:sqlite3_column_name(stmt, index) encoding:NSUTF8StringEncoding];
NSLog(@"%@",name);
// 3.拿到每一列的類型 SQLITE_INTEGER
int type = sqlite3_column_type(stmt, index);
switch (type) {
case SQLITE_INTEGER://整形
[record setObject:@(sqlite3_column_int64(stmt, index)) forKey:name];
break;
case SQLITE_FLOAT:
[record setObject:@(sqlite3_column_double(stmt, index)) forKey:name];
break;
case SQLITE3_TEXT:
// 文本類型
[record setObject:[NSString stringWithUTF8String:sqlite3_column_text(stmt, index)] forKey:name];
break;
case SQLITE_NULL:
// 空類型
[record setObject:[[NSNull alloc]init] forKey:name];
break;
default:
// 二進制類型 SQLITE_BLOB
// 一般情況下, 不會往數據庫中存儲二進制數據
break;
}
}
return record;
}
2. Swift中使用SQLite
在Swift中使用與OC基本一致。
2.1 打開數據庫
/**
打開數據庫
- parameter SQLiteName: 數據庫名稱
*/
func openDatabase(SQLiteName: String) {
//1. 拿到數據庫的存儲路徑
let path = SQLiteName.documentDir()
// print(path)
let cPath = path.cStringUsingEncoding(NSUTF8StringEncoding)!
//2. 打開數據庫
/**
* 打開數據庫方法
* open方法特點: 如果指定路徑對應的數據庫文件已經存在, 就會直接打開
* 如果指定路徑對應的數據庫文件不存在, 就會創建一個新的
* @param cPath 需要打開的數據庫文件的路徑,轉為C語言字符串
* @param db 打開之后的數據庫對象 (指針), 以后所有的數據庫操作, 都必須要拿到這個指針才能進行相關操作
*
* @return 返回整形值
*/
if sqlite3_open(cPath, &db) != SQLITE_OK {
print("打開數據庫失敗")
return
}
// 3. 創建表
if createTable() {
print("創建表成功")
} else {
print("創建表失敗")
}
}
/**
創建表
- returns: 是否創建成功
*/
private func createTable() -> Bool {
// 1.編寫SQL語句
// 建議: 在開發中編寫SQL語句, 如果語句過長, 不要寫在一行
// 開發技巧: 在做數據庫開發時, 如果遇到錯誤, 可以先將SQL打印出來, 拷貝到PC工具中驗證之后再進行調試
let sql = "CREATE TABLE IF NOT EXISTS T_Person( \n" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, \n" +
"name TEXT, \n" +
"age INTEGER \n" +
"); \n"
// 2. 執行sql語句
return execSQL(sql)
}
2.2 封裝執行sql語句方法
/**
執行除查詢以外的sql語句
- parameter sql: sql語句
- returns: 是否執行成功 true 成功
*/
func execSQL(sql: String) -> Bool {
// 0.將Swift字符串轉換為C語言字符串
let cSQL = sql.cStringUsingEncoding(NSUTF8StringEncoding)!
/**
* 執行sql語句
*
* @param COpaquePointer 已經打開的數據庫對象
* @param 需要執行的sql語句
* @param 執行SQL語句之后的回調, 一般傳nil
* @param 是第三個參數的第一個參數, 一般傳nil
* @param 錯誤信息, 一般傳nil
*/
if sqlite3_exec(db, cSQL, nil, nil, nil) != SQLITE_OK {
return false
}
return true
}
2.3執行查詢語句
/**
執行查詢語句
查詢所有的數據
:returns: 查詢到的字典數組
*/
func execRecordSql(sql: String) -> [[String: AnyObject]] {
// 0.將Swift字符串轉換為C語言字符串
let cSQL = sql.cStringUsingEncoding(NSUTF8StringEncoding)!
// 1.準備數據
var stmt: COpaquePointer = nil
/**
* 準備: 理解為預編譯SQL語句, 檢測里面是否有錯誤等等, 它可以提供性能
*
* @param db 已經開打的數據庫對象
* @param cSQL 需要執行的SQL語句
* @param -1 需要執行的SQL語句的長度, 傳入-1系統自動計算
* @param stmt 預編譯之后的句柄, 已經要想取出數據, 就需要這個句柄
* @param nil 一般傳nil
*
* @return
*/
if sqlite3_prepare_v2(db, cSQL, -1, &stmt, nil) != SQLITE_OK
{
print("準備數據失敗")
}
// 準備成功
var records = [[String: AnyObject]]()
// 2.查詢數據
// sqlite3_step代表取出一條數據, 如果取到了數據就會返回SQLITE_ROW
while sqlite3_step(stmt) == SQLITE_ROW
{
// 獲取一條記錄的數據
let record = recordWithStmt(stmt)
// 將當前獲取到的這一條記錄添加到數組中
records.append(record)
}
// 返回查詢到的數據
return records
}
/**
獲取一條記錄的值
- parameter stmt: 預編譯好的SQL語句
- returns: 字典
*/
private func recordWithStmt(stmt: COpaquePointer) ->[String: AnyObject]
{
// 2.1拿到當前這條數據所有的列
let count = sqlite3_column_count(stmt)
// print(count)
// 定義字典存儲查詢到的數據
var record = [String: AnyObject]()
for index in 0..<count
{
// 2.2拿到每一列的名稱
let cName = sqlite3_column_name(stmt, index)
let name = String(CString: cName, encoding: NSUTF8StringEncoding)!
// print(name)
// 2.3拿到每一列的類型 SQLITE_INTEGER
let type = sqlite3_column_type(stmt, index)
// print("name = \(name) , type = \(type)")
switch type
{
case SQLITE_INTEGER:
// 整形
let num = sqlite3_column_int64(stmt, index)
record[name] = Int(num)
case SQLITE_FLOAT:
// 浮點型
let double = sqlite3_column_double(stmt, index)
record[name] = Double(double)
case SQLITE3_TEXT:
// 文本類型
let cText = UnsafePointer<Int8>(sqlite3_column_text(stmt, index))
let text = NSString(CString: cText, encoding: NSUTF8StringEncoding)!
record[name] = text
case SQLITE_NULL:
// 空類型
record[name] = NSNull()
default:
// 二進制類型 SQLITE_BLOB
// 一般情況下, 不會往數據庫中存儲二進制數據
print("")
}
}
return record
}
End
總得來說,SQLite3的使用還是比較麻煩的,因為都是些c語言的函數,理解起來也有些困難。不過在一般開發過程中,使用的都是第三方開源庫 FMDB,它封裝了這些基本的c語言方法,使得我們在使用時更加容易理解,提高開發效率。