FMDB

FMDB

  • 实现客户端数据库操作的第三方框架.

  • 操作数据库的类 : FMDatabase.h

  • 队列调度数据库执行的类 : FMDatabaseQueue.h

  • 查询数据的类 : FMResultSet.h

FMDBFMDatabaseQueue底层是用GCD串行队列调度数据库在同步函数里面操作数据的.

FMDatabaseQueueManager

设计单例模式

  • FMDatabaseQueue设计成单例

  • 目的 : 全局只有一个串行队列,这样操作数据库更加安全

1
2
3
4
5
6
@interface : FMDatabaseQueue
+ (instancetype)sharedManager;
@end
  • 实例化单例的时候,创建数据库和表
    • 只需要创建一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
+(instancetype)sharedManager
{
static FMDatabaseQueueManager *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 1.获取数据库路径
NSString *SQLPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"my.db"];
// 2. 创建queue,管理数据库对象
instance = [FMDatabaseQueueManager databaseQueueWithPath:SQLPath];
// 3.创建数据库表
[instance inDatabase:^(FMDatabase *db) {
BOOL isCreate = [db executeUpdate:@"create table if not exists t_person(id integer primary key,name text not null,age integer)"];
if (isCreate) {
NSLog(@"建表成功");
}
}];
});
return instance;
}

FMDB增删改查

  • 导入头文件 #import "FMDB.h"

  • 操作的类 FMDatabase 单线程时使用

  • 全局的静态数据库对象

    1
    static FMDatabase *_db;

创建数据库和表

  • 数据库的创建和建表只需要执行一次.
  • initialize : 这个类在第一次使用时就被调用一次,后面就不会再调用;而且是先于实例化方法调用的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+ (void)initialize
{
// NSLog(@"这个类在第一次使用时就被调用一次");
// 1.获取数据库路径
NSString *SQLPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"heros.db"];
// 2.创建数据库和建表,只会执行一次
_db = [FMDatabase databaseWithPath:SQLPath];
// 3.打开数据库
BOOL isOpen = [_db open];
if (isOpen) {
// 4.建表
BOOL isCreate = [_db executeUpdate:@"create table if not exists t_heros(id integer primary key,name text not null,age integer)"];
if (isCreate) {
NSLog(@"建表成功");
}
}
}

新增

1
[_db executeUpdateWithFormat:@"insert into t_heros(name,age) values('张三',18)"];

修改

1
[_db executeUpdateWithFormat:@"update t_heros set age = 19 where name = '张三'"];

删除

1
[_db executeUpdateWithFormat:@"delete from t_heros where name = '张三'"];

查询 – 模糊查询

1
2
3
4
5
6
7
8
9
10
// 执行查询语句.获取到结果集
FMResultSet *resultSet = [_db executeQuery:@"select * from t_heros"];
// 遍历结果集,取数据
while ([resultSet next]) {
// 取出来的数据
NSString *name = [resultSet stringForColumn:@"name"];
int age = [resultSet intForColumn:@"age"];
}

如果数据库里面的记录有很多条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
+ (NSArray *)selectHeros
{
// 定义临时的模型数据
NSMutableArray *tmpM = [NSMutableArray array];
// 执行查询语句.获取到结果集
FMResultSet *resultSet = [_db executeQuery:@"select * from t_heros"];
// 遍历结果集,取数据
while ([resultSet next]) {
// 创建模型
Heros *hero = [[Heros alloc] init];
// 取出来的数据
NSString *name = [resultSet stringForColumn:@"name"];
int age = [resultSet intForColumn:@"age"];
// 给模型赋值
hero.name = name;
hero.age = @(age);
// 将模型添加到模型数组
[tmpM addObject:hero];
}
return tmpM.copy;
}

增删改查和事务

1
2
3
4
5
6
@interface ViewController () {
FMDatabaseQueueManager *_manager;
}
@end
1
2
3
4
5
- (void)viewDidLoad {
[super viewDidLoad];
_manager = [FMDatabaseQueueManager sharedManager];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// [self insert01];
// [self insert02];
// [self insert03];
// [self update];
// [self delete];
// [self query];
[self transaction];
}

