14

Three20研究院之结合第三方FMDB框架操作数据库详解(四)

 3 years ago
source link: https://www.xuanyusong.com/archives/638
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Three20研究院之结合第三方FMDB框架操作数据库详解(四)

开始本章之间,首先了解一下FMDB,它是一个老外写的第三方操作数据库的框架。因为IOS官方提供操作数据库的方法似乎不是那么近人而意,使用起来有点麻烦,所以这个老外哥们就将官方提供的方法又包了一层,它封装的方法非常浅,并没有多莫高深,只是更方便开发使用,我看了一下大概核心的代码500多行吧。下面开始学习它,首先需要将源码下载至本地,这哥们将代码上传至GitHub当中,(我现在越来越喜欢GitHub了,在这里总能找到老外写的好玩的东西)。
无需注册,在页面左上角标题下方点击“ZIP”按钮,即可将源码下载至本地,接着解压文件,然后将源码中src文件夹中的所有代码拷贝至工程当中。由于Xcode4.3.1有一个BUG比较恶心,不支持文件夹的拖拽添加至工程当中,所以需要手动添加文件夹。如下图所示,在下拉列表中点击“Add Files to Some Project”按钮,然后在我的电脑中 选择刚刚解压后的src文件夹,将FMDB中所有源码添加至当前工程。
Three20研究院之结合第三方FMDB框架操作数据库详解(四) - 雨松MOMO程序研究院 - 1
FMDB的源码添加完毕后,然后删除fmdb.m 这是作者提供工程的入口类,有兴趣的朋友可以自行研究,我们只学习如何使用这个框架。 目前程序是无法编译通过的,因为缺少类库文件,如下图所示,请将libsqlite3.0dylib添加至工程。
类库文件添加完毕后,到这一步工程就应该可以正常编译了。如果到这一步你的工程还有错误,那就恭喜你自己在好好检查检查工程的结构啦。接下来将步入正题,老规矩还是以一个实例的形式向大家介绍如何使用FMDB,如下图所以,又是我们熟悉的界面。
Three20研究院之结合第三方FMDB框架操作数据库详解(四) - 雨松MOMO程序研究院 - 3
本示例中,程序中共用到了3个控制类,它们是:
StartViewController:上图中绘制的启动视图。
SQLViewController:增、删、改、查后在屏幕中显示的列表视图。
MessageInfoController:选择数据库中某一项数据后将所有信息展示在这个视图中。
首先是程序的启动类,这里说明一下,有关以前提到的知识本节我就不在赘述,如果不理解的朋友请阅读本系列教程之前的文章。
AppDelegate.m
#import "AppDelegate.h"
#import "StartViewController.h"
#import "SQLViewController.h"
#import "MessageInfoController.h"
@implementation AppDelegate
@synthesize window = _window;
- (void)dealloc
    [_window release];
    [super dealloc];
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    TTNavigator* navigator = [TTNavigator navigator];
    navigator.persistenceMode = TTNavigatorPersistenceModeAll;
    navigator.window = [[[UIWindow alloc] initWithFrame:TTScreenBounds()] autorelease];
    TTURLMap* map = navigator.URLMap;
    [map from:@"*" toViewController:[TTWebController class]];
    [map from:@"tt://StartView" toSharedViewController:[StartViewController class]];
    [map from:@"tt://SQLView/(initInfo:)" toViewController:[SQLViewController class]];
    [map from:@"tt://MessageView/(initInfo:)" toViewController:[MessageInfoController class]];
    if (![navigator restoreViewControllers])
        [navigator openURLAction:[TTURLAction actionWithURLPath:@"tt://StartView"]];
    return YES;
