1、获取操作对象、获取创建数据库
-
1.1 获取操作对象、获取数据库
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
// 数据库管理器
#define CHDbMgr [CHFmdb shareInstance].fmdb
// 数据库管理队列,该对象支持多线程操作
#define CHDbQueue [CHFmdb shareInstance].queue
@interface CHFmdb : NSObject
/** 数据库管理器 */
@property(nonatomic, strong) FMDatabase *fmdb;
/** 数据库管理队列,该对象支持多线程操作 */
@property(nonatomic, strong) FMDatabaseQueue *queue;
+ (instancetype)shareInstance;
@end
NS_ASSUME_NONNULL_END
- CHFmdb.m
- 根据原数据库文件,拷贝数据库到沙盒文件夹,得到应用内部的数据库文件。这种方式通常应用表现在:提供电子书内容源文件用于展示。
#import "CHFmdb.h"
@interface CHFmdb ()
@end
#define DataBaseName @"OutLibrary"
@implementation CHFmdb
+ (instancetype)shareInstance {
static CHFmdb *fmdbMgr = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
fmdbMgr = [[self alloc] init];
// 判断是否存在数据库,如果有,就继续,如果没有就将工程里面的复制到Document
// 获取所有文档路径
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// 获取第一个路径
NSString *docPath = [searchPaths objectAtIndex:0];
// 拼接完整路径
NSString *dbPath = [docPath stringByAppendingPathComponent:@"BookLibrary.db"];
// 创建文件管理器
NSFileManager *fileManager = [[NSFileManager alloc] init];
// 判断该路径上是否存在数据库
BOOL isExit = [fileManager fileExistsAtPath:dbPath];
// 如果不存在,讲工程里面数据库的复制到Document里面
if (!isExit) {
// 获取工程里面的数据库路径
NSString *bundleDBPath = [[NSBundle mainBundle] pathForResource:DataBaseName ofType:@"db"];
// 拷贝工程里面的数据库到沙盒
BOOL success = [fileManager copyItemAtPath:bundleDBPath toPath:dbPath error:nil];
if (success) {
//CHLog(@"数据库复制成功");
}
}
// 根据数据库路径创建数据库管理器
fmdbMgr.fmdb = [[FMDatabase alloc] initWithPath:dbPath];
// 为数据库设置缓存,提高查询效率
[fmdbMgr.fmdb setShouldCacheStatements:YES];
// 根据数据库路径创建数据库管理队列,该对象支持多线程操作
fmdbMgr.queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
});
return fmdbMgr;
}
@end
-
1.2 创建数据库
- CHFmdb.m
- 根据数据库文件路径获取数据库管理器,数据库不存在则自动根据路径创建。这种方式通常应用表现在:保存个人喜好,健康数据等。
#import "CHFmdb.h"
@interface CHFmdb ()
@end
@implementation CHFmdb
+ (instancetype)shareInstance {
static CHFmdb *fmdbMgr = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
fmdbMgr = [[self alloc] init];
// 判断是否存在数据库,如果有,就继续,如果没有就将工程里面的复制到Document
// 获取所有文档路径
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// 获取第一个路径
NSString *docPath = [searchPaths objectAtIndex:0];
// 拼接完整路径
NSString *dbPath = [docPath stringByAppendingPathComponent:@"BookLibrary.db"];
// 根据数据库路径创建数据库管理器
fmdbMgr.fmdb = [[FMDatabase alloc] initWithPath:dbPath];
// 为数据库设置缓存,提高查询效率
[fmdbMgr.fmdb setShouldCacheStatements:YES];
// 根据数据库路径创建数据库管理队列,该对象支持多线程操作
fmdbMgr.queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
});
return fmdbMgr;
}
@end
2、FMDatabase 详细操作
-
2.0 创建表
- 先判断表是否存在,不存在再创建。属性修饰:字符串用->TEXT, 整型值用->INTEGER。
- 创建 t_ReadHistory 表,属性有自动增长唯一的ID, 书本ID:bookId, 书名:bookName, 阅读时间:readTime
if ([CHDbMgr open]) {
NSString *sqlStr = NSStringFormat(@"CREATE TABLE IF NOT EXISTS t_ReadHistory(id INTEGER PRIMARY KEY AUTOINCREMENT, bookId INTEGER, bookName TEXT, readTime TEXT);");
BOOL success = [CHDbMgr executeUpdate:sqlStr];
}
-
2.1 增 insert into
- executeUpdate:不确定的参数用?来占位(后面参数必须是oc对象,“;”代表语句结束)
- 例如基础数据类型的要加 “@(xxx)”转成对象类型NSNumber。
if ([CHDbMgr open]) {
[CHDbMgr executeUpdate:@"insert into t_ReadHistory(bookId, bookName, readTime) values(?,?,?);" , @(1001), @"西游记", @"2020年06月23日"];
}
- executeUpdateWithForamat:不确定的参数用%@,%d等来占位(参数为原始数据类型,执行语句不区分大小写)
if ([CHDbMgr open]) {
[CHDbMgr executeUpdateWithForamat:@"insert into t_ReadHistory(bookId, bookName, readTime) values(%ld, %@, %ld);", @(1001), @"西游记", @"2020年06月23日"];
}
- 参数是数组的使用方式
if ([CHDbMgr open]) {
[CHDbMgr executeUpdate:@"insert into t_ReadHistory (bookId, bookName, readTime) values(?, ?, ?);" withArgumentsInArray:@[@(1001), @"西游记", @"2020年06月23日"]];
}
-
2.2 删 delete
- 不确定的参数用?来占位 (后面参数必须是oc对象,需要将int包装成OC对象)
if ([CHDbMgr open]) {
[CHDbMgr executeUpdate:@"delete from t_ReadHistory where bookId = ?;", @(1001)];
}
if ([CHDbMgr open]) {
[CHDbMgr executeUpdateWithFormat:@"delete from t_ReadHistory where bookName = %@;", @"西游记"];
}
-
2.3 查 select ... from
if ([CHDbMgr open]) {
// FMResultSet结果集
FMResultSet *set = [CHDbMgr executeQuery:@"select bookId, bookName, readTime from t_ReadHistory;"];
// next 返回yes说明有数据
if ([set next]) {
NSInteger bookId = [set intForColumn:@"bookId"];
NSString *bookName = [set stringForColumn:@"bookName"];
NSString *readTime = [set stringForColumn:@"readTime"];
}
else {
CHLog(@"查询出错");
}
}
- 查询说明
- select命令就是查询,执行查询的方法是以-excuteQuery开头的。
- 执行查询时,如果成功返回FMResultSet对象,错误返回nil。
- 与执行更新相当,支持使用NSError参数。
- 同时,你也可以使用-lastErrorCode和-lastErrorMessage获知错误信息。
- FMResultSet获取不同数据格式的方法:
intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumnIndex:
objectForColumn:
if ([CHDbMgr open]) {
// 查符合条件的书的总数
sqlStr = @"select COUNT(*) from t_ReadHistory where bookId = ?;";
NSUInteger bookCount = [CHDbMgr intForQuery:sqlStr, @(1001)];
}
-
2.4 改 update
if ([CHDbMgr open]) {
[CHDbMgr executeUpdate:@"update t_ReadHistory set bookName = ? where bookId = ?", @"红楼梦", @(1001)];
}
if ([CHDbMgr open]) {
// 如果表格存在 则销毁
[CHDbMgr executeUpadate:@"drop BookLibrary if existst t_ReadHistory;"];
}
-
2.6 当添加了一列数据时,如果有需要,获取自增的id:
if ([CHDbMgr open]) {
BOOL success = [CHDbMgr executeUpdate:@"insert into t_ReadHistory(bookId, bookName, readTime) values(?,?,?);" , @(1001), @"西游记", @"2020年06月23日"];
if (success) {
// 此处为刚才添加的自增id号
NSInteger logid = CHDbMgr.lastInsertRowId;
CHLog(@"NSInteger == %ld", logid);
}
}
3、FMDatabaseQueue类 实现多线程操作
- 在多个线程中同时使用一个FMDatabase实例是不明智的。现在你可以为每个线程创建一个FMDatabase对象,不要让多个线程分享同一个实例,他无法在多个线程中同事使用。否则程序会时不时崩溃或者报告异常。所以,不要初始化FMDatabase对象,然后在多个线程中使用。这时候,我们就需要使用FMDatabaseQueue来创建队列执行事务。
- 创建表
// 会通过block传递队列中创建好的数据库给我们
[CHDbQueue inDatabase:^(FMDatabase *db) {
// 编写需要执行的代码
NSString *sqlStr = NSStringFormat(@"CREATE TABLE IF NOT EXISTS t_ReadHistory(id INTEGER PRIMARY KEY AUTOINCREMENT, bookId INTEGER, bookName TEXT, readTime TEXT);");
BOOL success = [db executeUpdate:sqlStr];
if (success) {
CHLog(@"创建表成功");
}
else {
CHLog(@"创建表失败");
}
}];
// 会通过block传递队列中创建好的数据库给我们
[CHDbQueue inDatabase:^(FMDatabase *db) {
// 编写需要执行的代码
[db executeUpdate:@"insert into t_ReadHistory(bookId, bookName, readTime) values(?,?,?);" , @(1001), @"西游记", @"2020年06月23日"];
[db executeUpdate:@"insert into t_ReadHistory(bookId, bookName, readTime) values(?,?,?);" , @(1002), @"三国演义", @"2020年06月23日"];
}];