用 FMDB 第三方框架操作 SQLite数据库

本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.

SQLite = SQL + Lite(light)
SQL = Structured Query Language (结构化查询语言)

SQLite 是一款轻型的数据库, 具有以下特点:

  1. 嵌入式的
  2. 占用资源少, 运行速度快
  3. Mac 已经内置了 SQLite

关系型数据库的特点

  1. 一个字段(field/col)存储一个值,类似于存储的一个属性
  2. 一行(row)存储一条记录,类似于一个对象
  3. 一个表(table)存储一系列数据,类似于一个对象数组
  4. 多个表之间存在一定的关系,类似于对象之间的关系

相关术语

  1. 字段(field/col),一个字段存储一个值,可以存储 INTEGER ,REAL ,TEXT ,BLOB,NULL 等五种类型的数据

    • SQLite 存储在本质上并不区分数据类型
  2. 主键(primary key)

  • 自动增长,程序员不需要关心
  1. 外键(foreign key)
  • 通过外键对应起和其他"表(table)"的关系

开发数据库的步骤(关键)

  1. 建立数据库 ->有存储数据的文件
  2. 创建表->每一张数据表存储一类数据
  3. 利用 SQL 语句对其进行增删查改,并在 UI 中显示

移动应用中使用数据库的好处

实际上,随着 4G 网络的普及以及数据的更新数据加快,这一优势正在慢慢削弱
  1. 将网络数据存储在本地,不用每次网络下载,节省用户流量
  2. 对本地数据进行查询

使用第三方框架 FMDB 来操作 SQLite 数据库的 Demo

iOS原生的操作数据库的方法十分繁琐, 所以一般使用第三方框架来操作SQLite 数据库.

总体思路导图

总体思路导图

在使用的过程中,需要用到一些常用的 SQL 语句,这些语句对于一些非专业的开发人员来说,不需要去记忆,我们只需要安装 navicat 这个软件,然后会自动生成一些常用的 SQL 命令,我们将其 copy 下来供日后使用

下文即是 demo 的主要步骤

  • 在 pod 中导入 FMDB
  • 在target 中添加FMDatabaseAdditionsVariadic和FMDatabaseVariadic两个 swift 文件

先介绍一下 FMDBDatabase 类

我们首先要创建一个中间管理层对 FMDBDatabase 类进行一个封装
这个中间管理层就叫做 FMDBManager
FMDBManager中的代码

import UIKit
/* 导入 FMDB 框架 (不同的命名空间)*/
import FMDB

class FMDBManager: NSObject {
    
    //--------- 首先创建一个单例(管理工具类) ---------
    static let sharerdManager = FMDBManager()
    
    var db: FMDatabase?
    
    //--------- 创建数据库 ---------
    func openDB() {
        /* 首先给出一个路径 */
        
        /* NSSearchPathForDirectoriesInDomains返回的是一个数组,所以要取出最后一个元素才能获得路径,否则无法拼接出正确的地址,那么在获取路径的时候就获取不到,造成无法创建数据库文件 */
        let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last!
        
        /* 打印出存储路径,供查询使用 */
        print(path)
        /* 给数据库起一个名字 */
        let databaseName = "testSqlite.sqlite"
        /* 将数库的名字拼接在路径后面 */
        let databasePath = "\(path)/\(databaseName)"
        
        //--------- 初始化 db ---------
        db = FMDatabase(path: databasePath)
        
        if db!.open() {
            print("数据连接成功")
            //--------- 创建一个表 ---------
            creatTable()
        }else{
            print("数据连接失败")
        }
    }
    
    
    //--------- 关于FMDBDatabase的使用 ---------
    /* 1. 创建,增删改 均使用 executeUpdate*/
    /* 2. 查询使用 executeQuery方法*/
    
    func creatTable() {
        
        //--------- 执行 sql 语句(不需要记忆)创建一个表格 ---------
        let sql = "CREATE TABLE IF NOT EXISTS t_emotion ('id' integer NOT NULL,'png' text NOT NULL,'text' text NOT NULL,PRIMARY KEY('id'))"
        
        // func executeUpdate(sql:String, _ values: AnyObject...) throws
        
        /* 当看到方法中出现 throws 时,必须做异常处理,否则报错 */
        
        try! db?.executeUpdate(sql, [])
    }

}
  • 我们将对数据库进行增,删,改的操作都交给 模型来处理
    假定一个应用场景是将所有的表情(例如emoji表情)都存储在数据库中,那么我就可以将每一个表情都看作是一个模型,将模型存储在数据库中,先创建一个模型类 EmotionModel 类

EmotionModel 类中的代码:

import UIKit

class EmotionModel: NSObject {
    
    /* 图片 */
    var png:String?
    
    /* 名称 */
    var text:String?
    
    /* kvc 构造函数 */
    init(dict:[String:AnyObject]) {
        
        super.init()
        setValuesForKeysWithDictionary(dict)
        
        
    }
    
    /* 异常处理 */
    override func setValue(value: AnyObject?, forUndefinedKey key: String) {}
}

// MARK:- 对数据库进行相关的操作

extension EmotionModel {
    
    /* 添加数据 */
    func insert () {
        /* 执行添加数据的 sql 语句 */
        
        let sql = "INSERT INTO 't_emotion' ('png', 'text') values ('\(self.png!)', '\(self.text!)')"
        
        try! FMDBManager.sharerdManager.db?.executeUpdate(sql, [])
        
    }
}

  • 这样, 在控制器中,假如我要添加模型 就只需要将 plist 文件中,或者网络加载出的 JSON 数据,字典转模型,再调用模型的对象方法,将模型对象存储到数据库中即可,控制器中的代码如下:
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
//        insertData()
        queryData()

    }

}

    // MARK:- 操作数据,更新 UI

