从C#到Objective-C,按部就班学习苹果开发(7)--使用FMDB对Sqlite数据库进行操做

本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,但愿带给你们更好,更真实的转换历程体验。本篇主要开始介绍基于XCode进行IOS程序的开发,介绍使用FMDB对Sqlite数据库进行操做,以及对数据库操做类进行抽象设计,以期达到重用、简化、高效开发的目的。html

在.NET领域开发了不少年,通常常见的项目都须要操做数据库,包括有Oracle、SqlServer、Mysql、Sqlite、Access等数据库,这些数据库是很常见的,咱们在.NET环境里面开发的各类系统,可能都或多或少须要和其中一种以上的数据库打交道,这也是我致力于提炼个人.NET领域的Winform开发框架、Web开发框架、混合式开发框架的目的,尽量达到简化、重用、高效开发的目的。sql

虽然如今在IOS领域作一些研究开发,即便IOS设备更多强调的是一个多媒体的设备,可是数据库的操做仍是必不可少,所以我先从我熟悉的数据库这块入手,了解其中数据库是如何操做的,有哪些现成的组件进行参考学习等等。数据库

在IOS里面开发,提起和数据库打交道,可能不少人都熟悉FMDB这个数据库的组件,它对IOS里面操做Sqlite数据库进行了很大程度的简化,简化后,咱们大多数状况下,只须要和FMDatabase和FMResultSet打交道便可,使用起来很是方便。设计模式

一、FMDB的操做

为了较好介绍总体性的内容,咱们先从FMDB的各类操做进行介绍。框架

1)数据库打开操做函数

FMDatabase *db= [FMDatabase databaseWithPath:dbPath] ;  
if (![db open]) {  
NSLog(@"没法打开数据库");  
return ;  
}  

2)数据库操做executeUpdatepost

使用FMDB,对于没有返回记录的操做,均可以用executeUpdate进行操做,以下是删除记录的函数学习

- (BOOL) deleteByCondition:(NSString *) condition {
    NSString *query = [NSString stringWithFormat:@"Delete FROM %@ Where %@ ", self.tableName, condition];
    BOOL result = [self.database executeUpdate:query];
    return result;
}

固然,对于SQLite不少数据库操做,咱们可使用参数化语句进行操做,以下例子所示测试

[db executeUpdate:@"INSERT INTO User (Name,Age) VALUES (?,?)",@"张三",[NSNumber numberWithInt:30]]  

参数化也可使用 : 字符做为参数标识,以下所示,是我封装的一个数据库操做函数atom

- (BOOL) deleteById:(id) key
{
    NSDictionary *argsDict = [NSDictionary dictionaryWithObjectsAndKeys:key, @"id", nil];
    NSString *query = [NSString stringWithFormat:@"Delete FROM %@ Where %@ =:id", self.tableName, self.primaryKey];
    BOOL result = [self.database executeUpdate:query withParameterDictionary:argsDict];
    return result;
}

3)返回集合的操做

操做有返回集合的语句,咱们就须要用到FMResultSet对象了,这个对象相似于之前见过的游标,不过不同的东西而已。

FMResultSet *rs=[db executeQuery:@"SELECT * FROM User"];
rs=[db executeQuery:@"SELECT * FROM User WHERE Age = ?",@"20"];
while ([rs next]){
NSLog(@"%@ %@",[rs stringForColumn:@"Name"],[rs stringForColumn:@"Age"]);
}

上面的代码操做,返回一个Resultset集合进行遍历使用,咱们能够根据Resultset的一些方法获取到不一样的数据内容。

相对于FMResult的操做方式,原生态的Sqlite数据库操做代码以下所示。看完是否是感受使用FMDB类库操做数据库方便不少呢。

 NSString *sqlQuery = @"SELECT * FROM User";
    sqlite3_stmt * statement;
    
    if (sqlite3_prepare_v2(db, [sqlQuery UTF8String], -1, &statement, nil) == SQLITE_OK) {
        while (sqlite3_step(statement) == SQLITE_ROW) {
            char *name = (char*)sqlite3_column_text(statement, 1);
            NSString *nsNameStr = [[NSString alloc]initWithUTF8String:name];
            int age = sqlite3_column_int(statement, 2);
            char *address = (char*)sqlite3_column_text(statement, 3);
            NSString *nsAddressStr = [[NSString alloc]initWithUTF8String:address];
            
            NSLog(@"name:%@  age:%d  address:%@",nsNameStr,age, nsAddressStr);
        }
    }
    sqlite3_close(db);

