概念
FMDB 是 iOS 平台的 SQLite 数据库框架; FMDB 以 OC 的方式封装了 SQLite 的 C 语言 API。以面相对象的方式操作数据库。
FMDB的优点
1) 使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码;
2) 对比苹果自带的 Core Data 框架,更加轻量级和灵活;
3) 提供了多线程安全的数据库操作方法,有效地防止数据混乱;
API讲解:
FMDB的三个主要类别:
FMDatabase
——代表一个 SQLite 数据库。 用于执行 SQL 语句。FMResultSet
——执行查询一个 FMDatabase 的结果集
。FMDatabaseQueue
——在多线程操作更新和查询时,需要使用这个类,它是线程安全的。
非线程安全数据库创建方法:
/** * @brief 创建一个数据库对象 数据库被创建时,数据库文件路径有三种情况: 1)具体文件路径 : 如果不存在会自动创建; 2)空字符串@"" : 会在临时目录创建一个空的数据库,当FMDatabase连接关闭时,数据库文件也被删除; 3)nil : 会创建一个内存中临时数据库,当FMDatabase连接关闭时,数据库会被销毁; * * @param inPath 数据库路径(绝对路径) * * @return 返回对象结果 */ + (instancetype)databaseWithPath:(NSString*)inPath;
/** * @brief 打开数据库 * * @return YES,成功;NO,失败 */ - (BOOL)open;
/** * @brief FMDB 中该方法相当于 `CREATE`,`UPDATE`,`INSERT`,`DELETE`(增删改) * * @param sql 指定执行的 SQL 语句 * * @return YES,执行成功;NO,失败 */ - (BOOL)executeUpdate:(NSString*)sql, ...;
线程安全数据库创建:
/** * @brief 创建数据库实例(查看源码可知:调用该方法后,数据库已打开) * * @param aPath 数据库绝对路径 * * @return 返回数据对线程对象 */ + (instancetype)databaseQueueWithPath:(NSString*)aPath; /** * @brief 同步队列执行数据库操作(dispatch_sync实现,该方法线程安全) * * @param block 回调数据库本身,我们可以在代码块内操作数据库 */ - (void)inDatabase:(void (^)(FMDatabase *db))block;
代码
非线程安全代码:
///============================================================================= /// @name 非线程安全数据库 ///============================================================================= #pragma mark - 创建非线程安全的数据库 - (void)createNonatomicDBWithDbPath:(NSString *)dbPath { // 创建数据库的实例 self.db = [FMDatabase databaseWithPath:dbPath]; // 打开数据库 BOOL flag = [self.db open]; if (flag) { DLog(@"db open success."); } else { DLog(@"db open failed."); } // 创建表 BOOL exeFlag = [self.db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_kingdev (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, phone TEXT NOT NULL, height REAL NOT NULL)"]; if (exeFlag) { DLog(@"CREATE success"); } else { DLog(@"CREATE failed"); } } // 增 - (IBAction)insertInto:(id)sender { BOOL flag = [self.db executeUpdate:@"INSERT INTO t_kingdev (name, phone, height) VALUES (?, ?, ?)", [NSString stringWithFormat:@"张%d", arc4random_uniform(50)], [NSString stringWithFormat:@"%d", arc4random_uniform(20)], [NSNumber numberWithFloat:(arc4random_uniform(1000) / 10.f)]]; if (flag) { DLog(@"INSERT INTO success"); } else { DLog(@"INSERT INTO failed"); } } // 删 - (IBAction)delete:(id)sender { // 删除 id 大于 20 的数据库数据 BOOL flag = [self.db executeUpdate:@"DELETE FROM t_kingdev WHERE id > 20"]; if (flag) { DLog(@"DELETE success"); } else { DLog(@"DELETE failed"); } } // 改 - (IBAction)update:(id)sender { // '张三' ——> 改为 'Flora' [self.db executeQuery:@"update t_kingdev set name = 'Flora' where name = '张三';"]; } // 查 - (IBAction)select:(id)sender { FMResultSet *resultSet = [self.db executeQuery:@"select * from t_kingdev"]; // 从结果集往下找 while ([resultSet next]) { // 根据字段取值 NSString *name = [resultSet stringForColumn:@"name"]; NSString *phone = [resultSet stringForColumn:@"phone"]; double height = [resultSet doubleForColumn:@"height"]; DLog(@"name=%@ phone=%@ height=%f", name, phone, height); } }
///============================================================================= /// @name 数据库多线程安全和事务 ///============================================================================= /** 模拟场景: 甲:500元 乙:1000元 乙给甲转账500元。 */ #pragma mark - 创建线程安全的数据库 - (void)createAtomicDB { NSString *docDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; NSString *dbPath = [docDir stringByAppendingPathComponent:@"t_bank.sqlite"]; // 创建队列且默认开发了数据库 FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:dbPath]; [queue inDatabase:^(FMDatabase *db) { // code····Operate db BOOL flag = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_bank (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, money INTEGER NOT NULL)"]; if (flag) { DLog(@"create t_bank success"); } else { DLog(@"create t_bank failed"); } }]; _queue = queue; } // 增 - (IBAction)at_Add:(id)sender { [_queue inDatabase:^(FMDatabase *db) { BOOL flag = [db executeUpdate:@"INSERT INTO t_bank (name, money) VALUES (?, ?);", @"甲", @500]; [db executeUpdate:@"INSERT INTO t_bank (name, money) VALUES (?, ?);", @"乙", @1000]; if (flag) { DLog(@"INSERT INTO t_bank success"); } else { DLog(@"INSERT INTO t_bank failed"); } }]; } // 删 - (IBAction)at_delete:(id)sender { [_queue inDatabase:^(FMDatabase *db) { BOOL flag = [db executeUpdate:@"DELETE FROM t_bank"]; if (flag) { DLog(@"DELETE t_bank success"); } else { DLog(@"DELETE t_bank failed"); } }]; } /** 操作1:乙 (1000 - 500) 扣500 操作2:甲 (500 + 500) 得500 操作1和操作2必须一起成功,称为一个不可分割的工作单元。所以我们用到了事务 若事务内的操作失败了某个,则数据库回滚到事务之前的状态!! */ // 改 - (IBAction)at_update:(id)sender { [_queue inDatabase:^(FMDatabase *db) { // 开启事务 [db beginTransaction]; // 操作1 BOOL flag1 = [db executeUpdate:@"UPDATE t_bank SET money = 500 WHERE name = '乙';"]; if (flag1) { DLog(@"flag1UPDATE t_bank success"); } else { DLog(@"flag1UPDATE t_bank failed"); // 失败回滚 [db rollback]; } // 操作2 BOOL flag2 = [db executeUpdate:@"UPDATE t_bank SET money = 1000 WHERE name = '甲';"]; if (flag2) { DLog(@"flag2UPDATE t_bank success"); } else { DLog(@"flag2UPDATE t_bank failed"); // 失败回滚 [db rollback]; } // 提交事务 [db commit]; }]; } // 查 - (IBAction)at_slect:(id)sender { [_queue inDatabase:^(FMDatabase *db) { FMResultSet *rs = [db executeQuery:@"SELECT * FROM t_bank"]; while ([rs next]) { DLog(@"name = %@ money = %d", [rs stringForColumn:@"name"], [rs intForColumn:@"money"]); } }]; }
参考:
尊重作者劳动成果,转载请注明: 【kingdev】