事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (void)transaction
{
double start = CACurrentMediaTime();
// 1.定义SQL语句
NSString *insertSQL = @"insert into t_person(name,age) values(?,?);";
// 2.队列调度数据库,使用事务插入数据,性能比原生SQLite3要好
[_manager inTransaction:^(FMDatabase *db, BOOL *rollback) {
// 3.循环新增大批量的数据
for (NSInteger i = 0; i < 1000; i++) {
// 4.执行SQL
BOOL isOK = [db executeUpdate:insertSQL,@"张三",@(100)];
// 出错就回滚
if (!isOK) {
// 回滚快照
*rollback = YES;
// 非常重要
break;
}
}
}];
NSLog(@"%f",CACurrentMediaTime()-start);
}

查询语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)query
{
// 1.定义SQL语句
NSString *updateSQL = @"select name,age from t_person;";
// 2.队列调度数据库
[_manager inDatabase:^(FMDatabase *db) {
// 3.执行SQL
FMResultSet *resultSet = [db executeQuery:updateSQL];
// 4.逐条取记录
while ([resultSet next]) {
NSString *name = [resultSet stringForColumn:@"name"];
int age = [resultSet intForColumn:@"age"];
NSLog(@"%@ -- %d",name,age);
}
}];
}

删除语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)delete
{
// 1.定义SQL语句
NSString *updateSQL = @"delete from t_person where id = ?;";
// 2.队列调度数据库
[_manager inDatabase:^(FMDatabase *db) {
// 3.执行SQL
BOOL isOK = [db executeUpdate:updateSQL,@(6)];
if (isOK) {
NSLog(@"删除数据成功");
} else {
NSLog(@"删除数据失败");
}
}];
}

修改语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void)update
{
// 1.定义SQL语句
NSString *updateSQL = @"update t_person set name = ?,age = ? where id = ?;";
// 2.队列调度数据库
[_manager inDatabase:^(FMDatabase *db) {
// 3.执行SQL
BOOL isOK = [db executeUpdate:updateSQL,@"王五",@(19),@(3)];
if (isOK) {
NSLog(@"更新数据成功");
} else {
NSLog(@"更新数据失败");
}
NSLog(@"影响行数 %d", db.changes);
}];
}

新增语句 (预编译 : 正确的方式)

  • 使用预编译指令来插入数据
  • 所谓预编译,提前对 SQL 语句检查并且缓存住
  • 使用 ? 占位
  • 字符串不需要 单引号
  • 在执行的时候,使用executeUpdate的参数替换 ?
  • 提示 : 今后工作中,所有的 SQL 执行,都一定用预编译指令!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (void)insert03
{
NSString *name = @"李四',0);";
// 1.定义SQL语句
NSString *insertSQL = @"insert into t_person(name,age) values(?,?);";
// 2.队列调度数据库
[_manager inDatabase:^(FMDatabase *db) {
// 3.执行SQL
BOOL isOK = [db executeUpdate:insertSQL,name,@(18)];
if (isOK) {
NSLog(@"新增数据成功");
} else {
NSLog(@"新增数据失败");
}
NSLog(@"自增长 ID: %zd", db.lastInsertRowId);
}];
}

新增语句 (SQL注入 : 会出错)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (void)insert02
{
NSString *name = @"李四',0);";
// 1.定义SQL语句
NSString *insertSQL = [NSString stringWithFormat:@"insert into t_person(name,age) values('%@',18);",name];
NSLog(@"%@",insertSQL);
// 2.队列调度数据库
[_manager inDatabase:^(FMDatabase *db) {
// 3.执行SQL
BOOL isOK = [db executeUpdate:insertSQL];
if (isOK) {
NSLog(@"新增数据成功");
} else {
NSLog(@"新增数据失败");
}
}];
}

新增数据 (不靠谱)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)insert01
{
NSString *name = @"李四";
// 1.定义SQL语句
NSString *insertSQL = [NSString stringWithFormat:@"insert into t_person(name,age) values('%@',18);",name];
// 2.队列调度数据库
[_manager inDatabase:^(FMDatabase *db) {
// 3.执行SQL
BOOL isOK = [db executeUpdate:insertSQL];
if (isOK) {
NSLog(@"新增数据成功");
} else {
NSLog(@"新增数据失败");
}
}];
}