当前位置: 首页 > 工具软件 > FMDB > 使用案例 >

FMDB的封装与使用

阎晋
2023-12-01

1. FMDB github 地址: FMDB
2. 使用 CocoaPods 导入框架 pod init

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'FMDB' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for FMDB
  pod 'FMDB'
end

3. 命令行指令 pod install --no-repo-update
4. 创建封装类 SQLiteManager.swift

import Foundation
import FMDB

//数据库名称
private let dbName = "my.db"

 
class SQLiteManager{
    //单例
    static let shareManager = SQLiteManager()
    let queue: FMDatabaseQueue?
    
    init(){
        //0.数据库路径 - 全路径 (可读可写)
        var path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last!
        path = (path as NSString).appendingPathComponent(dbName)
        print(path)
        //1.打开数据库队列
        //如果数据不存在,会建立数据库,然后,再创建队列
        //如果数据库存在,会直接创建队列并且打开数据库
        queue =  FMDatabaseQueue(path: path)
        createTable()
    }
    
    //创建数据表
    private func createTable(){
        //1.准备 SQL (只读,创建应用程序时,准备的素材)
        let path = Bundle.main.path(forResource: "db.sql", ofType: nil)!
        let sql = try! String(contentsOfFile: path)
        //2.执行 SQL
        queue?.inDatabase({ (db) in
            //executeStatements 建立多个表 executeUpdate 建立单个表
            if db.executeStatements(sql) {
               print("创表成功")
            }else{
                print("创表失败")
            }
        })
    }
    
    /// 执行 SQL 返回字典数组
    /// - Parameter sql: sql
    /// - Returns: 字典数组
    func execRecordSet(sql: String)-> [[String: Any]]? {
        
       //定义结果[字典数组]
        var result = [[String: Any]]()
        //2. 执行 SQL
        //执行数据库操作 '同步'执行数据库查询 - FMDB 默认情况下,都是在主线程上执行的
        SQLiteManager.shareManager.queue?.inDatabase({ db in
            print(Thread.current)
            guard let rs = db.executeQuery(sql, withArgumentsIn: []) else{
                print("没有结果")
                return
            }
            while rs.next(){
                //1.列数
                let colCount = rs.columnCount
                //创建字典
                var dict  = [String: Any]()
                //2.遍历每一列
                for col in 0..<colCount{
                    //1.列名
                    let name = rs.columnName(for: col)!
                    //2.值
                    let value = rs.object(forColumn: name)
                    //print("name \(name)  -- value \(value)")
                    //设置字典
                    dict[name] = value
                }
                //print(dict)
                result.append(dict)
            }
            //print(result)
        })
        //返回结果
        return result
    }
}

5. 创建指令文件 db.sql

-- 创建个人表 --
CREATE TABLE IF NOT EXISTS "T_Person" (
  "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  "name" TEXT,
  "age" INTEGER,
  "height" NUMBER
);

-- 创建图书表 --
CREATE TABLE IF NOT EXISTS "T_Book" (
  "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  "bookName" TEXT
);

