IOS:FMDB使用databaseQueue实现数据库操作线程安全

sqlite数据库是ios开发中经常使用到的数据持久化方案,因为项目需求的不同,对数据库操作的要求也不同。

由于最近使用sqlite时,有一些地方需要频繁的更新,这时在多线程操作时,其他线程访问数据库会造成程序崩溃,因为之前的框架里设计的数据库管理工具类采用的是单例模式,这样在多线程操作同一个数据库时很容易引起冲突,导致程序崩溃,所以开始寻找多线程下线程安全的办法。

其实FMDB本身已经对多线程做了考虑,FMDatabaseQueue就是为了解决数据库操作线程安全的,只是由于之前框架集成的单例操作,并且没有设计多线程访问,所以并没有发生这个问题。

FMDatabaseQueue解决线程安全的操作方法:

FMDatabaseQueue使用下面这个函数对数据库进行操作,通过描述可知,这样等于是把数据库的操作放到一个串行队列中,从而保证不会在同一时间对数据库做改动。

///-----------------------------------------------
/// @name Dispatching database operations to queue
///-----------------------------------------------

/** Synchronously perform database operations on queue.

 @param block The code to be run on the queue of `FMDatabaseQueue`
 */

- (void)inDatabase:(void (^)(FMDatabase *db))block;

FMDatabaseQueue要使用单例创建,这样多线程调用时,数据库操作使用一个队列,保证线程安全。

@interface DBHelper : NSObject
<pre name="code" class="objc">/**
 *  数据库操作队列
 */

@property(nonatomic,retain,readonly)FMDatabaseQueue *dbQueue;/** * 获取数据库管理类单例 */+(DBHelper *)sharedHelper;


在.m中实现单例创建(不做赘述),并重写dbQueue的get方法

//lazy load
-(FMDatabaseQueue *)dbQueue{
    if (!_dbQueue) {
        _dbQueue = [FMDatabaseQueue databaseQueueWithPath:DB_PATH];
    }
    return _dbQueue;
}

操作数据库时,通过单例的dbQueue在block内执行SQL操作,block属于同步执行,执行完之后才会跳出执行操作之后的语句

DBHelper *dbHelper = [DBHelper sharedHelper];
    __block BOOL res = NO;
    [dbHelper.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *sql = [NSString stringWithFormat:@"INSERT INTO %@(%@) VALUES (%@);", tableName, keyString, valueString];
        res = [db executeUpdate:sql withArgumentsInArray:insertValues];
        self.pk = res?[NSNumber numberWithLongLong:db.lastInsertRowId].intValue:0;
        NSLog([email protected]"插入成功":@"插入失败");
    }];
    return res;

注意:因为队列是串行执行的,因此inDatabase的block并不能嵌套使用,这样会导致错误。

了解了多线程下使用FMDatabaseQueue的操作原理就可以创建一个管理类对模型数据的存取查删进行统一管理,可以使用工具类操作,也可以创建集成NSObject的子类进行管理,需要存取的模型类继承此子类即可。

代码:

//
//  DBBaseModel.h
//  FMDB_TEST
//
//  Created by qihb on 16/4/8.
//  Copyright © 2016年 Qihb. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

/** SQLite五种数据类型 */
#define SQLTEXT     @"TEXT"
#define SQLINTEGER  @"INTEGER"
#define SQLREAL     @"REAL"
#define SQLBLOB     @"BLOB"
#define SQLNULL     @"NULL"
#define PrimaryKey  @"primary key"

#define primaryId   @"pk"

/**
 *  数据库对象的父类
 */
@interface DBBaseModel : NSObject

/** 主键 id */
@property (nonatomic, assign)   int        pk;

@property(nonatomic,copy)NSString *keyWord;                 //查表的关键字字段

/** 列名 */
@property (retain, readonly, nonatomic) NSMutableArray         *columeNames;
/** 列类型 */
@property (retain, readonly, nonatomic) NSMutableArray         *columeTypes;

#pragma --mark functions
/**
 *  获取该类(模型)中的所有属性 runtime
 *
 */
+ (NSDictionary *)getPropertys;

/** 获取所有属性,包括主键 */
+ (NSDictionary *)getAllProperties;

/** 数据库中是否存在表 */
+ (BOOL)isExistInTable;

/** 表中的字段*/
+ (NSArray *)getColumns;

