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

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

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

在IOS里面开发,提起和数据库打交道,可能很多人都熟悉FMDB这个数据库的组件,它对IOS里面操作Sqlite数据库进行了很大程度的简化,简化后,我们大多数情况下,只需要和FMDatabase和FMResultSet打交道即可,使用起来非常方便。

1、FMDB的操作

为了较好介绍整体性的内容,我们先从FMDB的各种操作进行介绍。

1)数据库打开操作

  1. FMDatabase *db= [FMDatabase databaseWithPath:dbPath] ;
  2. if (![db open]) {
  3. NSLog(@"无法打开数据库");
  4. return ;
  5. }

2)数据库操作executeUpdate

使用FMDB,对于没有返回记录的操作,都可以用executeUpdate进行操作,如下是删除记录的函数

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

当然,对于SQLite很多数据库操作,我们可以使用参数化语句进行操作,如下例子所示

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

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

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

3)返回集合的操作

操作有返回集合的语句,我们就需要用到FMResultSet对象了,这个对象类似于以前见过的游标,不过不一样的东西而已。

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

上面的代码操作,返回一个Resultset集合进行遍历使用,我们可以根据Resultset的一些方法获取到不同的数据内容。

相对于FMResult的操作方式,原生态的Sqlite数据库操作代码如下所示。看完是不是感觉使用FMDB类库操作数据库方便很多呢。

  1. NSString *sqlQuery = @"SELECT * FROM User";
  2. sqlite3_stmt * statement;
  3.  
  4. if (sqlite3_prepare_v2(db, [sqlQuery UTF8String], -, &statement, nil) == SQLITE_OK) {
  5. while (sqlite3_step(statement) == SQLITE_ROW) {
  6. char *name = (char*)sqlite3_column_text(statement, );
  7. NSString *nsNameStr = [[NSString alloc]initWithUTF8String:name];
  8. int age = sqlite3_column_int(statement, );
  9. char *address = (char*)sqlite3_column_text(statement, );
  10. NSString *nsAddressStr = [[NSString alloc]initWithUTF8String:address];
  11.  
  12. NSLog(@"name:%@ age:%d address:%@",nsNameStr,age, nsAddressStr);
  13. }
  14. }
  15. sqlite3_close(db);

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

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

2、数据库操作层的设计

上面小节介绍了使用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类型作为实体类类型,一种是使用一种半类型化的类型(实体类的基类)作为对象,我倾向于使用后者,因为毕竟比较接近真实的类型了。

  1. //
  2. // BaseEntity.h
  3. // MyDatabaseProject
  4. //
  5. // Created by 伍华聪 on 14-4-2.
  6. // Copyright (c) 2014年 伍华聪. All rights reserved.
  7. //
  8.  
  9. #import <Foundation/Foundation.h>
  10.  
  11. @interface BaseEntity : NSObject
  12.  
  13. @end
  1. //
  2. // UserInfo.h
  3. // MyDatabaseProject
  4. //
  5. // Created by 伍华聪 on 14-3-30.
  6. // Copyright (c) 2014年 伍华聪. All rights reserved.
  7. //
  8.  
  9. #import <Foundation/Foundation.h>
  10. #import "BaseEntity.h"
  11.  
  12. @interface UserInfo : BaseEntity
  13.  
  14. @property(nonatomic, copy) NSString *ID;
  15. @property(nonatomic, copy) NSString *name;
  16. @property(nonatomic, copy) NSString *fullName;
  17.  
  18. @end

