swift下FMDB的使用

swift下FMDB的使用
在IOS中可以使用的存储结构有很多,可以使用文件进行缓存,可以使用useDefault实现简单状态的存储,此外IOS下有一个常用的数据库框架–FMDB,它是对于SQLite的一层封装,可以使用它完成基本的增删查改,也可以对此完成事务,回滚等复杂操作
对于FMDB的集成,我们可以直接使用cocoapod完成集成,就像如下的podfile

target 'YouKu' do
	pod 'FMDB'
end

之后,就可以进行操作了,但是由于我使用的是swift,FMDB是使用OC语言封装的库,要使用的话,就应该在项目的根目录下面创建一个bridge.h,在其中导入我们的FMDB
在这里插入图片描述加完以后,我们还需要再info.plist中,进行设置,使得我们的改动内容生效,点击项目–TARGETS–Build Settings,其中有一个设置是swift compiler - General设置其中OC桥接文件为之前自定义的文件
在这里插入图片描述之后的话,就可以直接使用了,我们可以先定义一个公共的操作类,封装数据的初始化和增删查改等操作
先定义好我们的实体类对象

import UIKit

class PersonEntity: NSObject {
    
    var id : Int?;
    var name : String?;
    var age : Int?;
    
    func initParam(id : Int, name : String, age : Int) {
        self.id = id;
        self.name = name;
        self.age = age;
    }
    
    func toString() {
        print("用户信息:id = \(id ?? 0) 姓名 = \(name ?? "") 年龄 = \(age ?? -1)");
    }
}

首先来说,这个类应该是全局的单例对象,所以说,先设置一个static的对象,让其他的类调用来获取到对象,之后,在使用的时候要先打开db对象,这里需要注意的问题就是,对于这个数据库,我们是通过一个文件路径来生成的,如果原先就存在这个数据库,会直接打开这个文件,如果不存在,就会先创建数据库,在进行开启数据库的操作,值得注意的是,在FMDB中,使用数据库以后要及时关闭数据库,所以来说open和close一般是成对出现的

import UIKit

class FMDBManager: NSObject {

    static let shared = FMDBManager();
    
    var dbUrl : String?;
    
    var FMDBEntity : FMDatabase?;
    
    var FMDBQueue : FMDatabaseQueue?;
    
    //打开数据库,也就是创建数据库
    func opendb(dbName : String, tableName : String) {
        //在doc文件夹下面创建新的文件夹,用来储存数据库
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true);
        let docPath = paths[0] + "/\(dbName)";
        self.dbUrl = docPath;
        //创建新的数据库
        self.FMDBEntity = FMDatabase.init(path: self.dbUrl);
        //如果数据库存在,就直接打开,如果不存在,则会现在磁盘上创建数据库,然后再打开
        if self.FMDBEntity!.open() {
            print("数据库打开成功");
            //创建数据表
            self.initTable(t_name: tableName);
        }else {
            print("数据库打开失败");
        }
        //数据库每次操作完成以后就应该释放资源
        self.FMDBEntity!.close();
    }

	func initTable(t_name : String) {
        let sql = "CREATE TABLE IF NOT EXISTS \(t_name)( \n" +
            "id INTEGER PRIMARY KEY AUTOINCREMENT, \n" +
            "name TEXT, \n" +
            "age INTEGER \n" +
        "); \n"
        if self.FMDBEntity!.executeUpdate(sql, withArgumentsIn: []) {
            print("数据表创建成功");
        }else {
            print("数据表创建失败");
        }
    }
 }

这样的话,我们就完成了数据表的创建,如果要进行增删查改,我们就可以借助于FMDatabase执行对应的sql代码,对于FMDB来说,它和一般的数据库没有区别,所以说,原先使用的sql基本上没有变化,下面举个例子,如果我们要增加一条记录,那么需要执行的sql是

insert into table_name (cloum1, cloum2, cloum3...) values(value1, value2, value3...)

接下来,我们就可以编写一个方法来执行我们的sql,一般来说有两种方式

  1. 使用完整sql
  2. 使用占位符,后期填充参数
    在这里,可以直接把参数嵌入到自定义的String中,拼接成完整的sql语句用来执行,也可以使用?作为占位符,我使用的是占位符的方式,方法如下
func insert(tableName : String, param : [String : AnyObject]) {
        //定义增加的sql
        let sql = "insert into \(tableName) (name, age) values(?, ?)";
        //开启数据库连接,执行sql语句
        if self.FMDBEntity!.open() {
            //数据库连接已打开,传入sql和参数
            if self.FMDBEntity!.executeUpdate(sql, withArgumentsIn: ["殷素素", 23]) {
                print("操作完成");
            }else{
                print("操作失败");
            }
        }
        self.FMDBEntity!.close();
    }

而如果要进行查询操作时,我们就会接触到另外一个对象,那就是FMDB中的结果集FMResultSet,这个东西和jdbc中的结果集是相通的,可以进行逐行的遍历,得到每条数据,然后就可以进行相应的解析,在组合成一个完整的对象,也就是下面的方法

