iOS開發數據庫-FMDB

 前言

FMDB是以OC的方式封裝了SQLite的C語言API,使用起來更加面向對象,省去了不少麻煩、冗餘的C語言代碼;對比蘋果自帶的Core Data框架,更加輕量級和靈活;提供了多線程安全的數據庫操做方法,有效地防止數據混亂;FMDB同時兼容ARC和非ARC工程,在編譯的時候會自動根據工程配置來調整相關的內存管理代碼。html

使用方法

FMDB有三個主要的類sql

  • FMDatabase 表示一個單獨的SQLite數據庫。 用來執行SQLite的命令。
  • FMResultSet 表示FMDatabase執行查詢後的結果集
  • FMDatabaseQueue 若是你想在多線程中執行多個查詢或更新,你應該使用該類。這是線程安全的。

數據庫建立

經過指定SQLite數據庫文件路徑來建立FMDatabase對象數據庫

FMDatabase *db = [FMDatabase databaseWithPath:path];
if (![db open]) {
  NSLog(@"數據庫打開失敗!");
}

 

建立FMDatabase對象時參數爲SQLite數據庫文件路徑。該路徑能夠是如下三種之一:json

一、具體文件路徑數組

  • 該文件路徑無需真實存,若是不存在會自動建立。

二、空字符串@""安全

  • 表示會在臨時目錄建立一個空的數據庫,當FMDatabase 連接關閉時,數據庫文件也被刪除。

三、NULLruby

  • 會建立一個內存中臨時數據庫,當FMDatabase鏈接關閉時,數據庫會被銷燬

打開數據庫

在和數據庫交互 以前,數據庫必須是打開的。若是資源或權限不足沒法打開或建立數據庫,都會致使打開失敗,返回BOOL類型多線程

if (![db open]) {    
        [db release];   
        return;    
 }

 

執行更新

  • 一切不是SELECT命令的命令都視爲更新。這包括 CREATE, UPDATE, INSERT,ALTER,COMMIT, BEGIN, DETACH, DELETE, DROP, END, EXPLAIN, VACUUM, and REPLACE (等)。
    簡單來講,只要不是以SELECT開頭的命令都是UPDATE命令。
  • 執行更新返回一個BOOL值。YES表示執行成功,不然表示有那些錯誤 。你能夠調用 -lastErrorMessage 和 -lastErrorCode方法來獲得更多信息。

使用executeUpdate:方法執行更新app

- (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"]

執行查詢

SELECT命令就是查詢,執行查詢的方法是以 -excuteQuery開頭的。

查詢方法

- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments

 

執行查詢時,若是成功返回FMResultSet對象, 錯誤返回nil. 與執行更新至關,支持使用 NSError**參數。同時,你也能夠使用 -lastErrorCode和-lastErrorMessage獲知錯誤信息。

爲了遍歷查詢結果,你能夠使用while循環。你還須要知道怎麼跳到下一個記錄。使用FMDB,很簡單實現,就像這樣:

FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];   
while ([s next]) {   
    //retrieve values for each record   
}

你必須一直調用 -[FMResultSet next] 在你訪問查詢返回值以前,甚至你只想要一個記錄:
FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"];   
if ([s next]) {    
    int totalCount = [s intForColumnIndex:0];   
}

 

FMResultSet 提供了不少方法來得到所需的格式的值:

intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumnIndex:
objectForColumn:

 

這些方法也都包括 {type}ForColumnIndex 的這樣子的方法,參數是查詢結果集的列的索引位置。
你無需調用 [FMResultSet close]來關閉結果集, 當新的結果集產生,或者其數據庫關閉時,會自動關閉。

關閉數據庫

當使用完數據庫,你應該 -close 來關閉數據庫鏈接來釋放SQLite使用的資源。
[db close];

使用FMDatabaseQueue 及線程安全

在多個線程中同時使用一個FMDatabase實例是不明智的。如今你能夠爲每一個線程建立一個FMDatabase對象。 不要讓多個線程分享同一個實例,它沒法在多個線程中同時使用。 若此,壞事會常常發生,程序會時不時崩潰,或者報告異常,或者隕石會從天空中掉下來砸到你Mac Pro. 總之很崩潰。因此,不要初始化FMDatabase對象,而後在多個線程中使用。請使用 FMDatabaseQueue,它是你的朋友並且會幫助你。如下是使用方法:
首先建立隊列

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];

這樣使用

[queue inDatabase:^(FMDatabase *db) {    
      [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];    
      [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];    
      [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];    
      FMResultSet *rs = [db executeQuery:@"select * from foo"];    
      while([rs next]) {   
         …    
      }    
}];

 

像這樣,輕鬆地把簡單任務包裝到事務裏

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {    
        [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];    
        [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];    
        [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];    
        if (whoopsSomethingWrongHappened) {    
                *rollback = YES; return;    
        }   
        // etc…    
        [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]];    
}];

 

FMDatabaseQueue將運行在一個序列化隊列塊。因此若是你從多個線程在同一時間調用FMDatabaseQueue的方法,他們將執行他們收到的指令。這樣查詢和更新不會相互影響,每個都是快樂的。

