iOS数据库使用

作者: 笑忘书丶 | 来源:发表于2016-04-01 15:22 被阅读1047次

    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语言方法,使得我们在使用时更加容易理解,提高开发效率。

    相关文章

      网友评论

        本文标题:iOS数据库使用

        本文链接:https://www.haomeiwen.com/subject/xpjslttx.html