6. 测试方法

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        //使用 executeStatements 出现问题测试
        //demoInsert2(name: "王小', 0 , 0); DELETE FROM T_Person; -- ")
        //demoInsert4(dict: ["name":"李四","age":18,"height":1.9])
        //demoUpdate(dict: ["id": 10, "name": "老九", "age": 19, "height": 1.9])
       // demoDelete(id: 6)
        //person2()
       // person3()
          manyPerson()
    }
    
    //MARK: - 事务处理
    func manyPerson(){
        //1.准备 SQL
        let sql = "INSERT INTO T_Person (name, age, height) VALUES (:name, :age, :height);"
        let start = CACurrentMediaTime()
        //2.执行 SQL
        // OC 写法 *stop = YES
        //1.Swift 写法 rollback.pointee = true
        //2.FMDB的执行都是同步的!绝大多数开发时,不需要使用 block 回调
        //3.所有大数据的插入,都应该使用事务!
        SQLiteManager.shareManager.queue?.inTransaction({ db, rollback in
            for i in 0..<20000 {
               let name = "张三\(i)"
               let age = 20 + Int.random(in: 0...10)
               let height = String(1.5 + Float(Int.random(in: 0...4))/10)
               let dict:[String: Any] =  ["name":name, "age":age, "height":height]
               if !db.executeUpdate(sql, withParameterDictionary: dict){
                    //回滚
                    rollback.pointee = true
                    break
                }
                //模拟失败
                if i == 1000 {
                    rollback.pointee = true
                    break
                }
            }
        })
        print("完成  \(CACurrentMediaTime() - start)")
    }
    
    //MARK: 数据查询
    func person3(){
        //使用 FMDB 千万不要嵌套使用,否则一定挂掉
        DispatchQueue.global().async {
            //1.准备 SQL
            let sql = "SELECT  name, age, height FROM T_Person;"
            let array = SQLiteManager.shareManager.execRecordSet(sql: sql)
            print(array)
        }
    }
    
    func person2(){
        //1.准备 SQL
        let sql = "SELECT  name, age, height FROM T_Person;"
        //2. 执行 SQL
        SQLiteManager.shareManager.queue?.inDatabase({ db in
            
            guard let rs = db.executeQuery(sql, withArgumentsIn: []) else{
                print("没有结果")
                return
            }
            var result = [[String: Any]]()
            while rs.next(){
                //1.列数
                let colCount = rs.columnCount
                //创建字典
                var dict  = [String: Any]()
                //2.遍历每一列
                for col in 0..<colCount{
                    //1.列名
                    let name = rs.columnName(for: col)!
                    //2.值
                    let value = rs.object(forColumn: name)
                    //print("name \(name)  -- value \(value)")
                    //设置字典
                    dict[name] = value
                }
                //print(dict)
                result.append(dict)
            }
            
            print(result)
        })
    }
    
    func person1(){
        //1.准备 SQL
        let sql = "SELECT id, name, age, height FROM T_Person;"
        //2.执行 SQL
        SQLiteManager.shareManager.queue?.inDatabase({ db in
            guard let rs = try? db.executeQuery(sql, values: nil) else{
                print("没有结果")
                return
            }
            //逐行便利所有的数据结果 next 表示还有下一行
            while rs.next(){
                //使用的函数名称 取决于,需要的“返回值”
                let id = rs.int(forColumn: "id")
                let name = rs.string(forColumn: "name")
                let age =  rs.int(forColumn: "age")
                let height = rs.double(forColumn: "height")
                print("\(id) _ \(name) _ \(age) _ \(height)")
            }
        })
    }
    
    //MARK: - 基本数操作
    //删除操作
    func demoDelete(id: Int){
        //1.准备 SQL
        let sql = "DELETE FROM T_Person WHERE id=:id;"
        //2.执行 SQL
        SQLiteManager.shareManager.queue?.inDatabase({ db in
//            do{
//                try db.executeUpdate(sql, values: [id])
//                print("删除成功")
//            } catch{
//                print("删除失败")
//            }
            if db.executeUpdate(sql, withArgumentsIn: [id]){
                if db.changes > 0 {
                    print("删除成功")
                }else{
                    print("删除失败")
                }
            }else{
                print("执行失败")
            }
            //db.executeUpdate(sql, withArgumentsIn: [id])
        })
        print("OK")
    }
    
    //更新操作
    func demoUpdate(dict:[String: Any]){
        //1.准备 SQL
        let sql = "UPDATE T_Person set name = :name, age = :age, height = :height \n" +
        " WHERE id = :id;"
        SQLiteManager.shareManager.queue?.inDatabase({ db in
            if db.executeUpdate(sql, withParameterDictionary: dict){
                if db.changes > 0{
                    print("修改成功")
                }else{
                    print("修改失败")
                }
            }else{
                print("执行失败")
            }
        })
        print("OK")
    }
    
    func demoInsert4(dict: [String: Any]){
        /**
           FMDB 的特殊语法
           :key -> 与字典中的 key 相对应
         */
        let sql = "INSERT INTO T_Person (name, age, height) VALUES (:name, :age, :height);"
        SQLiteManager.shareManager.queue?.inDatabase({ db in
            if db.executeUpdate(sql, withParameterDictionary: dict) {
                print("执行 成功 自增长id - \(db.lastInsertRowId)")
            }else{
                print("插入失败")
            }
        })
       print("OK")
    }
    
    //由于注入的原因,现在开发中,数据操作时,通常会使用绑定参数的形式
    func demoInsert3(){
        //1.SQL
        /**
           ? 表示占位符号
           - SQLite 首先编译 SQL,再执行的时候,动态绑定数据,同样可以避免 “注入”
           - 使用占位符操作,不需要单引号
         */
        let sql = "INSERT INTO T_Person (name, age, height) VALUES (?, ?, ?);"
        SQLiteManager.shareManager.queue?.inDatabase({ db in
            do{
                try db.executeUpdate(sql, values: ["王小', 0 , 0); DELETE FROM T_Person; --", 28, 66])
                print("执行 成功 自增长id - \(db.lastInsertRowId)")
            } catch{
                print("插入失败")
            }
        })
       print("OK")
    }
    
    //SQL "注入" 演示
    func demoInsert2(name: String){
        //1.SQL
        let sql = "INSERT INTO T_Person (name, age, height) VALUES ('\(name)', 18, 1.8);"
        //2.执行SQL
        SQLiteManager.shareManager.queue?.inDatabase({ db in
            //在程序执行中,最好不要使用 executeStatements
            //使用 executeUpdate 会更安全
           // db.executeUpdate(sql, withArgumentsIn: [])
           try? db.executeUpdate(sql, values: nil)
        })
        print("OK")
    }
    
    func demoInsert1(){
        //1.SQL
        let sql = "INSERT INTO T_Person (name, age, height) VALUES ('张三', 18, 1.8);"
        //2.执行SQL
        SQLiteManager.shareManager.queue?.inDatabase({ db in
            //在程序执行中,最好不要使用 executeStatements
            db.executeStatements(sql)
        })
    }
}

 类似资料: