FMDB Swift版使用

FMDB是使用OC语言对原生的SQLite的包装库,可使用原生SQLite语句对其进行操作,且支持多线程从而保证线程安全性。

一:安装使用步骤

SPM: https://github.com/ccgus/fmdb

使用时导入包即可。例如 import FMDB

二:创建DBHelper类

创建单例工具类供访问。

import Foundation
import FMDB
 
class SQLiteManager: NSObject {
     
     
    // Create Single instance.
    private static let manger: SQLiteManager = SQLiteManager()
    class func getInstance() -> SQLiteManager {
     
        return manger
    }
     
    // Database's name.
    private let dbName = "table_basic.db"
     
    // URL
    lazy var dbURL: URL = {
     
        let fileURL = try! FileManager.default
            .url(for: .applicationSupportDirectory, in: .userDomainMask,
                 appropriateFor: nil, create: true)
            .appendingPathComponent(dbName)
        print("------>Database URL:", fileURL)
        return fileURL
    }()
     
    // FMDatabase Object.
    lazy var db: FMDatabase = {
     
        let database = FMDatabase(url: dbURL)
        return database
    }()
     
    // FMDatabaseQueue Object.
    lazy var dbQueue: FMDatabaseQueue? = {
     
        let databaseQueue = FMDatabaseQueue(url: dbURL)
        return databaseQueue
    }()
}

helper类里面存有数据表名称(sqlite对于用户而言无数据库,仅有数据表),每次使用时获取实例对象进行增删改查即可。

三:增删改查类型操作

1.初始化

    @IBAction func initListener(_ sender: Any) {
     
        let db = SQLiteManager.getInstance().db
        if db.open() {
     
            print("DATABASE INIT SUCCEED.")
        } else {
     
            print("ERROR AT: \(db.lastError())")
        }
    }

2.创建表

增删改查均可类使用Java中的预处理方式进行运行。例如,SQL语句已经拼写完成,只需将SQL语句中待定参数替换为?即可,在withArgumentsIn: []将变量对应传入即可。需要说明的是,在FMDB中,只有查询为executeQuery(),其余均为executeUpdate()

    @IBAction func createListener(_ sender: Any) {
     
        let sql = "create table if not exists table_basic (id integer primary key autoincrement, name text);"
        let db = SQLiteManager.getInstance().db
        if db.open(){
     
            try! db.executeUpdate(sql, withArgumentsIn: [])
        } else{
     
            print("ERROR AT \(db.lastError())")
        }
        db.close()
    }

3.增

    @IBAction func addListener(_ sender: Any) {
     
        let sql = "insert into table_basic (id, name) values(?,?);"
        let db = SQLiteManager.getInstance().db
        if db.open(){
     
            try! db.executeUpdate(sql, withArgumentsIn: [2007001, "jack"])
        } else{
     
            print("ERROR AT \(db.lastError())")
        }
        db.close()
    }

4.删

    @IBAction func delListener(_ sender: Any) {
     
        let sql = "delete from table_basic where id = 2007001;"
        let db = SQLiteManager.getInstance().db
        if db.open(){
     
            try! db.executeUpdate(sql, withArgumentsIn: [])
        } else{
     
            print("ERROR AT \(db.lastError())")
        }
        db.close()
    }

当然也可以写成如下预处理方式:

    @IBAction func delListener(_ sender: Any) {
     
        let sql = "delete from table_basic where id = ?;"
        let db = SQLiteManager.getInstance().db
        if db.open(){
     
            try! db.executeUpdate(sql, withArgumentsIn: [2007001])
        } else{
     
            print("ERROR AT \(db.lastError())")
        }
        db.close()
    }

5.改

    @IBAction func updateListener(_ sender: Any) {
     
        let sql = "update table_basic set name = "" where id = ?;"
        let db = SQLiteManager.getInstance().db
        if db.open(){
     
            try! db.executeUpdate(sql, withArgumentsIn: ["rose", 2007001])
        } else{
     
            print("ERROR AT \(db.lastError())")
        }
        db.close()
    }

6.查

这里的查询类似于JDBC的ResultSet,FMDB也存在类似于FMDBResultSet的查询结果集对象。需要说明的是,String类型的数据一般为optional类型,不要忘记解包。

    @IBAction func searchListener(_ sender: Any) {
     
        let sql = "select * from table_basic;"
        let db = SQLiteManager.getInstance().db
        if db.open() {
     
            if let resultSet = db.executeQuery(sql, withArgumentsIn: []) {
     
                while resultSet.next() {
     
                    print("DATA IS \(resultSet.int(forColumn: "id"))")
                    print("DATA IS \(resultSet.string(forColumn: "name")!)")
                }
            }
        } else {
     
            print("ERROR AT \(db.lastError())")
        }
        db.close()
    }

四:多数据类型存储

SQLite不仅可存储常见的date、varchar等类型数据,还可存储二进制类型数据,是为blob。下面将演示
将图片以二进制类型存储至数据库并读取后显示在视图上。
首先创建表的操作如下:

    @IBAction func createListener(_ sender: Any) {
     
        let sql = "create table if not exists table_bin (id integer primary key autoincrement, content_data blob);"
        let db = SQLiteManager.getInstance().db
        if db.open(){
     
            try! db.executeUpdate(sql, withArgumentsIn: [])
        } else{
     
            print("ERROR AT \(db.lastError())")
        }
        db.close()
    }

可以看到这里创建了一个名为content_data的blob类型的字段,其余并无特别之处。
接下来是存储的操作。这里需要说明的是,需要用预处理语句将图片存入(图片需要转换为Data类型)

@IBAction func alterListener(_ sender: Any) {
     
        let sql = "insert into table_bin (id, content_data) values(?,?);"
        let db = SQLiteManager.getInstance().db
        if db.open() {
     
            // Convert UIImage object into Data object.
            let image = UIImage(named: "testimg")
            let data = image?.pngData()
            // Store into preprocessing statement.
            try! db.executeUpdate(sql, withArgumentsIn: [2007003, data])
        } else{
     
            print("ERROR AT \(db.lastError())")
        }
        db.close()
    }

接下来就是读取了,读取时注意数据的解包即可。操作如下:

    @IBAction func searchListener(_ sender: Any) {
     
        let sql = "select * from table_bin;"
        let db = SQLiteManager.getInstance().db
        if db.open() {
     
            if let resultSet = db.executeQuery(sql, withArgumentsIn: []) {
     
                while resultSet.next() {
     
                    let imageData = resultSet.data(forColumn: "content_data")! as? Data
                    self.imageView.image = UIImage(data: imageData!)
                }
            }
        } else {
     
            print("ERROR AT \(db.lastError())")
        }
        
        db.close()
    }

五:事物类操作

FMDB同样支持事物类型的操作。事物具有四大特性(ACID)。这里演示的是事物队列进行批量操作,成功与失败的情况。

    @IBAction func searchListener(_ sender: Any) {
     
        if let queue = SQLiteManager.getInstance().dbQueue {
     
            queue.inTransaction {
      db, rollback in
                do {
     
                    for i in 0..<10 {
     
                        try db.executeUpdate("insert into table_basic (id, name) values (?,?);",
                                             values: [i, "jack"])
                    }
                } catch {
     
                    print("ERROR AND ROLLBACK.")
                    rollback.pointee = true
                }
            }
        }
    }

正常情况下上述内容会全部正常插入。下面是异常情况:

   @IBAction func searchListener(_ sender: Any) {
     
       if let queue = SQLiteManager.getInstance().dbQueue {
     
           queue.inTransaction {
      db, rollback in
               do {
     
                   for i in 0..<10 {
     
                       if i == 5 {
     
                           try db.executeUpdate("insert into table_error (id, name) values (?,?);",
                                                values: [i, "jack"])
                       }
                       try db.executeUpdate("insert into table_basic (id, name) values (?,?);",
                                            values: [i, "jack"])
                   }
               } catch {
     
                   print("ERROR AND ROLLBACK.")
                   rollback.pointee = true
               }
           }
       }
   }

由于设置了回滚,第五次执行的语句是不存在的数据表,正常情况下前面四组插入成功的数据也会被删除,是为回滚。

六:配合Model

七:模糊查询

SQLite支持like、glob语句。like不区分大小写,glob区分大小写。下面是两者的示例:

    @IBAction func searchListener(_ sender: Any) {
     
        let sql = "select * from table_basic where name like '%j%';"
        let db = SQLiteManager.getInstance().db
        if db.open() {
     
            if let resultSet = db.executeQuery(sql, withArgumentsIn: []) {
     
                while resultSet.next() {
     
                    print("DATA IS \(resultSet.int(forColumn: "id"))")
                    print("DATA IS \(resultSet.string(forColumn: "name")!)")
                }
            }
        } else {
     
            print("ERROR AT \(db.lastError())")
        }
        db.close()
    }

glob仅与上者语句不同:

let sql = "select * from table_basic where name glob '*j*';"

八:分组

let sql = "select name from table_basic group by name"

对name进行剔除重复的分组查询,结果中每个不同的name值仅有对应的唯一一条行记录,当然这个语句要看具体的业务逻辑。

九:排序

let sql = "select * from table_basic order by id asc"

对id进行以升序为条件的查询

十:触发器

触发器的创建使用是executeUpdate()语句。假设存在一个这样的业务逻辑:某张表记录用户每次的账号、密码、上次密码。上次密码的作用是防止用户待修改与上次密码重复。那么其创表语句如下:

let sql = "create table if not exists test_trigger(id integer primary key,password varchar(20),pass_password varchar(20))"

触发器语句如下:

    @IBAction func triggerListener(_ sender: Any) {
     
        let sql = "create trigger record_password before update on test_trigger begin insert into test_trigger(pass_password) values(old.password); end"
        let db = SQLiteManager.shareManger().db
        if db.open(){
     
            try! db.executeUpdate(sql, withArgumentsIn: [])
        } else{
     
            print("ERROR AT \(db.lastError())")
        }
        db.close()
    }

需要说明的是不要让触发语句的外部条件为for earch row。这里并不是说在ios的sqlite不支持for each row,而是该条件是可选的。因为大部分情况下触发语句是针对行操作,也就是说外部条件为for each row而内部为update等情况下会造成循环引用。

十一:索引

索引就比较简单了,同样也是executeUpdate(),语句如下:

let sql = "create index my_index on table_basic(id);"

你可能感兴趣的:(iOS,数据库,触发器,swift,ios,xcode)