/** 保存或更新
 * 如果不存在主键,保存,
 * 有主键,则更新
 */
- (BOOL)saveOrUpdate;
/** 保存单个数据 */
- (BOOL)save;
/** 批量保存数据 */
+ (BOOL)saveObjects:(NSArray *)array;
/** 更新单个数据 */
- (BOOL)update;
/** 批量更新数据*/
+ (BOOL)updateObjects:(NSArray *)array;
/** 删除单个数据 */
- (BOOL)deleteObject;
/** 批量删除数据 */
+ (BOOL)deleteObjects:(NSArray *)array;
/** 通过条件删除数据 */
+ (BOOL)deleteObjectsByCriteria:(NSString *)criteria;
/** 清空表 */
+ (BOOL)clearTable;

/** 查询全部数据 */
+ (NSArray *)findAll;

/** 通过主键查询 */
+ (instancetype)findByPK:(int)inPk;

/** 查找某条数据 */
+ (instancetype)findFirstByCriteria:(NSString *)criteria;

// 值 为 通过 条件查找  - 返回数组中的第一个
+ (instancetype)findWhereColoum:(NSString *)coloum equleToValue:(NSString *)value;

/** 通过条件查找数据
 * 这样可以进行分页查询 @" WHERE pk > 5 limit 10"
 */
+ (NSArray *)findByCriteria:(NSString *)criteria;

#pragma mark - must be override method
/**
 * 创建表
 * 如果已经创建,返回YES
 */
+ (BOOL)createTable;
/** 如果子类中有一些property不需要创建数据库字段,那么这个方法必须在子类中重写
 */
+ (NSArray *)transients;

//数据是否存在
- (BOOL )isExsistObj;

@end

.m

//
//  DBBaseModel.m
//  FMDB_TEST
//
//  Created by qihb on 16/4/8.
//  Copyright © 2016年 Qihb. All rights reserved.
//

#import "DBBaseModel.h"
#import "DBHelper.h"

#define dbTimeCount @"recent_time"

@implementation DBBaseModel

#pragma mark - override method
+ (void)initialize
{
    if (self != [DBBaseModel self]) {
        [self createTable];
    }
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        NSDictionary *dic = [self.class getAllProperties];
        _columeNames = [[NSMutableArray alloc] initWithArray:[dic objectForKey:@"name"]];
        _columeTypes = [[NSMutableArray alloc] initWithArray:[dic objectForKey:@"type"]];
    }

    return self;
}

#pragma mark - base method
/**
 *  获取该类的所有属性
 */
+ (NSDictionary *)getPropertys
{
    NSMutableArray *proNames = [NSMutableArray array];
    NSMutableArray *proTypes = [NSMutableArray array];
    NSArray *theTransients = [[self class] transients];
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList([self class], &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        //获取属性名
        NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
        if ([theTransients containsObject:propertyName]) {
            continue;
        }
        [proNames addObject:propertyName];
        //获取属性类型等参数
        NSString *propertyType = [NSString stringWithCString: property_getAttributes(property) encoding:NSUTF8StringEncoding];
        /*
         c char         C unsigned char
         i int          I unsigned int
         l long         L unsigned long
         s short        S unsigned short
         d double       D unsigned double
         f float        F unsigned float
         q long long    Q unsigned long long
         B BOOL
         @ 对象类型 //指针 对象类型 如NSString 是@“NSString”

         64位下long 和long long 都是Tq
         SQLite 默认支持五种数据类型TEXT、INTEGER、REAL、BLOB、NULL
         */
        if ([propertyType hasPrefix:@"[email protected]"]) {
            [proTypes addObject:SQLTEXT];
        } else if ([propertyType hasPrefix:@"Ti"]||[propertyType hasPrefix:@"TI"]||[propertyType hasPrefix:@"Ts"]||[propertyType hasPrefix:@"TS"]||[propertyType hasPrefix:@"TB"]) {
            [proTypes addObject:SQLINTEGER];
        } else {
            [proTypes addObject:SQLREAL];
        }

    }
    free(properties);

    return [NSDictionary dictionaryWithObjectsAndKeys:proNames,@"name",proTypes,@"type",nil];
}