这种继承模式和.NET的继承关系差不多了。

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

  1. //
  2. // BaseDAL.h
  3. // MyDatabaseProject
  4. //
  5. // Created by 伍华聪 on 14-3-30.
  6. // Copyright (c) 2014年 伍华聪. All rights reserved.
  7. //
  8.  
  9. #import <Foundation/Foundation.h>
  10. #import "FMDatabase.h"
  11. #import "BaseEntity.h"
  12.  
  13. @interface BaseDAL : NSObject
  14. {
  15. NSString *pathToDatabase;
  16. }
  17.  
  18. #pragma 数据库相关属性
  19.  
  20. @property (nonatomic, strong) NSString *pathToDatabase;
  21. @property (nonatomic, readonly) FMDatabase *database; // 数据库操作对象
  22. @property (nonatomic, strong) NSString *tableName;//表名称
  23. @property (nonatomic, strong) NSString *primaryKey;//主键
  24. @property (nonatomic, strong) NSString *sortField;//排序,默认为主键
  25. @property (nonatomic, assign, getter=isDescending) BOOL descending;//是否降序查询
  26.  
  27. //将DataReader的属性值转化为实体类的属性值,返回实体类(子类必须重写)
  28. -(id) rsToEntity:(FMResultSet *)rs;
  29.  
  30. //将实体对象的属性值转化为字典列表对应的键值(子类必须重写)
  31. -(NSDictionary *) dictByEntity:(BaseEntity *) info;
  32.  
  33. #pragma 基础操作接口
  34.  
  35. //根据指定对象的ID,从数据库中删除指定对象
  36. - (BOOL) deleteById:(id) key;
  37.  
  38. //根据指定条件,从数据库中删除指定对象
  39. - (BOOL) deleteByCondition:(NSString *) condition;
  40.  
  41. //更新对象属性到数据库中
  42. -(BOOL) update:(BaseEntity *) info byKey:(id) key;
  43.  
  44. //插入指定对象到数据库中
  45. -(BOOL) insert:(BaseEntity *) info;
  46.  
  47. //插入指定对象到数据库中,并返回最后插入的ID
  48. -(NSInteger) insert2:(BaseEntity *) info;
  49.  
  50. //查询数据库,检查是否存在指定ID的对象
  51. - (BaseEntity *) findById:(id) key;
  52.  
  53. //根据条件查询数据库,如果存在返回第一个对象
  54. -(BaseEntity *) findSingle:(NSString *) condition;
  55.  
  56. //根据条件查询数据库,如果存在返回第一个对象
  57. -(BaseEntity *) findSingle:(NSString *) condition orderBy:(NSString *) orderBy;
  58.  
  59. //根据条件查询数据库,并返回对象集合
  60. - (NSArray *) find:(NSString *) condition;
  61.  
  62. //根据条件查询数据库,并返回对象集合
  63. - (NSArray *) find:(NSString *) condition orderBy:(NSString *) orderBy;
  64.  
  65. //获取表的全部数据
  66. - (NSArray *) getAll;
  67.  
  68. //获取表的全部数据
  69. - (NSArray *) getAll:(NSString *) orderBy;
  70.  
  71. //获取某字段数据字典列表
  72. -(NSArray *) getFieldList:(NSString *) fieldName;
  73.  
  74. //获取表的所有记录数量
  75. -(int) getRecordCount;
  76.  
  77. //根据条件,获取表查询的记录数量
  78. -(int) getRecordCount:(NSString *) condition;
  79.  
  80. //根据条件,判断是否存在记录
  81. -(BOOL) isExistRecord:(NSString *)condition;
  82.  
  83. //查询数据库,检查是否存在指定键值的对象
  84. -(BOOL) isExist:(NSString *)fieldName value:(id) value;
  85.  
  86. //根据主键和字段名称,获取对应字段的内容
  87. -(NSString *) getFieldValue:(NSString *)key fieldName:(NSString *)fieldName;
  88.  
  89. //执行SQL查询语句,返回查询结果的所有记录的第一个字段,用逗号分隔。
  90. -(NSString *) sqlValueList:(NSString *)query;
  91.  
  92. #pragma 数据库初始化函数及关闭操作
  93.  
  94. //根据SQLite数据库地址初始化数据库
  95. -(id) initWithPath:(NSString *)filePath;
  96.  
  97. //根据SQLite数据库名称初始化数据库
  98. -(id) initWithFileName:(NSString *)fileName;
  99.  
  100. // 关闭连接
  101. -(void) close;
  102.  
  103. @end

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

  1. - (NSArray *) find:(NSString *) condition orderBy:(NSString *) orderBy {
  2. NSString *query = [NSString stringWithFormat:@"SELECT * FROM %@ ", self.tableName];
  3. if (condition != nil) {
  4. query = [query stringByAppendingFormat:@" where %@ ", condition];
  5. }
  6.  
  7. if (orderBy != nil) {
  8. query = [query stringByAppendingString:orderBy];
  9. }
  10. else {
  11. query = [query stringByAppendingFormat:@" ORDER BY %@ %@ ", self.sortField, self.isDescending ? @"DESC" : @"ASC"];
  12. }
  13.  
  14. FMResultSet *rs = [self.database executeQuery:query];
  15. NSMutableArray *array = [NSMutableArray arrayWithCapacity:[rs columnCount]];
  16.  
  17. BaseEntity *info = nil; //默认初始化为空
  18. while ([rs next]) {
  19. info = [self rsToEntity:rs];
  20. [array addObject:info];
  21. }
  22. [rs close];
  23.  
  24. return array;
  25. }

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

  1. info = [self rsToEntity:rs];

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

  1. //
  2. // UserDAL.h
  3. // MyDatabaseProject
  4. //
  5. // Created by 伍华聪 on 14-3-30.
  6. // Copyright (c) 2014年 伍华聪. All rights reserved.
  7. //
  8.  
  9. #import <Foundation/Foundation.h>
  10. #import "FMDatabase.h"
  11. #import "BaseDAL.h"
  12. #import "UserInfo.h"
  13.  
  14. @interface UserDAL : BaseDAL
  15. {
  16. }
  17.  
  18. //单例模式
  19. +(UserDAL *) defaultDAL;
  20.  
  21. @end

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

 

