iOSiOS-Developer-OC

iOS数据库使用

2016-04-01  本文已影响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() 方法查询数据。因为查询数据必须要获得查询结果,所以查询相对比较麻烦。有以下几个步骤:

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

上一篇下一篇

猜你喜欢

热点阅读