/** 获取所有属性,包含主键pk */
+ (NSDictionary *)getAllProperties
{
    NSDictionary *dict = [self.class getPropertys];

    NSMutableArray *proNames = [NSMutableArray array];
    NSMutableArray *proTypes = [NSMutableArray array];
    [proNames addObject:primaryId];
    [proTypes addObject:[NSString stringWithFormat:@"%@ %@",SQLINTEGER,PrimaryKey]];
    [proNames addObjectsFromArray:[dict objectForKey:@"name"]];
    [proTypes addObjectsFromArray:[dict objectForKey:@"type"]];

    return [NSDictionary dictionaryWithObjectsAndKeys:proNames,@"name",proTypes,@"type",nil];
}

/** 数据库中是否存在表 */
+ (BOOL)isExistInTable
{
    __block BOOL res = NO;
    DBHelper *dbHelper = [DBHelper sharedHelper];
    [dbHelper.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        res = [db tableExists:tableName];
    }];
    return res;
}

+ (NSArray *)getColumns
{
    DBHelper *dbHelper = [DBHelper sharedHelper];
    NSMutableArray *columns = [NSMutableArray array];
    [dbHelper.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        FMResultSet *resultSet = [db getTableSchema:tableName];
        while ([resultSet next]) {
            NSString *column = [resultSet stringForColumn:@"name"];
            [columns addObject:column];
        }
    }];
    return [columns copy];
}

/**
 * 创建表
 * 如果已经创建,返回YES
 */
+ (BOOL)createTable
{
    FMDatabase *db = [FMDatabase databaseWithPath:[DBHelper dbPath]];
    if (![db open]) {
        NSLog(@"数据库打开失败!");
        return NO;
    }

    NSString *tableName = NSStringFromClass(self.class);
    NSString *columeAndType = [self.class getColumeAndTypeString];
    NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(%@);",tableName,columeAndType];
    if (![db executeUpdate:sql]) {
        return NO;
    }

    NSMutableArray *columns = [NSMutableArray array];
    FMResultSet *resultSet = [db getTableSchema:tableName];
    while ([resultSet next]) {
        NSString *column = [resultSet stringForColumn:@"name"];
        [columns addObject:column];
    }
    NSDictionary *dict = [self.class getAllProperties];
    NSArray *properties = [dict objectForKey:@"name"];
    NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)",columns];
    //过滤数组
    NSArray *resultArray = [properties filteredArrayUsingPredicate:filterPredicate];

    for (NSString *column in resultArray) {
        NSUInteger index = [properties indexOfObject:column];
        NSString *proType = [[dict objectForKey:@"type"] objectAtIndex:index];
        NSString *fieldSql = [NSString stringWithFormat:@"%@ %@",column,proType];
        NSString *sql = [NSString stringWithFormat:@"ALTER TABLE %@ ADD COLUMN %@ ",NSStringFromClass(self.class),fieldSql];
        if (![db executeUpdate:sql]) {
            return NO;
        }
    }
    [db close];
    return YES;
}

//数据是否存在
- (BOOL )isExsistObj{

    id otherPaimaryValue = [self valueForKey:_keyWord];

    DBHelper *dbHelper = [DBHelper sharedHelper];

    __block BOOL isExist = NO;

    __block DBBaseModel *WeakSelf = self;

    [dbHelper.dbQueue inDatabase:^(FMDatabase *db) {

        NSString *tableName = NSStringFromClass(self.class);
        NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@ WHERE %@ = '%@'",tableName,WeakSelf.keyWord,otherPaimaryValue];

        FMResultSet *aResult = [db executeQuery:sql];

        if([aResult next]){

            isExist = YES;

        }else{

            isExist = NO;
        }
        [aResult close];
    }];

    return isExist;
}

- (BOOL)saveOrUpdate
{

    BOOL isExsist = [self isExsistObj];

    if (isExsist ) {

        return  [self update];

    }else{

        return [self save];

    }
}

