• 【原】iOS学习47之第三方-FMDB


    将 CocoaPods 安装后,按照 CocoaPods 的使用说明就可以将 FMDB 第三方集成到工程中,具体请看博客iOS学习46之第三方CocoaPods的安装和使用(通用方法)

    1. FMDB简介

     1> 概述

    • iOS 中原生的 SQLite API 在进行数据存储的时候,需要使用 C语言 中的函数,操作比较繁琐。于是,就出现了一系列将SQLite API 进行封装的库,例如 FMDBPlausibleDatabaseSQLitePersistentObjects 等。  

    • FMDB 是一款简洁易用的封装库。因此,在这里推荐使用第三方框架 FMDB,它是对 libsqlite3 框架的封装,用起来的步骤与 SQLite 使用类似,并且它对于多线程的并发操作进行了处理,所以是线程安全的。

     2> FMDB优缺点

    • 优点

      对多线程的并发操作进行处理,所以是线程安全的;

      以OC的方式封装了SQLite的C语言API,使用起来更加的方便;

      FMDB是轻量级的框架,使用灵活。

    • 缺点

      因为它是OC的语言封装的,只能在iOS开发的时候使用,所以在实现跨平台操作的时候存在局限性。

     3> FMDB中重要的类

    • FMDatabase:一个 FMDatabase 对象就代表一个单独的 SQLite数据库,用来执行 SQL语句

    • FMResultSet:使用 FMDatabase 执行查询后的结果集

    • FMDatabaseQueue:用于在多线程中执行多个查询或更新,它是线程安全的。

     4> FMDB使用步骤

    • 第一步:使用 CocoaPods 将第三方集成到工程项目中

    • 第二步:导入 libsqlite3.0 框架,导入头文件 FMDatabase.h

    • 第三步:代码实现,与 SQLite 使用步骤相似,创建数据库路径获得数据库路径打开数据库,然后对数据库进行增、删、改、查操作,最后关闭数据库

    2. FMDB创建数据库和数据表

    • 第一步:获得数据库文件的路径

      创建FMDatabase对象时参数为SQLite数据库文件路径,该路径可以是以下三种方式之一:

       ① 文件路径。该文件路径无需真实存在,如果不存在会自动创建

       ② 空字符串(@"")。表示会在临时目录创建一个空的数据库,当 FMDatabase 连接关闭时,文件也会被删除

       ③ NULL。将创建一个内在数据库,同样的,当 FMDatabase 连接关闭时,数据将会被销毁

      我们一般采用第一种方式来获得数据库文件的路径,具体实例代码如下:

       NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    
        self.filePath = [documentPath stringByAppendingPathComponent:@"student.sqlite"];
    
        NSLog(@"filePath = %@", self.filePath);
    • 第二步:使用路径初始化FMDB对象

      使用的初始化方法:

       + (instancetype)databaseWithPath:(NSString*)aPath

      实例代码:

        // 第四步:使用路径初始化FMDB对象
        self.database = [FMDatabase databaseWithPath:self.filePath];
    • 第三步:在和数据库交互之前,数据库必须是打开的。

      如果权限不足或者资源不足,则无法打开和创建数据库。

       // 需要判断数据库打开的时候才进行执行语句
       if (self.database.open) {
            // 创建表
        }
    • 第四步:创建表

      使用的执行SQL语句的方法:

        - (BOOL)executeUpdate:(NSString*)sql, ...

      该方法的返回值是一个BOOL值,我们可以根据返回值,来判断SQL语句是否执行成功。

      实例代码:

        if (self.database.open) {
            // 建表语句
            NSString *createSql = @"create table if not exists t_student(id integer primary key autoincrement not null, name text not null, age integer not null, sax text not null)";
            // 执行建表语句,创建数据表
            BOOL result = [self.database executeUpdate:createSql];
            // 判断是否建表成功
            if (result) {
                NSLog(@"创表成功");
            } else {
                NSLog(@"创表失败");
            }
        }
    • 第五步:关闭数据库

      实例代码:

        // 第五步:关闭数据库
        [self.database close];

    3. FMDB实现增、删、改、查

     1> FMDB—执行更新

      一切不是SELECT命令的命令都视为更新。这包括CREAT,UPDATE,INSERT,ALTER,BEGIN,COMMIT,DETACH,DELETE,DROP,END,EXPLAIN,VACUUM,REPLACE等。  

      简单来说,只要不是以 SELECT 开头的命令都是更新命令。

          执行更新返回一个BOOL值。YES表示执行成功,否则表示有错误。你可以调用 -lastErrorMessage 和 -lastErrorCode方法来得到更多信息。

     2> 执行更新命令的相关方法

    • executeUpdate:  不确定的参数用 ? 来占位(后面参数必须是 OC 对象,;代表语句结束,也可以不写)

      增、删、改的代码实例:

    // 增加(插入)数据
    BOOL result = [self.database executeUpdate:@"insert into t_student(name, age, sax) values(?, ?, ?)", @"xiaoming", @12, @""];
    
    // 更新数据
    BOOL result = [self.database executeUpdate:@"update t_student set name = ? where name = ?", @"xiaoming", @"小明"];
    
    // 删除数据
    BOOL result = [self.database executeUpdate:@"delete from t_student where name = ?", @"xiaoming"]; 
    • executeUpdateWithFormat:不确定的参数用%@%d等来占位 (参数为原始数据类型,执行语句不区分大小写)

      增、删、改的代码实例:

    // 增加(插入)数据
    BOOL result = [self.database executeUpdateWithFormat:@"insert into t_student (name, age, sax) values (%@, %i, %@);", @"xiaoming", @69, @""];
    
    // 更新数据
    BOOL result = [self.database executeUpdateWithFormat:@"update t_student set name = %@ where name = %@", @"xiaoming", @"小明"];
    
    // 删除数据
    BOOL result = [self.database executeUpdateWithFormat:@"delete from t_student where name = %@", @"xiaoming"];
    • executeUpdate:withArgumentsInArray:数组,直接使用数组

      增、删、改的代码实例:

    // 增加(插入)数据
    [self.database executeUpdate:@"insert into t_student(name, age, sax) values(?, ?, ?);"  withArgumentsInArray:@[@"xiaoming", @12, @""]];
    
    // 更新数据
    [self.database executeUpdate:@"update t_student set name = ? where name = ?;"  withArgumentsInArray:@[@"xiaoming", @"小明"]];
    
    // 删除数据
    [self.database executeUpdate:@"delete from t_student where name = ?;"  withArgumentsInArray:@[@"xiaoming"]];

      以上的方法大家可以根据自己的习惯和需求选择一种即可。

     3> FMDB—查询数据

      ① 概述  

      SELECT 命令就是查询,执行查询的方法是以 -excuteQuery 开头的。

      执行查询时,如果成功返回 FMResultSet 对象,错误返回 nil 。与执行更新相同,支持使用 NSError 参数。

      同时,你也可以使用 -lastErrorCode 和 -lastErrorMessage 获知错误信息。

      ② FMResultSet

      FMResultSet 提供了很多方法,来获取对应字段的信息:  

        intForColumn:、longForColumn:、longLongIntForColumn:、boolForColumn:、doubleForColumn:、stringForColumn:、dataForColumn:、dataNoCopyForColumn:、UTF8StringForColumnIndex:、objectForColumn:

      ③ 执行查询语句

      查询整个表

        // 查询结果使用的类FMResultSet
        FMResultSet *resultSet = [self.database executeQuery:@"select * from t_student"];

      根据条件查询

        //根据条件查询
        FMResultSet *resultSet = [self.db executeQuery:@"select * from t_student where id<?;", @14];

      ④ 遍历结果集合

        while (resultSet.next) {
            NSInteger ID = [resultSet intForColumn:@"id"];
            NSString *name = [resultSet objectForColumnName:@"name"];
            NSInteger age = [resultSet intForColumn:@"age"];
            NSString *sax = [resultSet objectForColumnName:@"sax"];
            
            NSLog(@"id = %ld name = %@, age = %ld, sax = %@", ID, name, age, sax);
        }

     4> 完整实例代码

    #import "ViewController.h"
    
    // 第一步:引入框架,引入支持的类库(libsqlite3.0.tbd)
    #import <FMDB.h>
    
    @interface ViewController ()
    
    /// 声明数据库对象
    @property (nonatomic, strong) FMDatabase *database;
    
    /// 声明存储路径
    @property (nonatomic, strong) NSString *filePath;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    
        [self createTabe];
    }
    
    #pragma mark - 创建表
    - (void)createTabe
    {
        // 第一步:创建sql语句
        NSString *createSql = @"create table if not exists t_student(id integer primary key autoincrement not null, name text not null, age integer not null, sax text not null)";
        
        // 第二步:找到存储路径
        NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    //    NSLog(@"document = %@", documentPath);
        self.filePath = [documentPath stringByAppendingPathComponent:@"student.sqlite"];
        NSLog(@"filePath = %@", self.filePath);
        
        // 第三步:使用路径初始化FMDB对象
        self.database = [FMDatabase databaseWithPath:self.filePath];
        
        // 第四步:数据库执行相关的操作
        // 需要判断数据库打开的时候才进行执行语句
        if (self.database.open) {
            BOOL result = [self.database executeUpdate:createSql];
            if (result) {
                NSLog(@"创表成功");
            } else {
                NSLog(@"创表失败");
            }
        }
        
        // 第五步:关闭数据库
        [self.database close];
    }
    
    #pragma mark - 插入
    - (IBAction)insertIntoAction:(id)sender
    {
        // 第一步:打开数据库
        [self.database open];
        
        // 第二步:进行相关的操作
        NSArray *nameArray = @[@"MBBoy", @"BoomSky", @"小明"];
        
        for (NSString *name in nameArray) {
    
            BOOL result = [self.database executeUpdate:@"insert into t_student(name, age, sax) values(?, ?, ?)", name, @69, @""]; // integer的数据不能在这里使用,必须使用一个对象型数据,比如NSNumber、NSString...
            
            [self.database executeUpdate:@"INSERT INTO t_student(name, age, sax) VALUES  (?, ?, ?);"  withArgumentsInArray:@[@"xiaoming", @12, @""]];
            
            if (result) {
                NSLog(@"插入成功");
            } else {
                NSLog(@"插入失败");
            }
        }
        [self.database close];// 更新数据// 删除数据// 增加(插入)数据
    }
    
    #pragma mark - 更新
    - (IBAction)updateAction:(id)sender
    {
        [self.database open];
        
        BOOL result = [self.database executeUpdate:@"update t_student set name = ? where name = ?", @"xiaoming", @"小明"];
        
        if (result) {
            NSLog(@"更新成功");
        } else {
            NSLog(@"更新失败");
        }
        
        [self.database close];
    }
    
    #pragma mark - 删除
    - (IBAction)deleteAction:(id)sender
    {
        [self.database open];
        BOOL result = [self.database executeUpdate:@"delete from t_student where name = ?", @"MBBoy"];
        if (result) {
            NSLog(@"删除成功");
        } else {
            NSLog(@"删除失败");
        }
        [self.database close];
    }
    
    #pragma mark - 查询
    - (IBAction)selectAction:(id)sender
    {
        [self.database open];
        
        // 查询结果使用的类FMResultSet
        FMResultSet *resultSet = [self.database executeQuery:@"select * from t_student"];
        
        // 遍历出需要的结果内容
        while (resultSet.next) {
            NSInteger ID = [resultSet intForColumn:@"id"];
            NSString *name = [resultSet objectForColumnName:@"name"];
            NSInteger age = [resultSet intForColumn:@"age"];
            NSString *sax = [resultSet objectForColumnName:@"sax"];
            
            NSLog(@"id = %ld name = %@, age = %ld, sax = %@", ID, name, age, sax);
        }
        
        [self.database close];
    }
    
    @end

    4. FMDB实现多线程操作

     1> 概述

    • 如果应用中使用了多线程操作数据库,那么就需要使用 FMDatabaseQueue 来保证线程安全了。 应用中不可在多个线程中共同使用一个 FMDatabase 对象操作数据库,这样会引起数据库数据混乱(例如使用两个线程同时对数据库进行更新和查找)。

    • 多个线程更新相同的资源导致数据竞争时使用等待队列(等待现在执行的处理结束)。

    • 队列的形式添加是 FMDB 比较常用的添加方式

    • FMDB 不支持多个线程同时操作,一般使用串行方式实现相关的操作。

     2> 创建操作队列

      使用的初始化方法

    + (instancetype)databaseQueueWithPath:(NSString*)aPath

     3> 把操作打包放在操作队列中

      打包的方法

    - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block

      在 Block 中添加串行队列

     4> 实例代码

    #pragma mark - 以队列的形式添加很多学生
    - (IBAction)insertManyStudent:(id)sender
    {
        // 以队列的形式添加学生是FMDB比较常用的添加方式
        // FMDB不支持多个线程同时操作,一般使用串行方式实现相关的操作
        
        [self.database open];
        
        // 第一步:创建操作队列
        FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:self.filePath];
        
        // 标识:记录是否操作成功
        __block BOOL isSucceed = YES;
        
        // 第二步:把操作打包放在操作队列中
        NSString *insertSql = @"insert into t_studen(name, age, sax) values(?, ?, ?)";
        
        [queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
            // 串行队列
            isSucceed &= [db executeUpdate:insertSql, @"隔壁老王", @38, @""];
            isSucceed &= [db executeUpdate:insertSql, @"Black", @18, @""];
            isSucceed &= [db executeUpdate:insertSql, @"-1", @23, @""];
            
            if (!isSucceed) {
                
                // block 返回的参数rollback进行处理(BOOL类型的指针)
                *rollback = YES;
                return;
            } else {
                NSLog(@"以队列的形式添加成功");
            }
        }];
        
        [self.database close];
    }
  • 相关阅读:
    考研级《计算机网络》知识梳理——第五期
    考研级《计算机网络》知识梳理——第三期
    考研级《计算机网络》知识梳理——第九期
    考研级《计算机网络》知识梳理——第八期
    考研级《计算机网络》知识梳理——第四期
    考研级《计算机网络》知识梳理——第十期
    如果你起走得更远!
    vim列编辑模式!
    CSS深入之label与input对齐!
    js中array的sort()方法!
  • 原文地址:https://www.cnblogs.com/gfxxbk/p/5542528.html
Copyright © 2020-2023  润新知