FMResultSet方法有下面几种,分别用于获取不一样的数据:

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

二、数据库操做层的设计

上面小节介绍了使用FMDB类库对Sqlite数据库的操做,使咱们大体了解了在IOS里面对数据库操做的过程。可是单纯若是介绍这些,我以为太泛泛了,并且对咱们使用起来也仍是很不方便,不少时候,我老是想经过设计的方式,来简化个人各类操做处理。如咱们知道,IOS里面的Objective C也提供了不少高级语言都有的属性,如对象继承,接口、多态等等。

我很早以前写过的随笔《Winform开发框架之数据访问层的设计》 ,介绍了我在.NET领域里面的数据库访问层的设计,因为这种设计,能很大程度上减小代码量,并提升开发效率,所以,我也想再IOS里面造成这样的数据访问设计,虽然IOS里面可能主要是使用单机版的数据库,如SQLite数据库,因此我想简化一些.NET里面的设计模型。

在.NET里面,个人框架分层主要以下所示,这种框架的设计模式,已经很好应用在了个人Winform开发框架、WCF及混合式开发框架、Web框架里面。它们常见的分层模式,能够分为UI层、BLL层、DAL层、IDAL层、Entity层、公用类库层等等

 而其中的DAL层的设计,示意图以下所示,DAL层主要是经过继承自BaseDAL基类(如BaseDALSQL)进行, BaseDALSQL进行更高一层的抽象,已达到更好的应用目的。

 

而咱们在IOS里面,则能够主要考虑SQLite数据库便可,所以,我把设计经过简化,构造下面的设计模型

 

在项目里面,数据访问层的文件以下所示(为了演示测试方便,使用User表进行操做)。

为了实现数据的承载,咱们须要把表的数据转换为实体类进行显示和操做,所以实体类的设计也须要考虑好。因为数据访问层的基类,封装了大多数的数据操做,也包括返回的数据对象和集合,所以数据访问层的基类,也涉及到了数据类型返回的问题。

因为IOS里面的Objective C里面没有泛型这样的东西,所以有两种方式能够来实现基类实体类的处理:一是使用动态类型id类型做为实体类类型,一种是使用一种半类型化的类型(实体类的基类)做为对象,我倾向于使用后者,由于毕竟比较接近真实的类型了。

//
//  BaseEntity.h
//  MyDatabaseProject
//
//  Created by 伍华聪 on 14-4-2.
//  Copyright (c) 2014年 伍华聪. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface BaseEntity : NSObject

@end
//
//  UserInfo.h
//  MyDatabaseProject
//
//  Created by 伍华聪 on 14-3-30.
//  Copyright (c) 2014年 伍华聪. All rights reserved.
//

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

@interface UserInfo : BaseEntity

@property(nonatomic, copy) NSString *ID;
@property(nonatomic, copy) NSString *name;
@property(nonatomic, copy) NSString *fullName;

@end

这种继承模式和.NET的继承关系差很少了。

对于数据访问层的设计代码,它就是在数据访问基类的定义以下,基类使用了FMDB的数据访问类进行操做的,里面不少操做的接口就是模仿我在.NET领域里面的数据访问层的设计。

//
//  BaseDAL.h
//  MyDatabaseProject
//
//  Created by 伍华聪 on 14-3-30.
//  Copyright (c) 2014年 伍华聪. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "FMDatabase.h"
#import "BaseEntity.h"

@interface BaseDAL : NSObject
{
    NSString *pathToDatabase;
}

#pragma 数据库相关属性

@property (nonatomic, strong) NSString *pathToDatabase;
@property (nonatomic, readonly) FMDatabase *database;  // 数据库操做对象
@property (nonatomic, strong) NSString *tableName;//表名称
@property (nonatomic, strong) NSString *primaryKey;//主键
@property (nonatomic, strong) NSString *sortField;//排序,默认为主键
@property (nonatomic, assign, getter=isDescending) BOOL descending;//是否降序查询


//将DataReader的属性值转化为实体类的属性值,返回实体类(子类必须重写)
-(id) rsToEntity:(FMResultSet *)rs;

//将实体对象的属性值转化为字典列表对应的键值(子类必须重写)
-(NSDictionary *) dictByEntity:(BaseEntity *) info;


#pragma 基础操做接口

//根据指定对象的ID,从数据库中删除指定对象
- (BOOL) deleteById:(id) key;