- (BOOL)save
{
    //保存修改时间
    NSTimeInterval time = [[NSDate date]timeIntervalSince1970];
    NSString *str = [NSString stringWithFormat:@"%.0f",time];

    NSString *tableName = NSStringFromClass(self.class);
    NSMutableString *keyString = [NSMutableString string];
    NSMutableString *valueString = [NSMutableString string];
    NSMutableArray *insertValues = [NSMutableArray  array];
    for (int i = 0; i < self.columeNames.count; i++) {
        NSString *proname = [self.columeNames objectAtIndex:i];
        if ([proname isEqualToString:primaryId]) {
            continue;
        }

        [keyString appendFormat:@"%@,", proname];
        [valueString appendString:@"?,"];
        id value;
        if ([proname isEqualToString:dbTimeCount]) {
            value = str;
        }else{
            value = [self valueForKey:proname];
        }
        if (!value) {
            value = @"";
        }
        [insertValues addObject:value];
    }

    [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)];
    [valueString deleteCharactersInRange:NSMakeRange(valueString.length - 1, 1)];

    DBHelper *dbHelper = [DBHelper sharedHelper];
    __block BOOL res = NO;
    [dbHelper.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *sql = [NSString stringWithFormat:@"INSERT INTO %@(%@) VALUES (%@);", tableName, keyString, valueString];
        res = [db executeUpdate:sql withArgumentsInArray:insertValues];
        self.pk = res?[NSNumber numberWithLongLong:db.lastInsertRowId].intValue:0;
        NSLog([email protected]"插入成功":@"插入失败");
    }];
    return res;
}

/** 批量保存用户对象 */
+ (BOOL)saveObjects:(NSArray *)array
{
    //判断是否是JKBaseModel的子类
    for (DBBaseModel *model in array) {
        if (![model isKindOfClass:[DBBaseModel class]]) {
            return NO;
        }
    }

    __block BOOL res = YES;
    DBHelper *dbHelper = [DBHelper sharedHelper];
    // 如果要支持事务
    [dbHelper.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        for (DBBaseModel *model in array) {
            //保存修改时间
            NSTimeInterval time = [[NSDate date]timeIntervalSince1970];
            NSString *str = [NSString stringWithFormat:@"%.0f",time];

            NSString *tableName = NSStringFromClass(model.class);
            NSMutableString *keyString = [NSMutableString string];
            NSMutableString *valueString = [NSMutableString string];
            NSMutableArray *insertValues = [NSMutableArray  array];
            for (int i = 0; i < model.columeNames.count; i++) {
                NSString *proname = [model.columeNames objectAtIndex:i];
                if ([proname isEqualToString:primaryId]) {
                    continue;
                }
                [keyString appendFormat:@"%@,", proname];
                [valueString appendString:@"?,"];
                id value;
                if ([proname isEqualToString:dbTimeCount]) {
                    value = str;
                }else{
                    value = [model valueForKey:proname];
                }
                if (!value) {
                    value = @"";
                }
                [insertValues addObject:value];
            }
            [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)];
            [valueString deleteCharactersInRange:NSMakeRange(valueString.length - 1, 1)];

            NSString *sql = [NSString stringWithFormat:@"INSERT INTO %@(%@) VALUES (%@);", tableName, keyString, valueString];
            BOOL flag = [db executeUpdate:sql withArgumentsInArray:insertValues];
            model.pk = flag?[NSNumber numberWithLongLong:db.lastInsertRowId].intValue:0;
            NSLog([email protected]"插入成功":@"插入失败");
            if (!flag) {
                res = NO;
                *rollback = YES;
                return;
            }
        }
    }];
    return res;
}

/** 更新单个对象 */
- (BOOL)update
{
    //设置更新时间
    NSTimeInterval time = [[NSDate date]timeIntervalSince1970];
    NSString *str = [NSString stringWithFormat:@"%.0f",time];

    DBHelper *dbHelper = [DBHelper sharedHelper];
    __block BOOL res = NO;

    [dbHelper.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        id primaryValue = [self valueForKey:self.keyWord];

        NSMutableString *keyString = [NSMutableString string];
        NSMutableArray *updateValues = [NSMutableArray  array];
        for (int i = 0; i < self.columeNames.count; i++) {
            NSString *proname = [self.columeNames objectAtIndex:i];
            if ([proname isEqualToString:self.keyWord]) {
                continue;
            }
            if([proname isEqualToString:primaryId]){

                continue;
            }
            [keyString appendFormat:@" %@=?,", proname];
            id value;
            if ([proname isEqualToString:dbTimeCount]) {
                value = str;
            }else{
                value = [self valueForKey:proname];
            }
            if (!value) {
                value = @"";
            }
            [updateValues addObject:value];
        }

        //删除最后那个逗号
        [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)];
        NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@ = ?;", tableName, keyString, self.keyWord];
        [updateValues addObject:primaryValue];
        res = [db executeUpdate:sql withArgumentsInArray:updateValues];
        NSLog([email protected]"更新成功":@"更新失败");
    }];
    return res;
}

