iOS數據庫使用

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語言方法,使得我們在使用時更加容易理解,提高開發效率。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容

  • 使用的過程根據使用的函數大致分為如下幾個過程: sqlite3_open() sqlite3_prepare() ...
    隨風飄蕩的小逗逼閱讀 6,645評論 0 3
  • 創建數據庫過程需要3個步驟: 1、使用sqlite3_open函數打開數據庫; 2、使用sqlite3_exec函...
    lilinjianshu閱讀 1,035評論 1 1
  • 什么是SQLite?數據庫存儲數據的步驟 ●SQLite是一款輕型的嵌入式數據庫,它占用資源非常的低,在嵌入式設備...
    飛行的貓閱讀 2,514評論 1 7
  • 蕾的婚禮,我的又一次上海之旅
    木西草丁閱讀 211評論 0 0
  • 幾個分享群同時開課 本是排好了時間 準備晚上去騎行的 忽然發現 搞錯了另一個群的分享時間 于是 作罷 CIPT 課...
    Jane_杜閱讀 204評論 0 0