IOS 數據存儲之 SQLite詳解


  在IOS開發中經常會需要存儲數據,對於比較少量的數據可以采取文件的形式存儲,比如使用plist文件、歸檔等,但是對於大量的數據,就需要使用數據庫,在IOS開發中數據庫存儲可以直接通過SQL訪問數據庫,也可以通過ORM進行對象關系的映射,當然也可以選擇使用第三方框架實現對數據庫的操作。在這里,主要來講解一下第一種方式,SQLite。

SQLite

  數據庫有很多,分為重量級和輕量級兩類,移動設備的內存比較小,因此需要選擇輕量級的數據庫。在移動設備上應用程序開發中使用數據庫,比較主流的是SQLite。SQLite支持跨平台,雖然是輕量級的數據庫,但是它的功能很強大,其功能不亞於很多大型的關系數據庫。

  SQLite基於C語言開發的輕型數據庫,因此需要使用C語言語法進行數據庫操作、訪問,不能直接使用OC語言訪問數據庫。此外,SQLite中采用的是動態數據類型,即使創建時定義了一種類型,在實際操作時也可以存儲其他類型,但是推薦建庫時使用合適的類型,特別是應用需要考慮跨平台的情況時。

  SQLite可以直接在代碼中使用,但是在開發的過程中,最好安裝一個可以直接打開數據庫的工具,來驗證對數據庫的操作是否正確,方便程序調試。大家可以去SQLite官方網站下載Mac OSX系統下的命令行工具,也可以使用類似於SQLiteManager、MesaSQLite等工具。

  數據庫的使用,主要包括對數據庫的打開關閉、創建表格、對數據庫中的數據進行增刪改查以及更新等操作,實際上也就是通常所講的SQL語句,SQLite中的SQL語法並沒有太大的差別,因此這里對於SQL語句的內容不過多贅述,大家可以參考其他SQL相關的內容,在這里,我們來詳細講解一下在IOS開發中的SQLite的使用。

SQLite使用步驟:

  1. 在項目中導入libsqlite3.0.dylib框架

  2. 獲取數據庫的路徑,一般是獲得獲得沙盒中Document文件夾路徑,然后拼接得到數據庫路徑

  3. 創建或者打開數據庫,需要注意的是,在打開的時候需要使用C語言,因此需要先將路徑轉為C的字符串,然后通過sqlite3_open()打開數據庫,如果文件存在則直接打開,否則創建並打開。根據sqlite3_open()返回值來判斷是否正確成功打開,如果成功打開,就可以使用得到的sqlite3類型的對象,來對數據庫進行其他操作。

  4. 執行寫好的SQL語句,對數據庫進行操作,執行SQL語句有兩種,一種是無返回值語句,比如創建、增加、刪除等,一種是有返回值的語句,比如查詢。

(1). 對於無返回值的語句(如增加、刪除、修改等)直接通過sqlite3_exec()函數執行,因此可以封裝一個方法,用於除查詢以外的操作

(2). 對於有返回值的語句則首先通過sqlite3_prepare_v2()進行sql語句評估(語法檢測),然后通過sqlite3_step()依次取出查詢結果的每一行數據,對於每行數據都可以通過對應的sqlite3_column_類型()方法獲得對應列的數據,如此反復循環直到遍歷完成。當然,最后需要釋放句柄。

  5. SQLite操作是持久連接,在整個操作過程中無需管理數據庫連接,如果使用完畢,可以選擇通過sqlite3_close()手動關閉數據庫

SQLite使用代碼

   導入框架,如圖所示

//  ViewController.m
//  JRSQLite查詢3
//
//  Created by jerehedu on 15/6/16.
//  Copyright (c) 2015年 jerehedu. All rights reserved.
//

#import "ViewController.h"
#import <sqlite3.h>

@interface ViewController ()
{
    sqlite3 *db;  //聲明數據庫對象
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 獲得數據庫路徑
    // 獲得沙盒中Document文件夾路徑
    NSString *dbPath = [self getUserDocumentPath];
    // 拼接得到數據庫路徑
    NSString *sqlitePath = [dbPath stringByAppendingPathComponent:@"test.sqlite"];
    