/** 批量更新用户对象*/
+ (BOOL)updateObjects:(NSArray *)array
{
    for (DBBaseModel *model in array) {
        if (![model isKindOfClass:[DBBaseModel class]]) {
            return NO;
        }
    }
    __block BOOL res = YES;
    DBHelper *dbHelper = [DBHelper sharedHelper];
    // 如果要支持事务
    [dbHelper.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        for (DBBaseModel *model in array) {
            NSTimeInterval time = [[NSDate date]timeIntervalSince1970];
            NSString *str = [NSString stringWithFormat:@"%.0f",time];

            NSString *tableName = NSStringFromClass(model.class);
            id primaryValue = [model valueForKey:primaryId];
            if (!primaryValue || primaryValue <= 0) {
                res = NO;
                *rollback = YES;
                return;
            }

            NSMutableString *keyString = [NSMutableString string];
            NSMutableArray *updateValues = [NSMutableArray  array];
            for (int i = 0; i < model.columeNames.count; i++) {
                NSString *proname = [model.columeNames objectAtIndex:i];
                if ([proname isEqualToString:primaryId]) {
                    continue;
                }
                [keyString appendFormat:@" %@=?,", proname];
                id value;
                if ([proname isEqualToString:dbTimeCount]) {
                    value = str;
                }else{
                    value = [model valueForKey:proname];
                }
                if (!value) {
                    value = @"";
                }
                [updateValues addObject:value];
            }

            //删除最后那个逗号
            [keyString deleteCharactersInRange:NSMakeRange(keyString.length - 1, 1)];
            NSString *sql = [NSString stringWithFormat:@"UPDATE %@ SET %@ WHERE %@=?;", tableName, keyString, primaryId];
            [updateValues addObject:primaryValue];
            BOOL flag = [db executeUpdate:sql withArgumentsInArray:updateValues];
            NSLog([email protected]"更新成功":@"更新失败");
            if (!flag) {
                res = NO;
                *rollback = YES;
                return;
            }
        }
    }];

    return res;
}

/** 删除单个对象 */
- (BOOL)deleteObject
{
    DBHelper *dbHelper = [DBHelper sharedHelper];
    __block BOOL res = NO;
    [dbHelper.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        id primaryValue = [self valueForKey:primaryId];
        if (!primaryValue || primaryValue <= 0) {
            return ;
        }
        NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = ?",tableName,primaryId];
        res = [db executeUpdate:sql withArgumentsInArray:@[primaryValue]];
        NSLog([email protected]"删除成功":@"删除失败");
    }];
    return res;
}

/** 批量删除用户对象 */
+ (BOOL)deleteObjects:(NSArray *)array
{
    for (DBBaseModel *model in array) {
        if (![model isKindOfClass:[DBBaseModel class]]) {
            return NO;
        }
    }

    __block BOOL res = YES;
    DBHelper *dbHelper = [DBHelper sharedHelper];
    // 如果要支持事务
    [dbHelper.dbQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        for (DBBaseModel *model in array) {
            NSString *tableName = NSStringFromClass(model.class);
            id primaryValue = [model valueForKey:primaryId];
            if (!primaryValue || primaryValue <= 0) {
                return ;
            }

            NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = ?",tableName,primaryId];
            BOOL flag = [db executeUpdate:sql withArgumentsInArray:@[primaryValue]];
            NSLog([email protected]"删除成功":@"删除失败");
            if (!flag) {
                res = NO;
                *rollback = YES;
                return;
            }
        }
    }];
    return res;
}

/** 通过条件删除数据 */
+ (BOOL)deleteObjectsByCriteria:(NSString *)criteria
{
    DBHelper *dbHelper = [DBHelper sharedHelper];
    __block BOOL res = NO;
    [dbHelper.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@ %@ ",tableName,criteria];
        res = [db executeUpdate:sql];
        NSLog([email protected]"删除成功":@"删除失败");
    }];
    return res;
}

/** 清空表 */
+ (BOOL)clearTable
{
    DBHelper *dbHelper = [DBHelper sharedHelper];
    __block BOOL res = NO;
    [dbHelper.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@",tableName];
        res = [db executeUpdate:sql];
        NSLog([email protected]"清空成功":@"清空失败");
    }];
    return res;
}