func select(tableName : String, param : [String : AnyObject]) -> [PersonEntity] {
        let pId = param["id"];
        //定义增加的sql
        let sql = "select * from \(tableName) where id = ?";
        //开启数据库连接,执行sql语句
        var persons : [PersonEntity] = [];
        if self.FMDBEntity!.open() {
            //数据库连接已打开,传入sql和参数
            if let queryResult = self.FMDBEntity?.executeQuery(sql, withArgumentsIn: [pId ?? 0]) {
                while queryResult.next() {
                    let id = queryResult.int(forColumn: "id");
                    let name = queryResult.string(forColumn: "name") ?? "";
                    let age = queryResult.int(forColumn: "age");
                    let p = PersonEntity();
                    p.initParam(id: Int(id), name: String(name), age: Int(age));
                    persons.append(p);
                }
            }
        }
        self.FMDBEntity!.close();
        return persons;
    }

下面还有删除和修改的代码,没有什么特别之处了,就不再赘述了

func update(tableName : String, param : [String : AnyObject]) {
        //定义修改的sql
        let sql = "update \(tableName) set name = ? where id = ?";
        //开启数据库连接,执行sql语句
        if self.FMDBEntity!.open() {
            //数据库连接已打开,传入sql和参数
            if self.FMDBEntity!.executeUpdate(sql, withArgumentsIn: ["张老道", 1]) {
                print("操作完成");
            }else{
                print("操作失败");
            }
        }
        self.FMDBEntity!.close();
    }

	func delete(tableName : String, param : [String : AnyObject]) {
        //定义删除的sql
        let sql = "delete from \(tableName) where id = ?";
        //开启数据库连接,执行sql语句
        if self.FMDBEntity!.open() {
            //数据库连接已打开,传入sql和参数
            if self.FMDBEntity!.executeUpdate(sql, withArgumentsIn: [1]) {
                print("操作完成");
            }else{
                print("操作失败");
            }
        }
        self.FMDBEntity!.close();
    }

有点意思的这个数据库里面的事务操作,在FMDB中事务操作依赖于一个队列来实现,那就是FMDatabaseQueue,先贴出来代码,然后再分析一下,先是队列的初始化

//执行之前需要先初始化数据库
    func openQueue(dbName : String, tableName : String) {
        let path = self.dbUrl ?? "";
        if path == "" {
            print("要先完成初始化");
            self.opendb(dbName: dbName, tableName: tableName);
        }
        self.FMDBQueue = FMDatabaseQueue.init(path: self.dbUrl);
    }

之后执行批量执行的代码

@objc func batchHandle() {
        print("批量插入");
        FMDBManager.shared.FMDBQueue?.inTransaction({ (db, rollback) in
            let trueTable = "t_Student";
            let errorTable = "t_Sent";
            for i in 40..<50{
                var sql = "insert into \(trueTable) (name, age) values(?, ?)";
                if i == 45 {
                    sql = "insert into \(errorTable) (name, age) values(?, ?)";
                }
                let result : Bool = db.executeUpdate(sql, withArgumentsIn: ["张启山", i]);
                if result {
                    print("插入数据:\(i)");
                }else{
                    print("出现错误,开始回滚");
                    rollback.pointee = true;
                    return ;
                }
            }
        })
    }

所以来说,进行批量查询的代码关键就在inTransaction这个方法中,如果直接点进去,我们会到达源代码中的这个位置
在这里插入图片描述
直接往里跳,会到具体的代码实现

- (void)beginTransaction:(FMDBTransaction)transaction withBlock:(void (^)(FMDatabase *db, BOOL *rollback))block {
    FMDBRetain(self);
    dispatch_sync(_queue, ^() { 
        
        BOOL shouldRollback = NO;

        switch (transaction) {
            case FMDBTransactionExclusive:
                [[self database] beginTransaction];
                break;
            case FMDBTransactionDeferred:
                [[self database] beginDeferredTransaction];
                break;
            case FMDBTransactionImmediate:
                [[self database] beginImmediateTransaction];
                break;
        }
        
        block([self database], &shouldRollback);
        
        if (shouldRollback) {
            [[self database] rollback];
        }
        else {
            [[self database] commit];
        }
    });
    
    FMDBRelease(self);
}

//下面这两段源代码是FMDatabase.m文件里面的函数
- (BOOL)rollback {
    BOOL b = [self executeUpdate:@"rollback transaction"];
    
    if (b) {
        _isInTransaction = NO;
    }
    
    return b;
}

- (BOOL)commit {
    BOOL b =  [self executeUpdate:@"commit transaction"];
    
    if (b) {
        _isInTransaction = NO;
    }
    
    return b;
}

不难看出,接下来,我们会跳转到beginTransaction方法,那这个方法执行内容又是什么呢,其实就只有一行代码

- (BOOL)beginTransaction {
    
    BOOL b = [self executeUpdate:@"begin exclusive transaction"];
    if (b) {
        _isInTransaction = YES;
    }
    
    return b;
}

就是执行了begin exclusive transaction,然后由于我们实现了源代码里面的block,会在这条执行以后粘贴上十条sql,然后根据shouldRollback的值决定最后的代码,我们在自己的代码执行时,通过判断每一插入语句的执行结果,决定返回的布尔值,然后如果每条代码都能执行,就提交commit,所有代码提交生效,否则就提交rollback,所有代码回滚,完成了事务的功能
参考文章
第一篇文章
第二篇文章
第三篇文章
对于更多的FMDB和Model封装操作,可以参考下面这篇文章,所的也很丰富,有兴趣的可以自己研究一下
实体类与数据库表的关联映射

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值