使用第三方库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会更快一点。