/** 查询全部数据 */
+ (NSArray *)findAll
{
    NSLog(@"db---%s",__func__);
    DBHelper *dbHelper = [DBHelper sharedHelper];
    NSMutableArray *users = [NSMutableArray array];
    [dbHelper.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@",tableName];
        FMResultSet *resultSet = [db executeQuery:sql];
        while ([resultSet next]) {
            DBBaseModel *model = [[self.class alloc] init];
            for (int i=0; i< model.columeNames.count; i++) {
                NSString *columeName = [model.columeNames objectAtIndex:i];
                NSString *columeType = [model.columeTypes objectAtIndex:i];
                if ([columeType isEqualToString:SQLTEXT]) {
                    [model setValue:[resultSet stringForColumn:columeName] forKey:columeName];
                } else {
                    [model setValue:[NSNumber numberWithLongLong:[resultSet longLongIntForColumn:columeName]] forKey:columeName];
                }
            }
            [users addObject:model];
            FMDBRelease(model);
        }
    }];

    return users;
}

/** 查找某条数据 */
+ (instancetype)findFirstByCriteria:(NSString *)criteria
{
    NSArray *results = [self.class findByCriteria:criteria];
    if (results.count < 1) {
        return nil;
    }

    return [results firstObject];
}

+ (instancetype)findByPK:(int)inPk
{
    NSString *condition = [NSString stringWithFormat:@"WHERE %@=%d",primaryId,inPk];
    return [self findFirstByCriteria:condition];
}

/** 通过条件查找数据 */
+ (NSArray *)findByCriteria:(NSString *)criteria
{
    DBHelper *dbHelper = [DBHelper sharedHelper];
    NSMutableArray *users = [NSMutableArray array];
    [dbHelper.dbQueue inDatabase:^(FMDatabase *db) {
        NSString *tableName = NSStringFromClass(self.class);
        NSString *sql = [NSString stringWithFormat:@"SELECT * FROM %@  %@",tableName,criteria];
        FMResultSet *resultSet = [db executeQuery:sql];
        while ([resultSet next]) {
            DBBaseModel *model = [[self.class alloc] init];
            for (int i=0; i< model.columeNames.count; i++) {
                NSString *columeName = [model.columeNames objectAtIndex:i];
                NSString *columeType = [model.columeTypes objectAtIndex:i];
                if ([columeType isEqualToString:SQLTEXT]) {
                    [model setValue:[resultSet stringForColumn:columeName] forKey:columeName];
                } else {
                    [model setValue:[NSNumber numberWithLongLong:[resultSet longLongIntForColumn:columeName]] forKey:columeName];
                }
            }
            [users addObject:model];
            FMDBRelease(model);
        }
    }];

    return users;
}

// 值 为 通过 条件查找  - 返回数组中的第一个
+ (instancetype)findWhereColoum:(NSString *)coloum equleToValue:(NSString *)value{

    return [[self class] findFirstByCriteria:[NSString stringWithFormat:@"WHERE %@='%@'",coloum,value]];
}

#pragma mark - util method
+ (NSString *)getColumeAndTypeString
{
    NSMutableString* pars = [NSMutableString string];
    NSDictionary *dict = [self.class getAllProperties];

    NSMutableArray *proNames = [dict objectForKey:@"name"];
    NSMutableArray *proTypes = [dict objectForKey:@"type"];

    for (int i=0; i< proNames.count; i++) {
        [pars appendFormat:@"%@ %@",[proNames objectAtIndex:i],[proTypes objectAtIndex:i]];
        if(i+1 != proNames.count)
        {
            [pars appendString:@","];
        }
    }
    return pars;
}

- (NSString *)description
{
    NSString *result = @"";
    NSDictionary *dict = [self.class getAllProperties];
    NSMutableArray *proNames = [dict objectForKey:@"name"];
    for (int i = 0; i < proNames.count; i++) {
        NSString *proName = [proNames objectAtIndex:i];
        id  proValue = [self valueForKey:proName];
        result = [result stringByAppendingFormat:@"%@:%@\n",proName,proValue];
    }
    return result;
}

#pragma mark - must be override method
/** 如果子类中有一些property不需要创建数据库字段,那么这个方法必须在子类中重写
 */
+ (NSArray *)transients
{
    return @[];
}

@end

DBHelper类

//
//  DBHelper.h
//  FMDB_TEST
//
//  Created by qihb on 16/4/8.
//  Copyright © 2016年 Qihb. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "FMDB.h"