代碼示例

FMDBDataBase.h

//  FMDBDataBase.h
#import <Foundation/Foundation.h>
@interface FMDBDataBase : NSObject

// 獲取數據庫管理對象單例的方法
+ (FMDBDataBase *)sharedDataBase;
// 關閉數據庫
- (void)closeDataBase;
// 清空數據庫
- (BOOL)deleteDataBase;
// 向搜索記錄表中插入新紀錄
- (BOOL)insertSearchText:(NSString *)searchText;
// 查詢數據庫中是否包含當前搜索記錄
- (BOOL)isExistSearchText:(NSString *)searchText;
// 獲取全部搜素記錄
- (NSMutableArray *)getAllSearchText;
// 刪除全部搜索記錄
- (BOOL)deleteAllSearchText;

@end

 

FMDBDataBase.m

#import "FMDBDataBase.h"
#import "FMDB.h"

@interface FMDBDataBase ()

@property (nonatomic, strong) FMDatabase *fmDataBase;

@end
@implementation FMDBDataBase


+ (FMDBDataBase *)sharedDataBase
{
    static FMDBDataBase *fmdbDataBase = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        fmdbDataBase = [[FMDBDataBase alloc] init];
    });
    return fmdbDataBase;
}

- (instancetype)init
{
    if (self = [super init]) {
        _fmDataBase = [FMDatabase databaseWithPath:[self getDataBasePath]];
        // 若是數據庫打開失敗返回空值
        if (![_fmDataBase open]) {
            return nil;
        }
    }
    // 若是數據庫打開成功 建立表
    // 建立搜索歷史記錄表
    NSString *searchHistorySql = @"CREATE TABLE IF NOT EXISTS t_history(t_id integer PRIMARY KEY AUTOINCREMENT, searchText text NOT NULL, age integer NOT NULL)";
    BOOL isSuc = [_fmDataBase executeUpdate:searchHistorySql];

    if (isSuc) {
        NSLog(@"建立成功!");
    }
    return self;
}


- (NSString *)getDataBasePath
{
    // 1.得到數據庫文件的路徑
    NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
//    NSString *doc = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
    NSString *fileName = [doc stringByAppendingPathComponent:@"fmdb.sqlite"];
    return fileName;
}

// 清空數據庫
- (BOOL)deleteDataBase
{
    NSString *sql = @"DELETE FROM t_history";
    BOOL isSuc = [_fmDataBase executeUpdate:sql];
    if (isSuc) {
        NSLog(@"刪除成功");
        return YES;
    }
    NSLog(@"刪除失敗");
    return NO;
}

// 關閉數據庫
- (void)closeDataBase
{
    if (_fmDataBase) {
        [_fmDataBase close];
    }
}


// 查詢數據庫中是否包含當前搜索記錄
- (BOOL)isExistSearchText:(NSString *)searchText
{
    NSString *sql = @"SELECT * FROM t_history";
    FMResultSet *results = [_fmDataBase executeQuery:sql];
    while (results.next) {
        if ([searchText isEqualToString:[results stringForColumn:@"searchText"]]) {
            return YES;
        }
    }
    return NO;
}

// 搜索記錄表中插入新記錄
- (BOOL)insertSearchText:(NSString *)searchText
{
    if (!searchText || [searchText isEqualToString:@""] || [self isExistSearchText:searchText]) {
        NSLog(@"數據爲空或已存在");
        return NO;
    }
    NSString *sql = @"INSERT INTO t_history(searchText) VALUES (?)";
   // executeUpdate : 不肯定的參數用?來佔位
    BOOL isInsertSuc = [_fmDataBase executeUpdate:sql, searchText];
    if (isInsertSuc) {
        NSLog(@"%@插入成功", searchText);
        return YES;
    }
    NSLog(@"%@插入失敗", searchText);
    return NO;
}

// 獲取全部的搜索記錄
- (NSMutableArray *)getAllSearchText
{
    NSString *sql = @"SELECT * FROM t_history order by t_id desc";
    // 保存全部數據的數組
    NSMutableArray *searchTests = [NSMutableArray array];
    FMResultSet *results = [_fmDataBase executeQuery:sql];
    while (results.next) {
        NSString *result = [results stringForColumn:@"searchText"];
        [searchTests addObject:result];
    }
    return searchTests;
}

// 刪除全部的搜索記錄
- (BOOL)deleteAllSearchText
{
    NSString *sql = @"DELETE FROM t_history";
    BOOL isDeleteSuc = [_fmDataBase executeUpdate:sql];
    if (isDeleteSuc) {
        NSLog(@"刪除成功");
        return YES;
    }
    return NO;
}

@end

 

注意

  • 若是ID設置爲逐漸,且設置爲自動增加的話,那麼把表中的數據刪除後,從新插入新的數據,ID的編號不是從0開始,而是接着以前的ID進行編號。

參考文章

歡迎閱讀上一篇:

iOS-Ant-Bang互助社區 426981364iOS技術交流羣 461069757 歡迎加入!

相關文章
相關標籤/搜索