//--------- 在控制器中我们只管理 UI 的相关操作,从数据库中获取数据我们交给模型来操作 ---------

/* 比如我们想在数据库的表中添加数据,就直接交给模型来做 */


extension ViewController {
    /* 先创建100个模型,放到数据库的表中 */
    func insertData() {
        for i in 0..<100 {
            let dict = ["png":"\(i).png", "text":"测试\(i)"]
            
            let emotionModel  = EmotionModel(dict: dict)
            
            emotionModel.insert()
        }
    }
}

  • 如果我需要查询数据,就需要使用到另外一个方法: executeQuery,这个方法是有返回值的,返回值是一个 FMResultSet 对象,假设我们现在有一个应用场景是将数据库中的所有数据打印出来,那么我们就在模型类中提供一个类方法,供外界调用

EmotionModel 类中的补充代码

 class func query ()->[[String : AnyObject]] {
        /* 该 sql 语句是选中表中的某一个 row */
        let sql =  "SELECT * FROM t_emotion"
        /* executeQuery方法的返回值是一个FMResultSet类型的数据 */
        
        
        /* 创建一个可变的数组,数组里面的元素是字典 */
        
        var dataArray : [[String : AnyObject]] = []
        if let result =  try! FMDBManager.sharerdManager.db?.executeQuery(sql, []) {
            while result.next() {
                let png = result.stringForColumn("png")
                let text = result.stringForColumn("text")
                let dict = ["png":png, "text":text]
                dataArray.append(dict)
            }
        }
        
        return dataArray
    }
  • 此时我们在控制器中调用的时候就可以直接调用模型类方法的 API,代码如下:
    func queryData () {
        let dataArray = EmotionModel.query()
        print(dataArray)
    }

再介绍一下 FMDBDatabaseQueue 类

  • 考虑到线程安全问题, 推荐使用 FMDBDatabaseQueue类,其实FMDBDatabaseQueue和 FMDBDatabase 类基本一样,只是多了一个闭包,将我们要执行的数据库操作放到闭包里去实现

  • 同样需要创建一个中间层的管理类,叫做 FMDBQueueManager 类
    代码如下

import UIKit
import FMDB

class FMDBQueueManager: NSObject {
    
    //--------- 同样 ,我们先创建一个管理类的单例 ---------
    static let sharedQueueManager = FMDBQueueManager()
    
    //--------- 我们为这个管理类设置一个属性,是 FMDBDatabaseQueue 类 ---------
    var dbQueue : FMDatabaseQueue?
    
    //--------- 创建并打开数据库的方法 ---------
    func openDB () {
        
        /* 首先给出一个路径 */
        let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).last!
        /* 将路径打印出来供调试使用 */
        
        print(path)
        /* 给数据库起一个名字 */
        let databaseName = "sqliteTestTwo.sqlite"
        /* 拼接路径 */
        let databasePath = "\(path)/\(databaseName)"
        
        /* 初始化 dbQueue */
        dbQueue = FMDatabaseQueue(path: databasePath)
        
        /* 创建一个表 */
        creatTable ()
        
    }

}

extension FMDBQueueManager {
    func creatTable () {
        /* 给出一个创建表的 sql 语句 */
        let sql = "CREATE TABLE IF NOT EXISTS t_emotion ('id' integer NOT NULL,'png' text NOT NULL,'text' text NOT NULL,PRIMARY KEY('id'))"
        
        /* 将执行 sql 语句的操作放到闭包中去执行 */
        dbQueue?.inDatabase({ (FMDatabase) in
            /* 一定要注意,这里是使用 executeUpdate,而不是 executeQuery */
            try! FMDatabase.executeUpdate(sql, [])
        })
    }
}

  • 那么在模型类中的添加数据的代码也将改变,EmotionModel 中的代码如下
    /* 添加数据 */
    func insert () {
        /* 执行添加数据的 sql 语句 */
        
        let sql = "INSERT INTO 't_emotion' ('png', 'text') values ('\(self.png!)', '\(self.text!)')"
        FMDBQueueManager.sharedQueueManager.dbQueue?.inDatabase({ (FMDatabase) in
            try! FMDatabase.executeUpdate(sql, [])
        })
    }
  • 控制器中的代码完全不需要变化
  • 在模型类中,查询数据库的代码也需要作出改变,代码如下:
class func query ()->[[String : AnyObject]] {
        /* 该 sql 语句是选中表中的某一个 row */
        let sql =  "SELECT * FROM t_emotion"
        /* executeQuery方法的返回值是一个FMResultSet类型的数据 */
        
        
        /* 创建一个可变的数组,数组里面的元素是字典 */
        
         var dataArray : [[String : AnyObject]] = []
     FMDBQueueManager.sharedQueueManager.dbQueue?.inDatabase({ (FMDatabase) in
            if let result = try! FMDatabase?.executeQuery(sql, []) {
                while result.next() {
                    let png = result.stringForColumn("png")
                    let text = result.stringForColumn("text")
                    let dict = ["png":png, "text":text]
                    dataArray.append(dict)
                }
            }
        })
        
        return dataArray
    }

运行结果如下

运行结果

PS. 本人有若干成套学习视频, 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容