/**
 *  数据库管理工具
 */
@interface DBHelper : NSObject

@property(nonatomic,retain,readonly)FMDatabaseQueue *dbQueue;

/**
 *  获取数据库管理类单例
 */
+(DBHelper *)sharedHelper;

/**
 *  数据库文件沙盒地址
 */
+ (NSString *)dbPath;

@end

.m

//
//  DBHelper.m
//  FMDB_TEST
//
//  Created by qihb on 16/4/8.
//  Copyright © 2016年 Qihb. All rights reserved.
//

#import "DBHelper.h"

@interface DBHelper ()

@property (nonatomic, retain) FMDatabaseQueue *dbQueue;

@end

@implementation DBHelper

+(DBHelper *)sharedHelper{
    static DBHelper *instance = nil;
    static dispatch_once_t onceToken;
    if (!instance) {
        dispatch_once(&onceToken, ^{
            instance = [[super allocWithZone:nil]init];
        });
    }
    return instance;
}
//lazy load
-(FMDatabaseQueue *)dbQueue{
    if (!_dbQueue) {
        _dbQueue = [FMDatabaseQueue databaseQueueWithPath:[[self class] dbPath]];
    }
    return _dbQueue;
}
//数据库地址
+ (NSString *)dbPath
{
    NSString *docsdir = [NSSearchPathForDirectoriesInDomains( NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSFileManager *filemanage = [NSFileManager defaultManager];
    docsdir = [docsdir stringByAppendingPathComponent:@"AppDataBase"];
    BOOL isDir;
    BOOL exit =[filemanage fileExistsAtPath:docsdir isDirectory:&isDir];
    if (!exit || !isDir) {
        [filemanage createDirectoryAtPath:docsdir withIntermediateDirectories:YES attributes:nil error:nil];
    }
    NSString *dbpath = [docsdir stringByAppendingPathComponent:@"TierTime.sqlite"];
    return dbpath;
}

#pragma --mark 保证单例不会被创建成新对象
+(instancetype)alloc{
    NSAssert(0, @"这是一个单例对象,请使用+(DBHelper *)sharedHelper方法");
    return nil;
}
+ (id)allocWithZone:(struct _NSZone *)zone
{
    return [DBHelper sharedHelper];
}

- (id)copyWithZone:(struct _NSZone *)zone
{
    return [DBHelper sharedHelper];
}

@end

使用示例:

PicCacheModel *pic1 = [[PicCacheModel alloc]init];
    pic1.pic_name       = @"DefaultImage_0";
    pic1.pic_from_source = @"0";
    pic1.pic_path       = [NSString stringWithFormat:@"%@.png",pic1.pic_name];
    pic1.pic_md5_str    = [TRMD5 file_md5:MODEL_DEFAULT_SAVE_PATH(pic1.pic_path)];

    PicCacheModel *pic2 = [[PicCacheModel alloc]init];
    pic2.pic_name       = @"DefaultImage_1";
    pic2.pic_from_source = @"0";
    pic2.pic_path       = [NSString stringWithFormat:@"%@.png",pic2.pic_name];
    pic2.pic_md5_str    = [TRMD5 file_md5:MODEL_DEFAULT_SAVE_PATH(pic2.pic_path)];

    PicCacheModel *pic3 = [[PicCacheModel alloc]init];
    pic3.pic_name       = @"DefaultImage_2";
    pic3.pic_from_source = @"0";
    pic3.pic_path       = [NSString stringWithFormat:@"%@.png",pic3.pic_name];
    pic3.pic_md5_str    = [TRMD5 file_md5:MODEL_DEFAULT_SAVE_PATH(pic3.pic_path)];

    [pic1 saveOrUpdate];
    [pic2 saveOrUpdate];
    [pic3 saveOrUpdate];

贴到这里,更多用法大家可以尝试一下

时间: 2024-12-25 00:35:45

IOS:FMDB使用databaseQueue实现数据库操作线程安全的相关文章

【ios开发】使用FMDB封装类的sqlite数据库操作

创建,插入,更新和删除:使用executeUpdate方法,而查询则用executeQuery 1.实例化FMDatabase//paths: ios下Document路径,Document为ios中可读写的文件夹NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);NSString *documentDirectory = [paths objectAtI

Java8 Lambda表达应用 -- 单线程游戏server+异步数据库操作

前段时间我们游戏server升级到开发环境Java8,这些天,我再次server的线程模型再次设计了一下,耗费Lambda表情. LambdaJava代码.特别是丑陋不堪的匿名内部类,这篇文章主要就是想和大家分享这一点. 线程模型 首先简介一下我们游戏server的线程模型.大致例如以下图所看到的: Netty线程池仅仅处理消息的收发,当Netty收到消息之后.会交给游戏逻辑线程处理.因为是单线程在处理游戏逻辑,所以每个消息必须非常快处理完.也就是说,不能有数据库等耗时操作.不然逻辑线程非常可能

Java8 Lambda表达式应用案例 -- 单线程游戏服务器+异步数据库操作

前段时间我们游戏服务器的开发环境升级到了Java8,这两天我又把服务器的线程模型重新设计了一下,用上了Lambda表达式.Lambda表达式确实能够大幅简化Java代码,特别是丑陋不堪的匿名内部类,这篇文章主要就是想和大家分享这一点. 线程模型 首先简单介绍一下我们游戏服务器的线程模型,大致如下图所示: Netty线程池只处理消息的收发,当Netty收到消息之后,会交给游戏逻辑线程处理.由于是单线程在处理游戏逻辑,所以每一个消息必须很快处理完,也就是说,不能有数据库等耗时操作,不然逻辑线程很可能

iOS数据库操作(使用FMDB)

iOS学习笔记(十六)——数据库操作(使用FMDB) 分类: iOS开发 2013-07-15 23:19 8655人阅读 评论(5) 收藏 举报 目录(?)[+] iOS中原生的SQLite API在使用上相当不友好,在使用时,非常不便.于是,就出现了一系列将SQLite API进行封装的库,例如FMDB.PlausibleDatabase.sqlitepersistentobjects等,FMDB (https://github.com/ccgus/fmdb) 是一款简洁.易用的封装库,这一

iOS 数据库操作(使用FMDB)

iOS 数据库操作(使用FMDB) iOS中原生的SQLite API在使用上相当不友好,在使用时,非常不便.于是,就出现了一系列将SQLite API进行封装的库,例如FMDB.PlausibleDatabase.sqlitepersistentobjects等,FMDB (https://github.com/ccgus/fmdb) 是一款简洁.易用的封装库,这一篇文章简单介绍下FMDB的使用. 在FMDB下载文件后,工程中必须导入如下文件,并使用 libsqlite3.dylib 依赖包.

iOS数据库操作之FMDB框架的使用

原文链接:http://blog.csdn.net/xyz_lmn/article/details/9312837 iOS中原生的SQLite API在使用上相当不友好,在使用时,非常不便.于是,就出现了一系列将SQLite API进行封装的库,例如FMDB.PlausibleDatabase.sqlitepersistentobjects等,FMDB (https://github.com/ccgus/fmdb) 是一款简洁.易用的封装库,这一篇文章简单介绍下FMDB的使用. 在FMDB下载文

iOS学习笔记(十六)——数据库操作(使用FMDB)

iOS中原生的SQLite API在使用上相当不友好,在使用时,非常不便.于是,就出现了一系列将SQLite API进行封装的库,例如FMDB.PlausibleDatabase.sqlitepersistentobjects等,FMDB (https://github.com/ccgus/fmdb) 是一款简洁.易用的封装库,这一篇文章简单介绍下FMDB的使用. 在FMDB下载文件后,工程中必须导入如下文件,并使用 libsqlite3.dylib 依赖包. FMDB同时兼容ARC和非ARC工

iOS 使用FMDB进行数据库操作

[摘要]本文介绍iOS 使用FMDB进行数据库操作,并提供详细的示例代码供参考. 1.首先要先导入第三方类库FMdatabase. 2.获得存放数据库文件的沙盒地址. View Row Code 1 +(NSString *)databaseFilePath 2 { 3   4 NSArray *filePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 5 NSStrin

iOS数据库操作fmdb1: FMDB数据库使用

FMDatabase 数据库 1.下载FMDatabase 导入下载里面的src文件,删了fmdb.m文件 2.引入framework:libsqlite3.dylib 3.引入头文件 #import "FMDatabase.h" 数据库文件要兼容iPhone4和iPhone5需要放在Document或Library目录中,先判断Document/Library中数据库文件(一般是.sqlite后缀)是否存在,如果不存在则从工程(bundle)中把数据库文件拷贝到Document中或L