美文网首页码皇之路之 iOS
sqlite3基本用法(FMDB底层)详解

sqlite3基本用法(FMDB底层)详解

作者: 德蒙_托尔斯泰 | 来源:发表于2016-03-08 23:48 被阅读295次

    看这篇文章之前你得:

    • 已经了解了基本的SQL语法, 基本会使用
      • 推荐一个桌面数据库软件: Navigate Premium(有需要的留言)
    • 知道移动端开发, SQLite是干什么的

    直接把我xcode中的文件考过来了, 已经写得很详细了, (可以直接考到xcode中, 这样就能按分类来看)耐心看吧...

    import UIKit
    
    class SQLiteManager: NSObject {
    
        static let shareInstance: SQLiteManager = SQLiteManager()
        
        /*数据库基本操作:
                1. 数据库文件
                2. 新建表
                3. 指定字段名称
                4. 添加记录
        */
        
        /// 创建数据库文件
        var db: COpaquePointer = nil
        
        
        // MARK:- 打开或创建数据库文件(一般就在appdelegate中打开一次就行了)
        func openDB(dbName: String) -> Bool {
            
            // 1. 生成db文件的路径, 没有就会创建一个
            let path = dbName.cacheDir()
            
            guard let cPath = path.cStringUsingEncoding(NSUTF8StringEncoding) else {
                return false
            }
            
            // 2. 创建数据库文件
            // 专门用户打开数据库文件, 如果不存在系统会自动创建一个新的
            // 第一个参数: 数据库文件的路径
            // 第二个参数: 数据库的指针(句柄), 只要打开成功系统就会将打开的数据库赋值给该指针
            // 后续所有关于数据库的操作, 都依赖于该指针
            // sqlite3_open方法会返回一个整型的值, 告诉我们是否打开成功
            
            if sqlite3_open(cPath, &db) != SQLITE_OK {
                return false
            }
            
            // 3. 新建表
            if !createTable() {
                return false
            }
            
            return true
        }
        
        // MARK:- 创建表
        func createTable() -> Bool {
            
            // 1.定义sql语句(可以在navcat中写好考进来, 换行写, 一目了然)
            let sqlStr = "CREATE TABLE 'T_Person' (" +
            "'name' TEXT," +
            "'age' INTEGER," +
            "'id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT" +
            ");"
            
            // 执行创建表的sql语句
            /*
            第1个参数: 一个已经打开的数据库
            第2个参数: 需要执行的SQL语句
            第3个参数: 执行SQL之后的回调
            第4个参数: 第三个参数的第一个参数
            第5个参数: 错误信息的指针, 如果执行过程中发生错误就会给传入的指针赋值
            */
            
            if sqlite3_exec(db, sqlStr.cStringUsingEncoding(NSUTF8StringEncoding)!, nil, nil, nil) != SQLITE_OK {
                
                return false
            }
            
            return true
        }
        
        // MARK:- 执行sql语句
        /*
        第1个参数: 一个已经打开的数据库
        第2个参数: 需要执行的SQL语句
        第3个参数: 执行SQL之后的回调
        第4个参数: 第三个参数的第一个参数
        第5个参数: 错误信息的指针, 如果执行过程中发生错误就会给传入的指针赋值
        */
        
        /*
          写入: sql语句
            let sqlStr =  "INSERT INTO T_Person \n" +
            "(name, age) \n" +
            "VALUES \n" +
            "('\(name!)', \(age));"
        */
        
        func execSQL(sqlStr: String) -> Bool {
            
            if sqlite3_exec(db, sqlStr.cStringUsingEncoding(NSUTF8StringEncoding)!, nil, nil, nil) != SQLITE_OK {
                return false
            }
            return true
        }
        
        
        // MARK:- 执行查询语句
        /*
          查询语句(条件语句的使用where)
             let sqlStr = "SELECT id, name, age FROM T_Person;"
        */
        func querySQL(sqlStr: String) -> [[String: AnyObject]]? {
                
            // 1. 预编译
            /*
            第一个参数: 一个已经打开的数据库
            第二个参数: 需要执行的SQL语句
            第三个参数: 需要执行的SQL语句的长度, 传入-1系统会自动计算
            第四个参数: 结果集指针(以后所有获取数据的操作, 都依赖于该指针)
            第五个参数: 没用过
            */
    
            // 结果集, 查新结果都依赖此
            var stmt: COpaquePointer = nil
            if sqlite3_prepare_v2(db, sqlStr.cStringUsingEncoding(NSUTF8StringEncoding)!, -1, &stmt, nil) != SQLITE_OK {
                return nil
            }
            
            // 2. 利用结果集取出查询结果
            // 只要是调用一次sqlite3_step方法, 系统就会利用stmt取出一条数据
            // 如果该方法的返回值等于SQLITE_ROW, 就代表可以获取数据
            
            var array = [[String: AnyObject]]()
            
            while sqlite3_step(stmt) == SQLITE_ROW {
                
                // 1. 获取当前一共多少列
                let count = sqlite3_column_count(stmt)
                // 定义一条空数据信息, 用于保存
                var dict = [String: AnyObject]()
                
                for i in 0..<count {
                    // 2. 获取每一列的名称
                    let cName = sqlite3_column_name(stmt, i)
                    let name = String(CString: cName, encoding: NSUTF8StringEncoding)!
                    
                    // 3. 获取每一列的数据类型
                    let type = sqlite3_column_type(stmt, i)
                    
                    switch type
                    {
                    case SQLITE_INTEGER:
                        let intValue = sqlite3_column_int(stmt, i)
                        dict[name] = Int(intValue)
                    case SQLITE_FLOAT:
                        let floatValue = sqlite3_column_double(stmt, i)
                        dict[name] = floatValue
                    case SQLITE_BLOB:
                        print("blob") // blob是二进制数据, 一般不做sqlite数据存储对象
                    case SQLITE_NULL:
                        dict[name] = NSNull()
                    case SQLITE_TEXT:
                        let cTextValue = UnsafePointer<Int8>(sqlite3_column_text(stmt, i))
                        let textValue = String(CString: cTextValue, encoding: NSUTF8StringEncoding)
                        dict[name] = textValue
                    default:
                        print("other")
                    }
                }
                
                array.append(dict)
            }
            return array
            
        }
        
        
        // MARK:- 数据库的事务处理(防止多线程不安全)
                /*
                1.事务能够保证多条SQL语句, 要么一起成功, 要么一起失败
                2.想要保证多条SQL语句一起成功或者一起失败, 就必须在执行多条SQL语句之前开启事物
                只有遇到了提交事务, 数据库才会修改数据
                3.如果中途遇到了意外, 我们可以通过回滚事物来还原以前的数据
                4.提高性能
                */
    
        //      - sqlite3执行sql语句是默认开启事务处理的, 当我们多条数据时, 应手动开启事务, 这样就只要开启一次, 提升性能
        //      - 重复的开启和提交是非常非常消耗性能的, 所以为了避免这种问题, 我们可以自己开启事物, 只要我们自己开了事物 ,系统就不会自动帮我们开启事物了
        
        // 1. 开启事务
        func beginTransaction() -> Bool {
            return execSQL("BEGIN TRANSACTION;")
        }
        
        // 2. 提交事务
        func commit() -> Bool {
            return execSQL("COMMIT TRANSACTION;")
        }
        
        // 3. 回滚事务
        func rollBack() -> Bool {
            return execSQL("ROLLBACK TRANSACTION;")
        }
        
        
        // MARK:- 数据写入之动态绑定插入, 提高性能, 多数SQLite框架都是采用这种方式来写入数据, FMDB也是这么干的
        private let SQLITE_TRANSIENT = unsafeBitCast(-1, sqlite3_destructor_type.self)
        
        func insert(sql: String, args: CVarArgType...) -> Bool
        {
            
            var stmt: COpaquePointer = nil
            // 1.预编译SQL语句
            if sqlite3_prepare_v2(db, sql.cStringUsingEncoding(NSUTF8StringEncoding)!, -1, &stmt, nil) != SQLITE_OK
            {
                print("预编译失败")
                return false
            }
            // 2.绑定参数
            var index: Int32 = 1
            for objc in args
            {
                if objc is Int
                {
                    /*
                    第一个参数: stmt对象
                    第二个参数: 需要绑定的字段的索引
                    注意: 字段索引从1开始
                    第三个参数: 需要绑定到SQL上的值
                    */
                    sqlite3_bind_int64(stmt, index, sqlite3_int64(objc as! Int))
                    
                }else if objc is Double
                {
                    sqlite3_bind_double(stmt, index, objc as! Double)
                }else if objc is String
                {
                    /*
                    第一个参数: stmt对象
                    第二个参数: 需要绑定的字段的索引
                    第三个参数: 需要绑定到SQL上的值
                    第四个参数: 没用过
                    第五个参数: bind方法中如何处理传入的字符串
                    处理方式有两种: 1.方法内部不管传入的字符串, 不会对传入的字符串进行retain(copy)操作, 所以在使用该字符串时字符串可能已经释放了, 此时系统就会随便设置一个值
                    2.方法内部会管理传入的字符串, 所以在使用字符串时字符串不会被释放, 使用完毕之后bind方法内部会自动释放该字符串
                    #define SQLITE_STATIC      ((sqlite3_destructor_type)0)  上述1
                    #define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)  上述2
                    
                    */
                    let text = objc as! String
                    let cText = text.cStringUsingEncoding(NSUTF8StringEncoding)!
                    sqlite3_bind_text(stmt, index, cText, -1, SQLITE_TRANSIENT)
                }
                
                index++
            }
            
            // 3.执行SQL语句
            if sqlite3_step(stmt) != SQLITE_DONE
            {
                print("执行SQL语句失败")
                return false
            }
            // 4.重置STMT
            sqlite3_reset(stmt)
            // 5.关闭STMT
            sqlite3_finalize(stmt)
            
            // 6.返回结果
            return true
        }
        
        // MARK:- sqlite异步执行
        // 1. 创建一个串行队列
        let queue = dispatch_queue_create("sqliteQueue", DISPATCH_QUEUE_SERIAL)
        
        /// 2. 异步执行SQL语句
        func execSQLQueue(action: (db: COpaquePointer) -> ()) {
            
            dispatch_async(queue) { () -> Void in
                action(db: self.db)
                // 在闭包中执行SQL语句
            }
        }
        
    }
    
    

    相关文章

      网友评论

        本文标题:sqlite3基本用法(FMDB底层)详解

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