数据持久化之FMDB简单使用

使用第三方库FMDB

1.创建.db文件,并取得路径

-(NSString *)getDBPath
{
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                             NSUserDomainMask, YES) objectAtIndex:0];
    NSString *dbPath = [docPath stringByAppendingPathComponent:@"user.db"];
    return dbPath;
}

2.创建表格,保存数据库

先要打开数据库,创建表,向表中插入数据,再执行更新操作,然后关闭数据库

- (IBAction)saveButtonClicked:(id)sender {
    //获取Document文件夹下的数据库文件,没有则创建
    NSString *dbPath = [self getDBPath];
    FMDatabase *database  = [FMDatabase databaseWithPath:dbPath];
    if (![database open]) {
        NSLog(@"Open database failed");
        return;
    }
    
    //创建表
    [database executeUpdate:@"create table user (name text,gender text,age integer)"];
    
    //插入数据
    BOOL insert = [database
                   executeUpdate:@"insert into user values (?,?,?)",
                    self.nameTextField.text,self.genderTextField.text,self.ageTextField.text];
    
    if (insert) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示"
                                                        message:@"保存成功"
                                                       delegate:self
                                              cancelButtonTitle:@"确定"
                                              otherButtonTitles:nil, nil];
        [alert show];
        [alert release];
    }
    // 关闭数据库连接
    [database close];
}

3.查询操作

先打开数据库,执行查询,再关闭数据库

 NSString *dbPath = [self getDBPath];
    FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
    if (![database open]) {
        return;
    }
    
    //不需要像Android中那样关闭Cursor关闭FMResultSet,因为相关的数据库关闭时,FMResultSet也会被自动关闭
    FMResultSet *resultSet = [database executeQuery:@"select * from user"];
    // 条件查询
<p style="margin-top: 0px; margin-bottom: 0px; font-size: 13px; font-family: Menlo;"><span style="color:#424242;">    // </span><strong>FMResultSet</strong> *resultSet = [database <strong>executeQuery</strong>:@"select * from user where name = ?",@"welfred"];</p>    while ([resultSet next]) {
        NSString *name = [resultSet stringForColumn:@"name"];
        NSString *gender = [resultSet stringForColumn:@"gender"];
        int age = [resultSet intForColumn:@"age"];
        NSLog(@"Name:%@,Gender:%@,Age:%d",name,gender,age);
    }
    
    [database close];

4.更新

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docPath = [paths objectAtIndex:0];
    NSString *dbPath = [docPath stringByAppendingPathComponent:@"user.db"];
    
    FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
    if (![database open]) {
        return;
    }
    
    //参数必须是NSObject的子类,int,double,bool这种基本类型,需要封装成对应的包装类才可以
    BOOL update = [database
                   executeUpdate:@"update user set name = ? where age = ?",@"RyanTang",
                   [NSNumber numberWithInt:24]];

    if(update){
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示"
                                                        message:@"更新成功"
                                                       delegate:self
                                              cancelButtonTitle:@"确定"
                                              otherButtonTitles:nil, nil];
        [alert show];
        [alert release];
    }

    [database close];

5.删除

 NSString *dbPath = [self getDBPath];
    FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
    if (![database open]) {
        return;
    }

    BOOL delete = [database executeUpdate:@"delete from user where name = ?",@"Tang"];
    if (delete) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"删除成功" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
        [alert show];
        [alert release];

    }
    
    [database close];

6涉及多线程

FMDatabaseQueue * queue = [FMDatabaseQueue databaseQueueWithPath:database_path];
    dispatch_queue_t q1 = dispatch_queue_create("queue1", NULL);
    dispatch_queue_t q2 = dispatch_queue_create("queue2", NULL);
    
    dispatch_async(q1, ^{
        for (int i = 0; i < 50; ++i) {
            [queue inDatabase:^(FMDatabase *db2) {
                
                NSString *insertSql1= [NSString stringWithFormat:
                                       @"INSERT INTO '%@' ('%@', '%@', '%@') VALUES (?, ?, ?)",
                                       TABLENAME, NAME, AGE, ADDRESS];
                
                NSString * name = [NSString stringWithFormat:@"jack %d", i];
                NSString * age = [NSString stringWithFormat:@"%d", 10+i];
                
                
                BOOL res = [db2 executeUpdate:insertSql1, name, age,@"济南"];
                if (!res) {
                    NSLog(@"error to inster data: %@", name);
                } else {
                    NSLog(@"succ to inster data: %@", name);
                }
            }];
        }
    });
    
    dispatch_async(q2, ^{
        for (int i = 0; i < 50; ++i) {
            [queue inDatabase:^(FMDatabase *db2) {
                NSString *insertSql2= [NSString stringWithFormat:
                                       @"INSERT INTO '%@' ('%@', '%@', '%@') VALUES (?, ?, ?)",
                                       TABLENAME, NAME, AGE, ADDRESS];
                
                NSString * name = [NSString stringWithFormat:@"lilei %d", i];
                NSString * age = [NSString stringWithFormat:@"%d", 10+i];
                
                BOOL res = [db2 executeUpdate:insertSql2, name, age,@"北京"];
                if (!res) {
                    NSLog(@"error to inster data: %@", name);
                } else {
                    NSLog(@"succ to inster data: %@", name);
                }
            }];
        }
    });


附:Sqlite中INTEGER PRIMARY KEY AUTOINCREMENT和rowid的使用
参考http://www. cnblogs.com/peida/archive/2008/11/29/1343832.html


在用sqlite设计表时,突然想到一个问题,就是我设计的表中,每个表都有一个自己的整形 id值作为主键,其实可以不指定这么一个id值,sqlite内部本来就会为每个表加上一个 rowid,这个rowid可以当成一个隐含的字段使用,但 是由sqlite引擎来维护的,在3.0以前rowid是32位的整数,3.0以后是64位的整数,为什么不直接使用这个内部的rowid作为每个表的 id主键呢。
想到就立即先查找一下sqlite的文档,看看用指定INTEGER PRIMARY KEY AUTOINCREMENT 和不指定自增长字段用rowid有什么区别。相关的文档在这里: http://www.sqlite.org/autoinc.htmlhttp://www.sqlite.org/faq.html
使用自增长字段为主键有不少问题,比如维护或是在大型分布应用中主键冲突的解决等。在一些大型分布应用中主键一般选用guid,这可以有效的避免主键冲突,减少对主键维护的工程。当然,对于中小型的应用,自增长字段的好处更多一些,简单、快速。
Sqlite中,一个自增长字段定义为INTEGER PRIMARY KEY AUTOINCREMENT  ,那么在插入一个新数据时,只需要将这个字段的值指定为NULL,即可由引擎自动设定其值,引擎会设定为最大的rowid+1。当然,也可以设置为非 NULL的数字来自己指定这个值,但这样就必须自己小心,不要引起冲突。当这个rowid的值大于所能表达的最大值 9223372036854775807 (3.0及以后版本的rowid最大值)后,rowid的新值会这个最大数之前随机找一个没被使用了的值。所以在rowid达到最大值前,rowid的值 是严格单调增加的。
INTEGER PRIMARY KEY AUTOINCREMENT 自增长字段的算法与rowid稍微有些不同。
第一,在达到最大值后, rowid会找已被删除的字段对应的rowid作为新值 ,而 自增长字段则会丢出一个SQLITE_FULL的错误
第二,自增长字段在增加新值时,是找 一个从没被使用过的rowid作为新值 ,而 rowid则是找最大已存在的rowid+1 。这里对应用的影响会比较大,尤其是一些对id值有依赖的元记录,只适合使用自增长字段而不能用rowid。比如,我们设计一个元记录表:
Create table meta_struct(id INTEGER PRIMARY KEY AUTOINCREMENT, name varchar, type Integer);
然后,定义一个一级表,来描述其它表的结构:
Create table meta_table(tableid INTEGER, table_field integer)
最后,我们的应用可以根据这个一级表来产生实际使用的二级表。 这样为保证兼容性 meta_struct中的id必须是唯一的,如果有字段被删除,也不能重复使用这个字段的id值,不然,在数据库合并时,一级表和二级表就会混乱。所以 meta_struct表中的主键只能使用自增长字段,而不能用rowid。
第三,使用自增 长字段,引擎会自动产生一个sqlite_sequence表,用于记录每个表的自增长字段的已使用的最大值,用户可以看到,并可以用使用Update、 Delete和Insert操作,但不建议这么使用,这会让引擎混乱。如果使用rowid,也会有这么一个内部表,用户可以维护rowid值,但看不到。
这么看来,如果直接使用rowid来代替自增加字段,根据两者的细微的差别,需要注意是否与自己的应用冲突,如果没有冲突,那么用rowid会更快一点。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值