Three20软件引擎之结合第三方FMDB框架操作数据库详解

来源:互联网 发布:mac dock自动隐藏 编辑:程序博客网 时间:2024/05/22 00:44



转载自雨松MOMO的博客原文地址:http://blog.csdn.net/xys289187120/article/details/7385474


开始本章之间,首先了解一下FMDB,它是一个老外写的第三方操作数据库的框架。因为IOS官方提供操作数据库的方法似乎不是那么近人而意,使用起来有点麻烦,所以这个老外哥们就将官方提供的方法又包了一层,它封装的方法非常浅,并没有多莫高深,只是更方便开发使用,我看了一下大概核心的代码500多行吧。下面开始学习它,首先需要将源码下载至本地,这哥们将代码上传至GitHub当中,(我现在越来越喜欢GitHub了,在这里总能找到老外写的好玩的东西)。
下载地址:https://github.com/ccgus/fmdb  

无需注册,在页面左上角标题下方点击“ZIP”按钮,即可将源码下载至本地,接着解压文件,然后将源码中src文件夹中的所有代码拷贝至工程当中。由于Xcode4.3.1有一个BUG比较恶心,不支持文件夹的拖拽添加至工程当中,所以需要手动添加文件夹。如下图所示,在下拉列表中点击“Add Files to Some Project”按钮,然后在我的电脑中 选择刚刚解压后的src文件夹,将FMDB中所有源码添加至当前工程。





FMDB的源码添加完毕后,然后删除fmdb.m 这是作者提供工程的入口类,有兴趣的朋友可以自行研究,我们只学习如何使用这个框架。 目前程序是无法编译通过的,因为缺少类库文件,如下图所示,请将libsqlite3.0dylib添加至工程。







类库文件添加完毕后,到这一步工程就应该可以正常编译了。如果到这一步你的工程还有错误,那就恭喜你自己在好好检查检查工程的结构啦。接下来将步入正题,老规矩还是以一个实例的形式向大家介绍如何使用FMDB,如下图所以,又是我们熟悉的界面。





本示例中,程序中共用到了3个控制类,它们是:
StartViewController:上图中绘制的启动视图。
SQLViewController:增、删、改、查后在屏幕中显示的列表视图。
MessageInfoController:选择数据库中某一项数据后将所有信息展示在这个视图中。

首先是程序的启动类,这里说明一下,有关以前提到的知识本节我就不在赘述,如果不理解的朋友请阅读本系列教程之前的文章。

AppDelegate.m
[cpp] view plaincopy
  1. #import "AppDelegate.h"  
  2. #import "StartViewController.h"  
  3. #import "SQLViewController.h"  
  4. #import "MessageInfoController.h"  
  5. @implementation AppDelegate  
  6.   
  7. @synthesize window = _window;  
  8.   
  9. - (void)dealloc  
  10. {  
  11.     [_window release];  
  12.     [super dealloc];  
  13. }  
  14.   
  15. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  
  16. {  
  17.     TTNavigator* navigator = [TTNavigator navigator];  
  18.     navigator.persistenceMode = TTNavigatorPersistenceModeAll;  
  19.     navigator.window = [[[UIWindow alloc] initWithFrame:TTScreenBounds()] autorelease];  
  20.     TTURLMap* map = navigator.URLMap;  
  21.     [map from:@"*" toViewController:[TTWebController class]];  
  22.     [map from:@"tt://StartView" toSharedViewController:[StartViewController class]];  
  23.     [map from:@"tt://SQLView/(initInfo:)" toViewController:[SQLViewController class]];  
  24.     [map from:@"tt://MessageView/(initInfo:)" toViewController:[MessageInfoController class]];  
  25.       
  26.       
  27.       
  28.     if (![navigator restoreViewControllers])   
  29.     {  
  30.         [navigator openURLAction:[TTURLAction actionWithURLPath:@"tt://StartView"]];  
  31.     }  
  32.     return YES;  
  33. }  
  34.   
  35. @end  



StartViewController.m 启动画面

[cpp] view plaincopy
  1. #import "StartViewController.h"  
  2.   
  3.   
  4. @implementation StartViewController  
  5.   
  6. - (void)viewDidLoad  
  7. {  
  8.     [super viewDidLoad];  
  9.     //标题栏名称  
  10.     self.title = @"雨松MOMO";  
  11.     //设置视图背景颜色  
  12.     self.view.backgroundColor = [UIColor blackColor];  
  13.     //创建图片视图    
  14.     TTImageView *imageview = [[[TTImageView alloc] initWithFrame:    
  15.                                CGRectMake(100, 50, 120, 120)] autorelease];    
  16.       
  17.     //设置图片视图显示的图片资源  
  18.     imageview.defaultImage = TTIMAGE(@"bundle://0.jpg");  
  19.     //将图片视图加入整个视图当中  
  20.     [self.view addSubview:imageview];  
  21.       
  22.       
  23.       
  24.     //创建label视图    
  25.     UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 30)];    
  26.     //设置显示内容    
  27.     label.text = @"Three20软件开发之FMDB数据库";    
  28.     //设置背景颜色    
  29.     label.backgroundColor = [UIColor blueColor];    
  30.     //设置文字颜色    
  31.     label.textColor = [UIColor whiteColor];    
  32.     //设置显示位置居中    
  33.     label.textAlignment = UITextAlignmentCenter;    
  34.     //设置字体大小    
  35.     label.font = [UIFont fontWithName:[[UIFont familyNames] objectAtIndex:10] size:20];    
  36.       
  37.     [self.view addSubview: label];  
  38.       
  39.       
  40.     NSArray *titles = [[NSArray alloc] initWithObjects:  
  41.                        @"插入数据库",  
  42.                        @"更新数据库",  
  43.                        @"删除数据库",  
  44.                        @"查询数据库",  
  45.                        nil];  
  46.       
  47.      
  48.       
  49.     for (int i =0; i < [titles count]; i++)   
  50.     {  
  51.       
  52.         //创建普通按钮    
  53.         UIButton *button = [UIButton buttonWithType:1];    
  54.         //设置按钮位置  
  55.         button.frame = CGRectMake(0, 200 + i * 40, 320, 30);    
  56.         //设置按钮现实文字  
  57.         [button setTitle:[titles objectAtIndex:i] forState:UIControlStateNormal];    
  58.         //设置按钮标记  
  59.         button.tag = i;  
  60.         //设置按钮点击后 绑定响应方法    
  61.         [button addTarget:self action:@selector(ButtonPressed:) forControlEvents:UIControlEventTouchUpInside];     
  62.         //将按钮添加入视图中  
  63.         [self.view addSubview: button];  
  64.     }  
  65.   
  66.       
  67. }  
  68.   
  69. //按钮点击后将进入这个方法  
  70. -(void)ButtonPressed:(id)buttonID  
  71. {  
  72.     //得到导航对象  
  73.     TTNavigator* navigator = [TTNavigator navigator];    
  74.     //获取点击的按钮  
  75.     UIButton *button = (UIButton *)buttonID;  
  76.     //注解1  
  77.     switch (button.tag) {  
  78.         case 0:  
  79.             [navigator openURLAction:[[TTURLAction actionWithURLPath:@"tt://SQLView/0"] applyAnimated:YES]];    
  80.             break;  
  81.         case 1:  
  82.             [navigator openURLAction:[[TTURLAction actionWithURLPath:@"tt://SQLView/1"] applyAnimated:YES]];    
  83.             break;   
  84.         case 2:  
  85.             [navigator openURLAction:[[TTURLAction actionWithURLPath:@"tt://SQLView/2"] applyAnimated:YES]];    
  86.             break;  
  87.         case 3:  
  88.             [navigator openURLAction:[[TTURLAction actionWithURLPath:@"tt://SQLView/3"] applyAnimated:YES]];    
  89.             break;  
  90.         default:  
  91.             break;  
  92.     }  
  93. }  
  94. @end  



注解1:在启动视图中,点击按钮后程序将进入@"tt://SQLView"页面,该页面对应的控制类是SQLViewController。

SQLViewController.h 

[cpp] view plaincopy
  1. #import <Three20/Three20.h>  
  2. #import "SingleDate.h"  
  3.   
  4.   
  5. @interface SQLViewController : TTTableViewController  
  6. {  
  7.     //记录上一页点击的按钮  
  8.     int pageID;  
  9.     //获取单例模型的实例  
  10.     SingleDate *instance;  
  11.     //数据库对象  
  12.     FMDatabase *db;  
  13. }  
  14. @end  


SQLViewController.m

所有使用FMDB操作数据库的方法都写在这个类中,增加、删除、修改、查询都在里面。请大家先阅读一下,后面我会有详细的注解。

[cpp] view plaincopy
  1. #import "SQLViewController.h"  
  2. #import "FMDatabase.h"  
  3. #import "FMDatabaseAdditions.h"  
  4.   
  5. #define DATABASE_NAME @"Temp.db"  
  6.   
  7.   
  8. @implementation SQLViewController  
  9.   
  10. -(id)initInfo:(int)page  
  11. {  
  12.     if (self = [super init]) {  
  13.         //注解1  
  14.         pageID = page;  
  15.   
  16.     }  
  17.     return self;  
  18.   
  19. }  
  20.   
  21. - (void)viewDidLoad  
  22. {  
  23.   
  24.     [super viewDidLoad];  
  25.       
  26.     //注解2  
  27.       
  28.     //图片的地址  
  29.     NSMutableArray *iconurls = [[[NSMutableArray alloc] initWithObjects:  
  30.                                 @"bundle://0.jpg",  
  31.                                 @"bundle://1.jpg",  
  32.                                 @"bundle://2.jpg",  
  33.                                 @"bundle://3.jpg",  
  34.                                 nil] autorelease];  
  35.     //姓名  
  36.     NSMutableArray *names = [[[NSMutableArray alloc] initWithObjects:  
  37.                              @"雨松MOMO",  
  38.                              @"RORO娃娃",  
  39.                              @"小老虎",  
  40.                              @"哇咔咔",  
  41.                              nil] autorelease];  
  42.     //性别  
  43.     NSMutableArray *sexs = [[NSMutableArray alloc] initWithObjects:  
  44.                              @"男",  
  45.                              @"女",  
  46.                              @"男",  
  47.                              @"女",  
  48.                              nil];  
  49.     //地址  
  50.     NSMutableArray *address = [[[NSMutableArray alloc] initWithObjects:  
  51.                             @"北京市朝阳区",  
  52.                             @"北京市西城区",  
  53.                             @"北京市海淀区",  
  54.                             @"北京市东城区",  
  55.                             nil] autorelease];  
  56.       
  57.     //年龄  
  58.     NSMutableArray *ages = [[[NSMutableArray alloc] initWithObjects:  
  59.                                  [NSNumber numberWithInt:28],  
  60.                                  [NSNumber numberWithInt:27],  
  61.                                  [NSNumber numberWithInt:26],  
  62.                                  [NSNumber numberWithInt:25],  
  63.                                  nil] autorelease];  
  64.       
  65.       
  66.       
  67.     //获取单例对象实例  
  68.     instance = [SingleDate getInstance];  
  69.     //读取数据库  
  70.     db = [instance loadDB:DATABASE_NAME];  
  71.     //结果集  
  72.     FMResultSet *rs = nil;  
  73.     //判断当前页面的ID  
  74.     switch (pageID)   
  75.     {  
  76.         case 0:  
  77.             //注解3  
  78.             if(![db tableExists:@"Message"])  
  79.             {  
  80.                 //创建数据库  
  81.                 if ([db executeUpdate:@"CREATE TABLE Message (_id INTEGER PRIMARY KEY AUTOINCREMENT,IconUrl text, Name text, Sex text,Address text,Age integer)"])   
  82.                 {  
  83.                     NSLog(@"创建表成功");  
  84.                       
  85.                     for(int i=0 ;i<[iconurls count]; i++)  
  86.                     {  
  87.                         //遍历测试数据数组,然后将测试数据写入数据库当中  
  88.                         if([db executeUpdate:@"INSERT INTO Message (IconUrl,Name,Sex,Address,Age) VALUES (?,?,?,?,?)", [iconurls objectAtIndex:i],[names objectAtIndex:i], [sexs objectAtIndex:i],[address objectAtIndex:i],[ages objectAtIndex:i]])  
  89.                         {  
  90.                             NSLog(@"插入数据成功");  
  91.                               
  92.                         }  
  93.                     }  
  94.                 }  
  95.             }  
  96.             //返回一个结果集  
  97.             rs=[db executeQuery:@"SELECT * FROM Message"];    
  98.             break;  
  99.         case 1:  
  100.             //判断当数据库中含有Message表时  
  101.             if([db tableExists:@"Message"])  
  102.             {  
  103.                 //注释4  
  104.                 if([db executeUpdate:@"UPDATE Message SET Name = ? WHERE _id = ? ", @"雨松MOMO(已修改)",[NSNumber numberWithInt:1]])  
  105.                 {  
  106.                     NSLog(@"更新一个数据成功");  
  107.                       
  108.                 }  
  109.                   
  110.                 if([db executeUpdate:@"UPDATE Message SET Name = ? , Sex = ? ,  Address = ? WHERE _id = ? ", @"雨松MOMO(已修改)",@"男(已修改)",@"北京市朝阳区(已修改)",[NSNumber numberWithInt:1]])  
  111.                 {  
  112.                     NSLog(@"更新多条数据成功");  
  113.                       
  114.                 }  
  115.             }  
  116.               //返回一个结果集  
  117.             rs=[db executeQuery:@"SELECT * FROM Message"];    
  118.             break;  
  119.         case 2:  
  120.              //判断当数据库中含有Message表时  
  121.              if([db tableExists:@"Message"])  
  122.              {  
  123.                  //删除数据  
  124.                  if([db executeUpdate:@"DELETE FROM Message WHERE _id = ?",[NSNumber numberWithInt:2]])  
  125.                  {  
  126.                      NSLog(@"删除数据成功");  
  127.                  }  
  128.                
  129.              }  
  130.               //返回一个结果集  
  131.             rs=[db executeQuery:@"SELECT * FROM Message"];    
  132.             break;  
  133.         case 3:  
  134.               //返回一个结果集  
  135.              rs=[db executeQuery:@"SELECT * FROM Message WHERE _id = ? ",[NSNumber numberWithInt:3]];    
  136.             break;  
  137.               
  138.               
  139.         default:  
  140.             break;  
  141.     }  
  142.       
  143.       
  144.     
  145.     
  146.     NSMutableArray * array = [[[NSMutableArray alloc]init] autorelease];  
  147.     //注释5  
  148.     while ([rs next])  
  149.     {    
  150.         [array addObject:[TTTableImageItem itemWithText:[rs stringForColumn:@"Name"]   
  151.                                                imageURL:[rs stringForColumn:@"IconUrl"]  
  152.                                                     URL:[NSString stringWithFormat: @"%@%@", @"tt://MessageView/", [rs stringForColumn:@"_id"]]]];  
  153.     }    
  154.     //用完以后一定要关闭  
  155.     [rs close];  
  156.       
  157.     //设置列表自适应高度    
  158.     self.variableHeightRows = YES;  
  159.     //设置列表现实的数据  
  160.     self.dataSource = [TTSectionedDataSource dataSourceWithArrays:@"当前数据库信息",array,nil];  
  161. }  
  162.   
  163. @end  

 

注解1:(int)page表示上一个页面进入本页面中传递的参数,在本页面中通过它来判断不同的数据库操作。
注解2:这4个数组包含的信息有,图片地址、姓名、性别、地址、年龄,这些数据仅仅起到测试数据的作用,后面我们需要将它们全部写入数据库当中。然后强调一下数据库与表的区别,一个库中可以添加多个表,本节中使用的数据库名称为Temp.db 数据表为Message,请大家注意一下即可。
注解3:tableExists方法用来判断数据库中是否包含某一个表,当数据库中没有该表时,创建该表并且写入数据。如果数据库中有该表则不做任何操作。 为了区分数据库,所以在插入数据时设置第一个参数为 _id INTEGER PRIMARY KEY AUTOINCREMENT它表示在数据库中添加_id字段,并且该字段为自动添值,它是一个向上递增的序列用来区分每条数据。
注解4:用于更新数据,这些都是标准的SQL语句,这里主要强调一下当更新多条数据时 数据之间使用逗号隔开。
注释5:这里主要学习FMResultSet类,它是一个查询的结果集。使用数据库语句找到的数据将全部写入FMResultSet对象当中,然后使用循环判断该对象即可。[rs next]方法表示拨动结果集的指针,返回true表示当前指针对应的结果集当中有值。最后将数据库中拿到的数据已列表的形式显示在视图中,如下图所示,列表中已经写入数据。




在获取数据库对象时,这使用到了单例模式,单例模式在程序语言设计中占据重要地位,而在IOS程序开发中也会频繁使用单例模式。举个简单的例子 从viewA 进入  viewB , 但是viewB需要viewA中计算过的一些数据,有些朋友会想那直接在页面切换时将参数传递给viewB不就行了。这样是可以,但是如果程序中频繁的使用这样的操作肯定会出问题,因为它们属于View层,MVC设计模式告诉我们一定不要让数据放在View层。 在这里单例模型就好比控制器层,在viewA中将数据计算完毕后,写入单例对象中的变量中,进入viewB后,在继续从单例中获取刚刚viewA传递进去的数值。这样的思路会清晰很多,避免后期很多麻烦。


SingleDate.h

[cpp] view plaincopy
  1. #import <Three20/Three20.h>  
  2. #import "FMDatabase.h"  
  3.   
  4. @interface SingleDate : NSObject  
  5. {  
  6.   
  7. }  
  8. //获取单例对象  
  9. +(SingleDate *)getInstance;  
  10. //读取数据库  
  11. -(FMDatabase *)loadDB:(NSString *)dbName;  
  12. @end  



SingleDate.m

[cpp] view plaincopy
  1. #import "SingleDate.h"  
  2.   
  3. @implementation SingleDate  
  4.   
  5. static SingleDate *instance = nil;  
  6.   
  7. //获取单例  
  8. +(SingleDate *)getInstance  
  9. {  
  10.     @synchronized(self)   
  11.     {  
  12.         if (instance == nil)   
  13.         {  
  14.             instance = [[self alloc] init];  
  15.         }  
  16.     }  
  17.     return instance;  
  18. }  
  19.   
  20. //读取数据库  
  21. -(FMDatabase* )loadDB:(NSString *)dbName  
  22. {  
  23.     //获取当前程序路径  
  24.     NSURL *appUrl = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];  
  25.     NSString *dbPath = [[appUrl path] stringByAppendingPathComponent:dbName];  
  26.     //注释1  
  27.     FMDatabase *db= [FMDatabase databaseWithPath:dbPath] ;    
  28.       
  29.     if (![db open]) {    
  30.         [db release];  
  31.         NSLog(@"数据库未能创建/打开");    
  32.         return nil;  
  33.     }      
  34.   
  35.     return db;  
  36. }  
  37. @end  



注释1:如果没有该数据库则创建并且返回数据库对象,如果有该数据库直接返回对象。接着调用[db open]方法判断数据库对象是否创建与打开成功。

        接下来我们看看创建的数据库文件保存在了那里,如果你是用真机调试,那么请在Xcode主界面中点击右上角Organizer按钮,接着选择机器的名称,在机器对应的下拉列表中选择Applications,最后点击右侧页面下方Download按钮将应用程序中的数据下载至本地,解开这个包即可看到创建的数据库。如果你使用模拟器调试,该文件保存的路径为Library(资源库)->Application Support ->iPhone Simulator -> 5.1->Applciations->你的项目->Documents->Temp.db ,这个路径是我的模拟器路径,不同的模拟器版本路径会稍有差异,不过都差不多。如果实在找不到,请输出上方代码的 dbPath,它就是当前你数据库文件的路径。
下面我推荐大家使用在Mac下查看数据库的软件,名称是Navicat Premium。那么本节中也使用这个软件打开上述代码创建的数据库。打开Uavicat Premium 然间后,在左上角出点击Connection下拉列表选择SQLite,然后弹出创建数据库查看页面,接着在Connection Name :输入一个名称(随便即可)Database File:选择刚刚创建的数据库文件的路径, 最后点击右下角OK按钮即可。如下图所示,已经将数据库打开,并且相应的数值已经写入Message数据库当中,这些数据从左到右的含义是:升序ID、图片的地址、名称、性别、地址、年龄,这些应该能看懂吧。怎么样这个软件还不错吧。





        接着是最后的展示页面,该页面将每一条数据库中的所有数据展示在屏幕当中。将选择列表的ID传入该页面,然后通过ID 去数据库中查询数值,从而将所有的信息展示在该页面中,代码在下面,仔细阅读以下就会明白。