    // 創建或打開數據庫
    const char *p = [sqlitePath UTF8String];
    int res = sqlite3_open(p, &db);
    if (res==SQLITE_OK) {
        NSLog(@"db open");
     
        //<一>創建表格
        NSString *sql = @"create table if not exists temps (t_id integer primary key autoincrement,t_name varchar(20) )";
        if ([self execNoQueryWithSQL:sql]) {
            NSLog(@"table temps is created");
        }
        
        //<二>插入數據
        NSString *insert_Sql = @"insert into temps (t_name) values ('pear')";
        if ([self execNoQueryWithSQL:insert_Sql]) {
            NSLog(@"table insert ");
        }
        
        //<三>刪除數據
        NSString *delete_sql = @"delete from temps where t_id=2";
        if ([self execNoQueryWithSQL:delete_sql]) {
            NSLog(@"table delete ");
        }
        
        //<四>修改數據
        NSString *update_sql = @"update temps set t_name='ios' where t_id=1";
        if ([self execNoQueryWithSQL:update_sql]) {
            NSLog(@"table update ");
        }
        
        //<五>查詢簡單的數據1
        NSString *select_sql1 = @"select * from temps where t_id=1";
        sqlite3_stmt *stmt1 = [self execQueryWithSQL:select_sql1];
        while (sqlite3_step(stmt1) == SQLITE_ROW) {
            //按照當前列的類型選數據,列數從0開始
            int t_id = sqlite3_column_int(stmt1, 0);
            const unsigned char *t_name = sqlite3_column_text(stmt1, 1);
            NSString *name = [NSString stringWithUTF8String:(char*)t_name];
            NSLog(@"%i %@",t_id,name);
        }
        //釋放stmt statement
        sqlite3_finalize(stmt1);
        
        //<五>查詢數據2 參數化的sql語句  查找id>2 並且名字以p開頭的
        //用?占位
        int seachId2 = 2;
        NSString *seach_name = @"p%";
        NSString *seach_sql = @"select * from temps where t_id>? and t_name like ?";
        sqlite3_stmt *stmt6 = [self execQueryWithSQL:seach_sql andWithParams:@[[NSNumber numberWithInt:seachId2],seach_name]];
        
        //准備執行(相當於點擊run query),執行的時候是一行一行的執行
        while (sqlite3_step(stmt6) == SQLITE_ROW) {
            //按照當前列的類型選數據,列數從0開始
            int t_id = sqlite3_column_int(stmt6, 0);
            const unsigned char *t_name = sqlite3_column_text(stmt6, 1);
            NSString *name = [NSString stringWithUTF8String:(char*)t_name];
            NSLog(@"..>>>>>>...%i %@",t_id,name);
        }
        
        sqlite3_finalize(stmt6);
    }
    
    // 關閉數據庫
    sqlite3_close(db);
}

#pragma mark - 獲得沙盒sandbox里面document文件夾路徑
- (NSString *)getUserDocumentPath
{
    NSArray *path  = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentPath = [path firstObject];
    return documentPath;
}

#pragma mark - 執行除查找以外的數據庫操作方法
-(BOOL)execNoQueryWithSQL:(NSString*)sql
{
    /*
     執行
     參數1:sqlite3 對象
     參數2:c形式的 sql語句
     參數3:回調函數
     參數4:回調函數的參數
     參數5:錯誤信息(可以char類型指針接受錯誤信息,用來查錯使用)
     */
    if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, NULL) == SQLITE_OK) {
        return YES;
    }
    return NO;
}

#pragma mark 返回查詢結果集(簡單無參)
-(sqlite3_stmt *)execQueryWithSQL:(NSString*)sql
{
    //執行sql語句之后,返回的結果語句
    sqlite3_stmt *stmt;
    /*
     准備執行查詢的sql語句 (相當於把查詢語句寫好)
     參數3:sql語句長度,通常用-1表示(系統會自動計算),也可以用strlength函數計算
     參數4:sql_stmt對象 (執行的對象)
     參數5:未執行的sql語句
     */
    int pre_res = sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, NULL);
    //如果准備成功
    if (pre_res == SQLITE_OK) {
        return stmt;
    }
    return NULL;
}

#pragma mark - 返回查詢結果集(有參數的)
-(sqlite3_stmt *)execQueryWithSQL:(NSString *)sql andWithParams:(NSArray *)params
{
    sqlite3_stmt *stmt;
    int pre_res = sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, NULL);
    if (pre_res == SQLITE_OK) {
        //有參數,循環綁定參數
        if (params!=nil) {
            for (int i=0; i<params.count; i++) {
                
                id obj = params[i];
                //綁定的數據類型可能為NSString或者NSNumber,或者數據為空,分別判斷
                if (obj==nil) {
                    //如果數據為nil
                    sqlite3_bind_null(stmt, i+1);
                }
                else if ([obj respondsToSelector:@selector(objCType)])
                {
                    //當前的綁定的數據類型位NSNumber
                    //NSNumber判斷包裝的是int?longInt?shortInt?float?double?
                    /*
                     strstr(參數1,參數2) (strstr() c中函數搜索一個字符串在另一個字符串中的第一次出現,則該函數返回第一次匹配的字符串的地址,找不到返回NULL)
                     判斷參數1中的字符在參數2的字符串char*中出現的索引
                     [obj objCType] 如果obj是int返回字符串i
                     */
                    if (strstr("ilsILS", [obj objCType])) {
                        /*
                         綁定參數 如果有where
                         參數1:sqlite_stmt對象 (statement結果集)
                         參數2:占位符索引 從1開始
                         參數3:替代占位符的真實參數
                         */
                        sqlite3_bind_int(stmt, i+1, [obj intValue]);
                    }
                    else if (strstr("fdFD", [obj objCType]))
                    {
                        sqlite3_bind_double(stmt, i+1, [obj doubleValue]);
                    }
                    else
                    {
                        stmt = nil;
                    }
                }
                else if ([obj respondsToSelector:@selector(UTF8String)])
                {
                    //當前的綁定的數據類型位NSString 判斷是否有UTF8String方法
                    //用bind替換占位符 索引從1開始
                    sqlite3_bind_text(stmt, i+1, [obj UTF8String], -1, NULL);
                }
                else
                {
                    stmt = nil;
                }
            }
            
            return stmt;
        }
    }
    
    return NULL;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

 

  疑問咨詢或技術交流,請加入官方QQ群:JRedu技術交流 (452379712)

 

作者: 傑瑞教育
出處: http://www.cnblogs.com/jerehedu/ 
本文版權歸煙台傑瑞教育科技有限公司和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM