SQLite之三 FMDB
简介
- 什么是FMDB
- FMDB是iOS平台的SQLite数据库框架
- FMDB以OC的方式封装了SQLite的C语言API
- FMDB的优点
- 使用起来更加面向对象,省去了很多麻烦、冗余的C语言代码
- 对比苹果自带的Core Data框架,更加轻量级和灵活
- 提供了多线程安全的数据库操作方法,有效地防止数据混乱
基本使用
- 核心类:FMDB有三个主要的类
- FMDatabase
- 一个FMDatabase对象就代表一个单独的SQLite数据库
- 作用: 用来执行SQL语句
- FMResultSet
- 使用FMDatabase执行查询后的结果集
- FMDatabaseQueue
- 用于在多线程中执行多个查询或更新,它是线程安全的
- FMDatabase
- 打开数据库
-
通过指定SQLite数据库文件路径来创建FMDatabase对象
FMDatabase *db = [FMDatabase databaseWithPath:path]; if (![db open]) { NSLog(@"数据库打开失败!"); }
-
文件路径有三种情况(后面两种都不用)
- 具体文件路径
- 如果不存在会自动创建
- 空字符串@””
- 会在临时目录(沙合中的temp文件下)创建一个空的数据库
- 当FMDatabase连接关闭时,数据库文件也被删除
- nil
- 会创建一个内存中临时数据库,当FMDatabase连接关闭时,数据库会被销毁
- 具体文件路径
-
- 执行更新
- 在FMDB中,除查询以外的所有操作,都称为“更新”
- create、drop、insert、update、delete等
-
使用executeUpdate:方法执行更新(包含了上面的所有操作)
- (BOOL)executeUpdate:(NSString*)sql, ... - (BOOL)executeUpdateWithFormat:(NSString*)format, ... - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
-
示例
[db executeUpdate:@"UPDATE t_student SET age = ? WHERE name = ?;", @20, @"Jack"]
- 在FMDB中,除查询以外的所有操作,都称为“更新”
- 执行查询
-
查询方法
- (FMResultSet *)executeQuery:(NSString*)sql, ... - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
-
示例
// 查询数据 FMResultSet *rs = [db executeQuery:@"SELECT * FROM t_student"]; // 遍历结果集 while ([rs next]) { NSString *name = [rs stringForColumn:@"name"]; int age = [rs intForColumn:@"age"]; double score = [rs doubleForColumn:@"score"]; }
-
FMDatabaseQueue
- FMDatabase这个类是线程不安全的,如果在多个线程中同时使用一个FMDatabase实例,会造成数据混乱等问题,因此在开发中尽量使用这个类进行数据库操作!!!
- 为了保证线程安全,FMDB提供方便快捷的FMDatabaseQueue类
-
FMDatabaseQueue的创建
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:path];
-
简单使用
[queue inDatabase:^(FMDatabase *db) { [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"]; [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"]; [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"]; FMResultSet *rs = [db executeQuery:@"select * from t_student"]; while ([rs next]) { // … } }];
- 使用事务Transaction
- 事务:张三给李四转100元钱
- 发生两件事: 1.张三账户减100 2.李四账户加100
- 若只执行了1,那么张三就亏100元,因此,1.2步骤要么同时成功,要么同时失败。要想达到这种效果,就应该把这两个事件放到事务中。
- 步骤:开启事务,然后将两个操作放到事务中。
- 回滚:若事务中的操作无论哪一步操作失败,都可以申请回滚,一旦回滚,之前事务中的一切操作都无效。
-
核心代码
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) { [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"]; [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"]; [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"]; FMResultSet *rs = [db executeQuery:@"select * from t_student"]; while ([rs next]) { // … } }];
-
事务回滚
*rollback = YES;
代码举例
-
基本使用:
#import "IWViewController.h" #import "FMDB.h" @interface IWViewController () @property (nonatomic, strong) FMDatabase *db; @end @implementation IWViewController - (void)viewDidLoad { [super viewDidLoad]; // 0.获得沙盒中的数据库文件名 NSString *filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"student.sqlite"]; // 1.创建数据库实例对象 self.db = [FMDatabase databaseWithPath:filename]; // 2.打开数据库 if ( [self.db open] ) { NSLog(@"数据库打开成功"); // 创表 BOOL result = [self.db executeUpdate:@"create table if not exists t_student (id integer primary key autoincrement, name text, age integer);"]; if (result) { NSLog(@"创表成功"); } else { NSLog(@"创表失败"); } } else { NSLog(@"数据库打开失败"); } } - (IBAction)insert { for (int i = 0; i<40; i++) { NSString *name = [NSString stringWithFormat:@"rose-%d", arc4random() % 1000]; NSNumber *age = @(arc4random() % 100 + 1); // executeUpdate : 不确定的参数用?来占位 [self.db executeUpdate:@"insert into t_student (name, age) values (?, ?);", name, age]; // executeUpdateWithFormat : 不确定的参数用%@、%d等来占位 //[self.db executeUpdateWithFormat:@"INSERT INTO t_student (name, age) VALUES (%@, %d);", name, arc4random_uniform(40)]; } } - (IBAction)update { [self.db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"]; } - (IBAction)query { // 1.查询数据 FMResultSet *rs = [self.db executeQuery:@"select * from t_student where age > ?;", @50]; // 2.遍历结果集 while (rs.next) { int ID = [rs intForColumn:@"id"]; NSString *name = [rs stringForColumn:@"name"]; int age = [rs intForColumn:@"age"]; NSLog(@"%d %@ %d", ID, name, age); } } @end
-
FMDatabaseQueue的使用
#import "IWViewController.h" #import "FMDB.h" @interface IWViewController () @property (nonatomic, strong) FMDatabaseQueue *queue; @end @implementation IWViewController - (void)viewDidLoad{ [super viewDidLoad]; // 0.获得沙盒中的数据库文件名 NSString *filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"student.sqlite"]; // 1.创建数据库队列(内部已经打开(创建)一个数据库了) self.queue = [FMDatabaseQueue databaseQueueWithPath:filename]; // 2.创表 [self.queue inDatabase:^(FMDatabase *db) { BOOL result = [db executeUpdate:@"create table if not exists t_student (id integer primary key autoincrement, name text, age integer);"]; if (result) { NSLog(@"创表成功"); } else { NSLog(@"创表失败"); } }]; } //插入数据 - (IBAction)insert { [self.queue inDatabase:^(FMDatabase *db) { for (int i = 0; i<40; i++) { NSString *name = [NSString stringWithFormat:@"rose-%d", arc4random() % 1000]; NSNumber *age = @(arc4random() % 100 + 1); [db executeUpdate:@"insert into t_student (name, age) values (?, ?);", name, age]; } }]; } //查询数据 - (IBAction)query { [self.queue inDatabase:^(FMDatabase *db) { // 1.查询数据 FMResultSet *rs = [db executeQuery:@"select * from t_student where age > ?;", @50]; // 2.遍历结果集 while (rs.next) { int ID = [rs intForColumn:@"id"]; NSString *name = [rs stringForColumn:@"name"]; int age = [rs intForColumn:@"age"]; NSLog(@"%d %@ %d", ID, name, age); } }]; } //事物处理的方式1:(自己开启事物,添加内容,提交事物) - (IBAction)update { [self.queue inDatabase:^(FMDatabase *db) { // 1. 开启事务 //SQL语句的开启事物: begin transaction; //[db executeUpdate:@"begin transaction;"]; //FMDB 封装的语法 [db beginTransaction]; //2. 放入到事物中的内容 //第一条 [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"]; //第二条 [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"]; //不设置主动回滚,默认是3条语句都执行完之后,若有一条没有执行成功,则默认会回滚,但是,我们也可以主动回滚(比如在执行第二条时发现有问题,主动回滚) //3. 第二条有问题则主动回滚(在中途执行过程中发信问题,主动回滚) //这里是判断语句 if (@"发现情况不对"){ // 回滚事务 // FMDB 封装的语法 [db rollback]; //SQL语句的回滚事物rollback transaction; //[db executeUpdate:@"rollback transaction;"]; } //第三条 [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"]; //4. 提交事务 [db commit]; //SQL语句的提交事物commit transaction; //[db executeUpdate:@"commit transaction;"]; }]; } //事物处理的方式2:直接使用inTransaction方法 /* - (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block; 内部执行过程 1. 开启事物 2.调用block 3. 提交事物 */ - (IBAction)delete { [self.queue inTransaction:^(FMDatabase *db, BOOL *rollback) { //放入到事物中的内容 //第一条 [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"]; //第二条 [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"]; //中途主动回滚(默认3条执行完之后,任一条失败全部回滚) // if (发现情况不对){ // // 回滚事务 // *rollback = YES; // } //第三条 [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"]; }]; } @end