//根据指定条件,从数据库中删除指定对象
- (BOOL) deleteByCondition:(NSString *) condition;

//更新对象属性到数据库中
-(BOOL) update:(BaseEntity *) info byKey:(id) key;

//插入指定对象到数据库中
-(BOOL) insert:(BaseEntity *) info;

//插入指定对象到数据库中,并返回最后插入的ID
-(NSInteger) insert2:(BaseEntity *) info;

//查询数据库,检查是否存在指定ID的对象
- (BaseEntity *) findById:(id) key;

//根据条件查询数据库,若是存在返回第一个对象
-(BaseEntity *) findSingle:(NSString *) condition;


//根据条件查询数据库,若是存在返回第一个对象
-(BaseEntity *) findSingle:(NSString *) condition orderBy:(NSString *) orderBy;

//根据条件查询数据库,并返回对象集合
- (NSArray *) find:(NSString *) condition;

//根据条件查询数据库,并返回对象集合
- (NSArray *) find:(NSString *) condition orderBy:(NSString *) orderBy;

//获取表的所有数据
- (NSArray *) getAll;

//获取表的所有数据
- (NSArray *) getAll:(NSString *) orderBy;

//获取某字段数据字典列表
-(NSArray *) getFieldList:(NSString *) fieldName;

//获取表的全部记录数量
-(int) getRecordCount;

//根据条件,获取表查询的记录数量
-(int) getRecordCount:(NSString *) condition;

//根据条件,判断是否存在记录
-(BOOL) isExistRecord:(NSString *)condition;

//查询数据库,检查是否存在指定键值的对象
-(BOOL) isExist:(NSString *)fieldName value:(id) value;

//根据主键和字段名称,获取对应字段的内容
-(NSString *) getFieldValue:(NSString *)key fieldName:(NSString *)fieldName;

//执行SQL查询语句,返回查询结果的全部记录的第一个字段,用逗号分隔。
-(NSString *) sqlValueList:(NSString *)query;

#pragma 数据库初始化函数及关闭操做

//根据SQLite数据库地址初始化数据库
-(id) initWithPath:(NSString *)filePath;

//根据SQLite数据库名称初始化数据库
-(id) initWithFileName:(NSString *)fileName;

// 关闭链接
-(void) close;

@end

和个人.NET框架里面的数据访问层设计同样,数据访问基类已经封装了大多数的数据访问操做,所以各个表的数据访问对象,它的代码就能够很简洁了。从上面的基类接口设计能够看到,里面一些实体类返回函数或者列表返回函数,都使用了BaseEntity做为对象,咱们具体在起子类使用的时候,把它返回的对象再一次转换便可。对于数据库访问基类,咱们以一个返回集合的接口实现来分析。

- (NSArray *) find:(NSString *) condition orderBy:(NSString *) orderBy {
    NSString *query = [NSString stringWithFormat:@"SELECT * FROM %@ ", self.tableName];
    if (condition != nil) {
        query = [query stringByAppendingFormat:@" where %@ ", condition];
    }
    
    if (orderBy != nil) {
        query = [query stringByAppendingString:orderBy];
    }
    else {
        query = [query stringByAppendingFormat:@" ORDER BY %@ %@ ", self.sortField, self.isDescending ? @"DESC" : @"ASC"];
    }
    
    FMResultSet *rs = [self.database executeQuery:query];
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:[rs columnCount]];
    
    BaseEntity *info = nil; //默认初始化为空
    while ([rs next]) {
        info = [self rsToEntity:rs];
        [array addObject:info];
    }
    [rs close];
    
    return array;
}

上面代码使用了参数化的SQL语句进行查询,而且,对返回的数据库的ResultSet进行转换为实体类。

info = [self rsToEntity:rs];

因为基类封装了大多数的数据库操做函数,所以数据访问层的具体表的实现类,能够很简洁,可是已经具有了常见的CRUD操做,以及一些分页查询等复杂的数据操做功能。

//
//  UserDAL.h
//  MyDatabaseProject
//
//  Created by 伍华聪 on 14-3-30.
//  Copyright (c) 2014年 伍华聪. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "FMDatabase.h"
#import "BaseDAL.h"
#import "UserInfo.h"

@interface UserDAL : BaseDAL
{
}

//单例模式
+(UserDAL *) defaultDAL;

@end

基于篇幅的缘由,我将在下一篇介绍如何在界面层中使用这样的数据访问设计类,先放上一些测试程序的界面截图。

 

相关文章
相关标签/搜索