MessageInfoController.h


[cpp] view plaincopy
  1. #import <Three20/Three20.h>  
  2. #import "SingleDate.h"  
  3. #import "FMDatabase.h"  
  4. @interface MessageInfoController  
  5. : TTViewController  
  6. {  
  7.     //数据库ID  
  8.     int sqlID;  
  9.     //单例对象  
  10.     SingleDate *instance;  
  11.     //数据库对象  
  12.     FMDatabase *db;  
  13. }  
  14. @end  


MessageInfoController.m

[cpp] view plaincopy
  1. #import "MessageInfoController.h"  
  2.   
  3. #define DATABASE_NAME @"Temp.db"  
  4.   
  5. @implementation MessageInfoController  
  6.   
  7. -(id)initInfo:(int)page  
  8. {  
  9.     if (self = [super init]) {  
  10.   
  11.         sqlID = page;  
  12.           
  13.     }  
  14.     return self;  
  15.       
  16. }  
  17.   
  18. - (void)viewDidLoad  
  19. {  
  20.    
  21.     [super viewDidLoad];  
  22.     instance = [SingleDate getInstance];  
  23.     db = [instance loadDB:DATABASE_NAME];  
  24.     //查询数据库的结果集  
  25.     FMResultSet *rs=[db executeQuery:@"SELECT * FROM Message WHERE _id = ? ",[NSNumber numberWithInt:sqlID]];    
  26.     if(rs != nil)  
  27.     {  
  28.         [rs next];  
  29.         //获取名称  
  30.         NSString * name = [rs stringForColumn:@"Name"];  
  31.         //获取图片  
  32.         UIImage *  image = TTIMAGE([rs stringForColumn:@"IconUrl"]);  
  33.         //获取性别  
  34.         NSString *  sex = [rs stringForColumn:@"Sex"];  
  35.         //获取地址  
  36.         NSString *  address = [rs stringForColumn:@"Address"];  
  37.         //获取年龄  
  38.         int age = [rs intForColumn:@"Age"];  
  39.           
  40.         [rs close];  
  41.           
  42.           
  43.         //标题栏名称  
  44.         self.title = name;  
  45.         //设置视图背景颜色  
  46.         self.view.backgroundColor = [UIColor blackColor];  
  47.         //创建图片视图    
  48.         TTImageView *imageview = [[[TTImageView alloc] initWithFrame:    
  49.                                    CGRectMake(100, 200, 120, 120)] autorelease];    
  50.           
  51.         //设置图片视图显示的图片资源  
  52.         imageview.defaultImage = image;  
  53.         //将图片视图加入整个视图当中  
  54.         [self.view addSubview:imageview];  
  55.       
  56.         //将所有文字信息写入视图当中  
  57.         CGRect frame = CGRectMake(10, 60, 300, 100);  
  58.         TTStyledTextLabel* label = [[[TTStyledTextLabel alloc] initWithFrame:frame] autorelease];  
  59.         NSString* labelText = [NSString stringWithFormat: @"姓名:%@ 性别:%@ 地址:%@ 年龄%d" , name, sex,address,age];   
  60.        
  61.         label.text = [TTStyledText textFromXHTML:labelText];  
  62.         label.font = [UIFont systemFontOfSize:22];  
  63.         [self.view addSubview:label];  
  64.   
  65.   
  66.     }  
  67.       
  68.       
  69. }  
  70. @end  



最后欢迎各位盆友可以和MOMO一起讨论Three20软件开发,如果你觉得看得不清楚,MOMO附带上本章的源码下载,希望大家可以一起学习 哈哈~。哇咔咔~ MOMO愿和 大家好好学习,大家一起进步哈~!!!


下载地址:http://download.csdn.net/detail/xys289187120/4165817