框架地址:[https://github.com/ccgus/fmdb]
-
下载框架
-
首先要明白框架中几个重要的类
- FMDatabase 代表一个数据库 并且可以使用它执行sqlite语句
- FMDatabaseQueue 是线程安全的存储跟读取数据 这里有一个概念---
事物
- FMResultSet 结果集 从数据库中读取数据的集合
使用之前会发现编译报错,需要先在Build Phases->Link Binary With Libraries->导入libsqlite3.tbd 再次编译就👌
FMDatabase
- 首先我们我们不考虑线程安全下使用FMDatabase来创建数据库实现离线缓存
- 导入
#import "FMDB.h"
- 导入
// 1.获得数据库路径
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"daba.sqlist"];
// 2.创建数据对象
FMDatabase *dataBase = [FMDatabase databaseWithPath:path];
// 3.打开数据库的时候首先判断当前数据库是否存在, 如果不存储在就创建一个并打开, 如果存储在那么就不创建直接打开
BOOL isSuccess = [dataBase open];
if (!isSuccess) {
NSLog(@"打开数据库失败");
}else{
NSLog(@"打开数据库成功");
}
}
创建好数据库之后,接下来就是根据我们的需求在数据库中中创建表了,一个数据库可以创建多张表, 一张表包含多个字段,也就是对应表的
column
, 表是有row跟column
构成, 有点类似Excle表细节不多说 创建一个完整的字段表
// 数据库前提是open状态
/**
* PRIMARY KEY 主键
* AUTOINCREMENT 自动增量
* UNIQUE 唯一
* DEFAULT 默认值
* @param INTEGER 整型
* @param TEXT 文本
* @param REAL 浮点型
* @param
* @param BLOB 二进制
*/
BOOL isSuccess = [dataBase executeUpdate:@"CREATE TABLE IF NOT EXIST t_table (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, age INTEGER NOT NULL UNIQUE DEFAULT 0, score REAL NOT NULL unique, data BLOB);"];
if (!isSuccess) {
NSLog(@"创建表成功");
}else{
NSLog(@"创建表失败");
}
- 表创建完成了,接下来就是我们利用FMDatabase对象要插入数据了, 这里我们了解一下插入数据的常见3中写法
/**此种方式后面拼接的参数必须为对象 不可以为int double等基本数据类型*/
[dataBase executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
/**此种方式后面拼接的参数可以为int double等基本数据类型 因为是使用的占位符*/
[dataBase executeUpdateWithFormat:@"insert into t_table(name, age, score, data) values(%@, %d , %f, %@)", @"jake", 12, 91.0, [NSData data]];
/**此种方式后面拼接的参数必须为对象 不可以为int double等基本数据类型 将对象依次防近视数组*/
[dataBase executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)" withArgumentsInArray:@[@12, @91.0, [NSData data]]];
-注意
当我们存储表中BLOB 对应的字段如果为字典
的时候,这个时候我们不应该把字典
往里存,应当使用[NSKeyedArchiver archivedDataWithRootObject:<#(nonnull id)#>]
将字典转换成NSData
类型, 这样当我们取出数据的时候取出的是NSData类型,再将NSData类型数据通过[NSKeyedUnarchiver unarchiveObjectWithData:<#(nonnull NSData *)#>];
反归档得到就是字典,如果直接存字典,从数据数据库中取出的是字符串
, 字典装模型时候会报错
- 下面再说一下查表, 更行表 删除表数据就不说了,跟插入数据一样 自己研究
- 书写格式几乎相同 就是返回结果是
FMResultSet
类型(结果集), 这时候根据遍历结果集取出每一条数据
/**此种方式后面拼接的参数必须为对象 不可以为int double等基本数据类型*/
[dataBase executeQuery:@"select *From t_table;"];
/**此种方式后面拼接的参数可以为int double等基本数据类型 因为是使用的占位符*/
[dataBase executeQueryWithFormat:@"SELECT name, age, score from t_table where name = %@", @"jake"];
/**此种方式后面拼接的参数必须为对象 不可以为int double等基本数据类型 将对象依次防近视数组*/
[dataBase executeQuery:@"select *from t_table where age > ?" withArgumentsInArray:@[@12]];
- 拿查整个表的全部数据为例
FMResultSet * resultSet = [dataBase executeQuery:@"select *From t_table;"];
while ([resultSet next]) {
// 通过字段获取 文本数据
[resultSet stringForColumn:@"name"];
// 也可以通过Column来获取
[resultSet stringForColumnIndex:2];
// 通过字段获取 对象数据
[resultSet objectForColumnName:@"dict"];
}
- 关于FMResultSet使用结合如下
/* 获取下一个记录 */
- (BOOL)next;
/* 获取记录有多少列 */
- (int)columnCount;
/* 通过列名得到列序号,通过列序号得到列名 */
- (int)columnIndexForName:(NSString *)columnName;
- (NSString *)columnNameForIndex:(int)columnIdx;
/* 获取存储的整形值 */
- (int)intForColumn:(NSString *)columnName;
- (int)intForColumnIndex:(int)columnIdx;
/* 获取存储的长整形值 */
- (long)longForColumn:(NSString *)columnName;
- (long)longForColumnIndex:(int)columnIdx;
/* 获取存储的布尔值 */
- (BOOL)boolForColumn:(NSString *)columnName;
- (BOOL)boolForColumnIndex:(int)columnIdx;
/* 获取存储的浮点值 */
- (double)doubleForColumn:(NSString *)columnName;
- (double)doubleForColumnIndex:(int)columnIdx;
/* 获取存储的字符串 */
- (NSString *)stringForColumn:(NSString *)columnName;
- (NSString *)stringForColumnIndex:(int)columnIdx;
/* 获取存储的日期数据 */
- (NSDate *)dateForColumn:(NSString *)columnName;
- (NSDate *)dateForColumnIndex:(int)columnIdx;
/* 获取存储的二进制数据 */
- (NSData *)dataForColumn:(NSString *)columnName;
- (NSData *)dataForColumnIndex:(int)columnIdx;
/* 获取存储的UTF8格式的C语言字符串 */
- (const unsigned cahr *)UTF8StringForColumnName:(NSString *)columnName;
- (const unsigned cahr *)UTF8StringForColumnIndex:(int)columnIdx;
/* 获取存储的对象,只能是NSNumber、NSString、NSData、NSNull */
- (id)objectForColumnName:(NSString *)columnName;
- (id)objectForColumnIndex:(int)columnIdx;
FMDatabaseQueue
如果项目中开启多个线程同时访问一个数据库的时候,需要考虑到线程安全的问题了,那么就要使用这个类来对数据库操作了, 那么我们还要理解一个概念
事物
常说开启事物 提交事物 回滚事物 是什么意思呢? 为什么需要开启事物呢说个情形就明白了,加入我们像一个数据库表中插入一百条数据, 我们可能会执行一百条SQLite语句,如果当我们执行到一半的时候出错了,那么我们需要
回滚
, 让之前执行的的数据恢复之前数据,也就是一句话,我们需要所有的语句都执行成功才生效,那么我们就需要开启事物
,当确定所有的语句都执行成功就提交事物
, 如果中途出错,那么就回滚事物
首先我们不开启事物,就线程安全来使用FMDB
// 1.获得数据库路径
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"daba.sqlist"];
// 创建FMDatabaseQueue 同时会创建数据库 如果数据库不存在
FMDatabaseQueue *baseQueue = [FMDatabaseQueue databaseQueueWithPath:path];
- 当我们对数据库进行CRUDde的时候调用
- (void)inDatabase:(void (^)(FMDatabase *db))block
, 在block回调给我的FMDatabase对象进行操作 ,操作跟上面一样的 例如
[baseQueue inDatabase:^(FMDatabase *db) {
// 打开数据库 创建表
BOOL isSuccess = [db open];
if (!isSuccess) {
NSLog(@"打开数据库失败");
}else{
NSLog(@"打开数据库成功");
// 插入数据
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
}
}];
- 使用一下开启事物, 加入我们插入同时插入十条数据
// 1.获得数据库路径
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"daba.sqlist"];
// 创建FMDatabaseQueue 同时会创建数据库 如果数据库不存在
FMDatabaseQueue *baseQueue = [FMDatabaseQueue databaseQueueWithPath:path];
[baseQueue inDatabase:^(FMDatabase *db) {
// 打开数据库 创建表
BOOL isSuccess = [db open];
if (!isSuccess) {
NSLog(@"打开数据库失败");
}else{
NSLog(@"打开数据库成功");
// 开启事物
[db beginTransaction];
// 插入数据
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
// 也可以在中途回滚 就是该次对数据库的操作不生效 通常中途一般不回滚
[db rollback];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
// 提交事物
[db commit];
}
}];
- 其实FMDB内部已经帮我们封装好,只需要调用FMDatabaseQueue对象方法
- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block
[baseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
// 如果中途想回滚 就这样操作
*rollback = YES;
[db rollback];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
[db executeUpdate:@"insert into t_table(name, age, score, data) values(?, ? , ?, ?)", @"jake", @12, @91.0, [NSData data]];
}];
- 下班了,不写了,大家如果发现有误,谢谢反馈给我😝😄