从C#到Objective-C,循序渐进学习苹果开发(7)--使用FMDB对Sqlite数据库进行操作的更多相关文章

  1. 从C#到Objective-C,循序渐进学习苹果开发(4)--代码块(block)和错误异常处理的理解

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本文继续上一篇随笔<从 ...

  2. 从C#到Objective-C,循序渐进学习苹果开发(3)--分类(category)和协议Protocal的理解

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本文继续上一篇随笔<从 ...

  3. 从C#到Objective-C,循序渐进学习苹果开发(2)--Objective-C和C#的差异

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台开发苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验. 在上篇<从C#到 ...

  4. 从C#到Objective-C,循序渐进学习苹果开发(5)--利用XCode来进行IOS的程序开发

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.前面几篇随笔主要介绍C#和O ...

  5. 从C#到Objective-C,循序渐进学习苹果开发(1)--准备开发账号和开发环境

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验,因为一旦方方面面都精通了,也 ...

  6. 从C#到Objective-C,循序渐进学习苹果开发(6)--视图控制器的使用

    本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换历程体验.本篇主要开始介绍基于XCod ...

  7. IOS开发-UI学习-sqlite数据库的操作

    IOS开发-UI学习-sqlite数据库的操作 sqlite是一个轻量级的数据库,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了,而且它的处理速度比Mysql.PostgreSQL这 ...

  8. Jmeter学习之— 参数化、关联、断言、数据库的操作

    一. Jmeter参数化1. 文件参数化解释:创建测试数据,将数据写入TXT文件文件中,然后Jmeter从文件中读取数据.例如用户注册操作:1. 首先在Jmeter下创建一个线程组,如下图: 2. 然 ...

  9. 【VS开发】循序渐进学习使用WINPCAP(一)

    winpcap教程 中文教程 http://www.ferrisxu.com/WinPcap/html/index.html 除此之外, WinPcap · Developer Resources下载 ...

随机推荐

  1. 记一次Url重写_发布之后iis 404

    把api封装完,客户要求app的url能不能不变(客户之前用的php的api开发app,已经开发了很多了,所以希望不改动url).但是这个规则要求是:xx/api.php?s=/{controller ...

  2. 一个简单的通用Makefile实现

    一个简单的通用Makefile实现   Makefile是Linux下程序开发的自动化编译工具,一个好的Makefile应该准确的识别编译目标与源文件的依赖关系,并且有着高效的编译效率,即每次重新ma ...

  3. Azure China (8) 使用Azure PowerShell创建虚拟机,并设置固定Virtual IP Address和Private IP

    <Windows Azure Platform 系列文章目录> 本文介绍的是由世纪互联运维的Windows Azure China. 相比于Global Azure (http://www ...

  4. AngularJS快速入门指南17:Includes

    使用AngularJS,你可以在HTML中包含其它的HTML文件. 在HTML中包含其它HTML文件? 当前的HTML文档还不支持该功能.不过W3C建议在后续的HTML版本中增加HTML import ...

  5. UIButton添加倒计时

    最近一个项目有获取手机短信跟邮箱验证码功能, 所以要加一个UIButton倒计时功能 例子代码如下: //获取验证码按钮 - (IBAction)getButtonClick:(UIButton *) ...

  6. [Spring框架]Spring JDBCTmplate基础入门总结.

    前言:前面有讲过 Spring IOC以及AOP的基本使用方法, 这里就再来讲下Spring JDBCTemplate的使用方法. 一, 概述这里先说一下Spring 整合的一些模板: 从上图中可以看 ...

  7. fir.im Weekly - iOS开发中的Git流程

    本期 fir.im Weekly 收集了微博上的热转资源,包含 Android.iOS 开发工具.源码等好用的轮子,还有一些 APP 设计的 Tips,希望对你有用. 精仿知乎日报 iOS 端 @我偏 ...

  8. salesforce 零基础学习(三十七) DML及Database方法简单描述

    在apex中通过soql查询可以使用两种方式,使用DML语句或者使用Database的方法. 使用DML语句和使用Database类的方法对于我们来说用的都很多,并且都很常见.对于数据库常见的操作:增 ...

  9. java连接数据库的模糊查询

    1:模糊查询是比较常见的一种查询方式,例如在订单表中,包含有订单的具体日期.如果要查询某年某月的订单信息,最好的方式就是使用模糊查询.进行模糊查询需要使用关键字LIKE.在使用LIKE关键字进行模糊查 ...

  10. python中paramiko模块的使用

    paramiko是python一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接1.可以远程操作服务器文件 例如: df:查看磁盘使用情况 mkdir:创建目录 mv/cp/mk ...