StartViewController.m 启动画面
#import "StartViewController.h"
@implementation StartViewController
- (void)viewDidLoad
    [super viewDidLoad];
    //标题栏名称
    self.title = @"雨松MOMO";
    //设置视图背景颜色
    self.view.backgroundColor = [UIColor blackColor];
    //创建图片视图
    TTImageView *imageview = [[[TTImageView alloc] initWithFrame:
                               CGRectMake(100, 50, 120, 120)] autorelease];  
    //设置图片视图显示的图片资源
    imageview.defaultImage = TTIMAGE(@"bundle://0.jpg");
    //将图片视图加入整个视图当中
    [self.view addSubview:imageview];
    //创建label视图
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 30)];
    //设置显示内容
    label.text = @"Three20软件开发之FMDB数据库";
    //设置背景颜色
    label.backgroundColor = [UIColor blueColor];
    //设置文字颜色
    label.textColor = [UIColor whiteColor];
    //设置显示位置居中
    label.textAlignment = UITextAlignmentCenter;
    //设置字体大小
    label.font = [UIFont fontWithName:[[UIFont familyNames] objectAtIndex:10] size:20];  
    [self.view addSubview: label];
    NSArray *titles = [[NSArray alloc] initWithObjects:
                       @"插入数据库",
                       @"更新数据库",
                       @"删除数据库",
                       @"查询数据库",
                       nil];
    for (int i =0; i < [titles count]; i++)
        //创建普通按钮
        UIButton *button = [UIButton buttonWithType:1];
        //设置按钮位置
        button.frame = CGRectMake(0, 200 + i * 40, 320, 30);
        //设置按钮现实文字
        [button setTitle:[titles objectAtIndex:i] forState:UIControlStateNormal];
        //设置按钮标记
        button.tag = i;
        //设置按钮点击后 绑定响应方法
        [button addTarget:self action:@selector(ButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
        //将按钮添加入视图中
        [self.view addSubview: button];
//按钮点击后将进入这个方法
-(void)ButtonPressed:(id)buttonID
    //得到导航对象
    TTNavigator* navigator = [TTNavigator navigator];
    //获取点击的按钮
    UIButton *button = (UIButton *)buttonID;
    //注解1
    switch (button.tag) {
        case 0:
            [navigator openURLAction:[[TTURLAction actionWithURLPath:@"tt://SQLView/0"] applyAnimated:YES]];
            break;
        case 1:
            [navigator openURLAction:[[TTURLAction actionWithURLPath:@"tt://SQLView/1"] applyAnimated:YES]];
            break;
        case 2:
            [navigator openURLAction:[[TTURLAction actionWithURLPath:@"tt://SQLView/2"] applyAnimated:YES]];
            break;
        case 3:
            [navigator openURLAction:[[TTURLAction actionWithURLPath:@"tt://SQLView/3"] applyAnimated:YES]];
            break;
        default:
            break;
注解1:在启动视图中,点击按钮后程序将进入@”tt://SQLView”页面,该页面对应的控制类是SQLViewController。
SQLViewController.h 
#import <Three20/Three20.h>
#import "SingleDate.h"
@interface SQLViewController : TTTableViewController
    //记录上一页点击的按钮
    int pageID;
    //获取单例模型的实例
    SingleDate *instance;
    //数据库对象
    FMDatabase *db;
SQLViewController.m
所有使用FMDB操作数据库的方法都写在这个类中,增加、删除、修改、查询都在里面。请大家先阅读一下,后面我会有详细的注解。
#import "SQLViewController.h"
#import "FMDatabase.h"
#import "FMDatabaseAdditions.h"
#define DATABASE_NAME @"Temp.db"
@implementation SQLViewController
-(id)initInfo:(int)page
    if (self = [super init]) {
        //注解1
        pageID = page;
    return self;
- (void)viewDidLoad
    [super viewDidLoad];
    //注解2
    //图片的地址
    NSMutableArray *iconurls = [[[NSMutableArray alloc] initWithObjects:
                                @"bundle://0.jpg",
                                @"bundle://1.jpg",
                                @"bundle://2.jpg",
                                @"bundle://3.jpg",
                                nil] autorelease];
    NSMutableArray *names = [[[NSMutableArray alloc] initWithObjects:
                             @"雨松MOMO",
                             @"RORO娃娃",
                             @"小老虎",
                             @"哇咔咔",
                             nil] autorelease];
    NSMutableArray *sexs = [[NSMutableArray alloc] initWithObjects:
                             @"男",
                             @"女",
                             @"男",
                             @"女",
                             nil];
    NSMutableArray *address = [[[NSMutableArray alloc] initWithObjects:
                            @"北京市朝阳区",
                            @"北京市西城区",
                            @"北京市海淀区",
                            @"北京市东城区",
                            nil] autorelease];
    NSMutableArray *ages = [[[NSMutableArray alloc] initWithObjects:
                                 [NSNumber numberWithInt:28],
                                 [NSNumber numberWithInt:27],
                                 [NSNumber numberWithInt:26],
                                 [NSNumber numberWithInt:25],
                                 nil] autorelease];
    //获取单例对象实例
    instance = [SingleDate getInstance];
    //读取数据库
    db = [instance loadDB:DATABASE_NAME];
    //结果集
    FMResultSet *rs = nil;
    //判断当前页面的ID
    switch (pageID)
        case 0:
            //注解3
            if(![db tableExists:@"Message"])
                //创建数据库
                if ([db executeUpdate:@"CREATE TABLE Message (_id INTEGER PRIMARY KEY AUTOINCREMENT,IconUrl text, Name text, Sex text,Address text,Age integer)"])
                    NSLog(@"创建表成功");
                    for(int i=0 ;i<[iconurls count]; i++)
                        //遍历测试数据数组,然后将测试数据写入数据库当中
                        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]])
                            NSLog(@"插入数据成功");
            //返回一个结果集
            rs=[db executeQuery:@"SELECT * FROM Message"];
            break;
        case 1:
            //判断当数据库中含有Message表时
            if([db tableExists:@"Message"])
                //注释4
                if([db executeUpdate:@"UPDATE Message SET Name = ? WHERE _id = ? ", @"雨松MOMO(已修改)",[NSNumber numberWithInt:1]])
                    NSLog(@"更新一个数据成功");
                if([db executeUpdate:@"UPDATE Message SET Name = ? , Sex = ? ,  Address = ? WHERE _id = ? ", @"雨松MOMO(已修改)",@"男(已修改)",@"北京市朝阳区(已修改)",[NSNumber numberWithInt:1]])
                    NSLog(@"更新多条数据成功");
              //返回一个结果集
            rs=[db executeQuery:@"SELECT * FROM Message"];
            break;
        case 2:
             //判断当数据库中含有Message表时
             if([db tableExists:@"Message"])
                 //删除数据
                 if([db executeUpdate:@"DELETE FROM Message WHERE _id = ?",[NSNumber numberWithInt:2]])
                     NSLog(@"删除数据成功");
              //返回一个结果集
            rs=[db executeQuery:@"SELECT * FROM Message"];
            break;
        case 3:
              //返回一个结果集
             rs=[db executeQuery:@"SELECT * FROM Message WHERE _id = ? ",[NSNumber numberWithInt:3]];
            break;
        default:
            break;
    NSMutableArray * array = [[[NSMutableArray alloc]init] autorelease];
    //注释5
    while ([rs next])
        [array addObject:[TTTableImageItem itemWithText:[rs stringForColumn:@"Name"]
                                               imageURL:[rs stringForColumn:@"IconUrl"]
                                                    URL:[NSString stringWithFormat: @"%@%@", @"tt://MessageView/", [rs stringForColumn:@"_id"]]]];
    //用完以后一定要关闭
    [rs close];
    //设置列表自适应高度
    self.variableHeightRows = YES;
    //设置列表现实的数据
    self.dataSource = [TTSectionedDataSource dataSourceWithArrays:@"当前数据库信息",array,nil];
注解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

#import <Three20/Three20.h>
#import "FMDatabase.h"
@interface SingleDate : NSObject
//获取单例对象
+(SingleDate *)getInstance;
//读取数据库
-(FMDatabase *)loadDB:(NSString *)dbName;

SingleDate.m

#import "SingleDate.h"
@implementation SingleDate
static SingleDate *instance = nil;
//获取单例
+(SingleDate *)getInstance
    @synchronized(self)
        if (instance == nil)
            instance = [[self alloc] init];
    return instance;
//读取数据库
-(FMDatabase* )loadDB:(NSString *)dbName
    //获取当前程序路径
    NSURL *appUrl = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    NSString *dbPath = [[appUrl path] stringByAppendingPathComponent:dbName];
    //注释1
    FMDatabase *db= [FMDatabase databaseWithPath:dbPath] ;  
    if (![db open]) {
        [db release];
        NSLog(@"数据库未能创建/打开");
        return nil;
    return db;

注释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、图片的地址、名称、性别、地址、年龄,这些应该能看懂吧。怎么样这个软件还不错吧。
Three20研究院之结合第三方FMDB框架操作数据库详解(四) - 雨松MOMO程序研究院 - 5
        接着是最后的展示页面,该页面将每一条数据库中的所有数据展示在屏幕当中。将选择列表的ID传入该页面,然后通过ID 去数据库中查询数值,从而将所有的信息展示在该页面中,代码在下面,仔细阅读以下就会明白。
MessageInfoController.h
#import <Three20/Three20.h>
#import "SingleDate.h"
#import "FMDatabase.h"
@interface MessageInfoController
: TTViewController
    //数据库ID
    int sqlID;
    //单例对象
    SingleDate *instance;
    //数据库对象
    FMDatabase *db;
MessageInfoController.m
#import "MessageInfoController.h"
#define DATABASE_NAME @"Temp.db"
@implementation MessageInfoController
-(id)initInfo:(int)page
    if (self = [super init]) {
        sqlID = page;
    return self;
- (void)viewDidLoad
    [super viewDidLoad];
    instance = [SingleDate getInstance];
    db = [instance loadDB:DATABASE_NAME];
    //查询数据库的结果集
    FMResultSet *rs=[db executeQuery:@"SELECT * FROM Message WHERE _id = ? ",[NSNumber numberWithInt:sqlID]];
    if(rs != nil)
        [rs next];
        //获取名称
        NSString * name = [rs stringForColumn:@"Name"];
        //获取图片
        UIImage *  image = TTIMAGE([rs stringForColumn:@"IconUrl"]);
        //获取性别
        NSString *  sex = [rs stringForColumn:@"Sex"];
        //获取地址
        NSString *  address = [rs stringForColumn:@"Address"];
        //获取年龄
        int age = [rs intForColumn:@"Age"];
        [rs close];
        //标题栏名称
        self.title = name;
        //设置视图背景颜色
        self.view.backgroundColor = [UIColor blackColor];
        //创建图片视图
        TTImageView *imageview = [[[TTImageView alloc] initWithFrame:
                                   CGRectMake(100, 200, 120, 120)] autorelease];  
        //设置图片视图显示的图片资源
        imageview.defaultImage = image;
        //将图片视图加入整个视图当中
        [self.view addSubview:imageview];
        //将所有文字信息写入视图当中
        CGRect frame = CGRectMake(10, 60, 300, 100);
        TTStyledTextLabel* label = [[[TTStyledTextLabel alloc] initWithFrame:frame] autorelease];
        NSString* labelText = [NSString stringWithFormat: @"姓名:%@ 性别:%@ 地址:%@ 年龄%d" , name, sex,address,age];
        label.text = [TTStyledText textFromXHTML:labelText];
        label.font = [UIFont systemFontOfSize:22];
        [self.view addSubview:label];

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

(下载后必需搭建three20环境成功后才能运行~ 因为three20为引用加载,所以程序路径都是我本机的请见谅!或者你可可以将你的Three20路径修改的和我一样就可以直接运行啦,我的路径是:User (用户) -> Share(共享)->Three20)。
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK