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阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容

  • 使用的过程根据使用的函数大致分为如下几个过程: sqlite3_open() sqlite3_prepare() ...
    随风飘荡的小逗逼阅读 6,519评论 0 3
  • 创建数据库过程需要3个步骤: 1、使用sqlite3_open函数打开数据库; 2、使用sqlite3_exec函...
    lilinjianshu阅读 1,029评论 1 1
  • 什么是SQLite?数据库存储数据的步骤 ●SQLite是一款轻型的嵌入式数据库,它占用资源非常的低,在嵌入式设备...
    飞行的猫阅读 2,490评论 1 7
  • 蕾的婚礼,我的又一次上海之旅
    木西草丁阅读 191评论 0 0
  • 几个分享群同时开课 本是排好了时间 准备晚上去骑行的 忽然发现 搞错了另一个群的分享时间 于是 作罢 CIPT 课...
    Jane_杜阅读 201评论 0 0