到年底了,公司基本上没什么事情了。这一段时间研究了下FMDB,公司项目用的是coredata。查了一下资料对比了一下realm,FMDB,coredata。
realm是性能最高的。但是realm国内资料基本不多,而且大部分都是官网公布的例子。而且cocoapods基本拉不下来。但是也花了2天时间使用了一下realm,给我的感觉就是难用。而且对于整个工程来说会复杂很多,因为realm模型是必须继承RealmObject的。这样就必须多出一套模型。
FMDB基于sqlite用OC封装的一个库。FMDB最好是用一个单例封装起来,方便项目的使用。提供一个Demo,有需要的同学可以去看看。
先说单线程操作数据库:
创建一个叫FMDBManage的单例类,然后倒入相关的头文件,创建对象。
创建数据库:
- (void)createFMDB
{
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"test.sqlite"];
NSLog(@"数据库地址:%@",filePath);
self.db = [FMDatabase databaseWithPath:filePath];
self.queue = [FMDatabaseQueue databaseQueueWithPath:filePath];
}
创建表:
- (void)createTable
{
if ([self.db open]) {
NSString *userSql = @"CREATE TABLE IF NOT EXISTS 'user' (user_phone test primary key,user_id test,user_sex test,time test)";
NSString *travelSql = @"CREATE TABLE IF NOT EXISTS 'travel' (travel_id test primary key,own_id test,title test,date test)";
if (![self.db executeUpdate:userSql]) {
NSLog(@"创建表失败");
}
if (![self.db executeUpdate:travelSql]) {
NSLog(@"创建表失败");
}
[self.db close];
}
}
操作数据库的时候必须要先open然后使用完了在close。创建表的时候注意设置约束。
保存数据:
- (void)saveUser:(User *)user
{
if ([self.db open]) {
BOOL insert = [self.db executeUpdate:@"INSERT INTO user(user_phone,user_id,user_sex,time)VALUES(?,?,?,?)",user.phone,user.ID,user.sex,[self currentTime]];
if (!insert) {
//执行更新操作
NSLog(@"执行更新操作");
[self updateUser:user];
}
[self.db close];
}
}
因为已经设置好了主键,所以不管是新用户数据还是更新数据。我们都只需要在插入失败时自动去调用更新数据API。
更新数据:
- (void)updateUser:(User *)user
{
if ([self.db open]) {
[self.db executeUpdate:@"UPDATE 'user' SET user_id = ?, user_sex = ?,time = ? WHERE user_phone = ? ",user.ID,user.sex,[self currentTime],user.phone];
[self.db close];
}
}
查询数据:
//查询所有user数据,按照某一个字段降序排列(默认是升序小到大)
- (NSMutableArray *)getAllUserOrderByTime
{
NSMutableArray *userArr = [[NSMutableArray alloc]init];
if ([self.db open]) {
FMResultSet *result = [self.db executeQuery:@"select * from user order by time desc"];
while ([result next]) {
User *user = [[User alloc]init];
user.ID = [result stringForColumn:@"user_id"];
user.phone = [result stringForColumn:@"user_phone"];
user.sex = [result stringForColumn:@"user_sex"];
user.lastLogintime = [result stringForColumn:@"time"];
[userArr addObject:user];
}
[self.db close];
}
return userArr;
}
删除数据:
- (void)deleteUserWithPhone:(NSString *)phone
{
if ([self.db open]) {
[_db executeUpdate:@"DELETE FROM user WHERE user_phone = ?",phone];
[self.db close];
}
}
下面在说下怎么关联表。比如你的app用户登录了,然后在这个用户下产生的数据肯定就只是这个用户的数据,其他用户是不能访问的。
创建两个数据Model。
怎么去关联表呢?在创建表的代码中有一个own_id字段,这个字段用用户的唯一标识去设置。取得时候也通过用户唯一标识就OK了。
给当前用户添加Travel模型:
//给当前登录用户添加行程
- (void)saveTravel:(Travel *)travel toUser:(NSString *)phone
{
if ([self.db open]) {
[self.db executeUpdate:@"INSERT INTO travel(travel_id,own_id,title,date)VALUES(?,?,?,?)",travel.travel_id,phone,travel.title,[self baseDecodeWithDic:travel.totalDic]];
[self.db close];
}
}
//查询当前登录用户的行程
- (void)getTravelAt:(NSString *)travelID fromUser:(NSString *)phone
{
if ([self.db open]) {
FMResultSet *result = [self.db executeQuery:@"select * from travel where own_id = ?",phone];
while ([result next]) {
Travel *travel = [[Travel alloc]init];
travel.travel_id = [result stringForColumn:@"travel_id"];
travel.own_id = [result stringForColumn:@"own_id"];
travel.title = [result stringForColumn:@"title"];
NSString *strData = [result stringForColumn:@"date"];
travel.totalDic = [self baseEncodeWithString:strData];
NSLog(@"travel:%@",travel);
}
[self.db close];
}
}
//删除某一个用户的所有行程
- (void)deleteAllTravelFromUser:(NSString *)phone
{
if ([self.db open]) {
[self.db executeUpdate:@"DELETE FROM travel WHERE own_id = ?",phone];
[self.db close];
}
}
如果app需要自动登录功能,那怎么去确定那个用户是最后一个登录的。我这里使用到一个时间戳,通过对时间排序去判断用户登录的先后。
- (NSString *)currentTime
{
NSDateFormatter *formatter =[[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyyMMddHHmmss"];
NSString *currentTime = [formatter stringFromDate:[NSDate date]];
return currentTime;
}
如果app需要存储字典或数组的话,就需要对字典或数组进行编码:
//字典编码
- (NSString *)baseDecodeWithDic:(NSMutableArray *)dic{
NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:0 error:nil];
NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
return str;
}
//字符串解码
- (NSMutableArray *)baseEncodeWithString:(NSString *)str
{
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
NSMutableArray *dic = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
return dic;
}
这些都是在同一个线程下操作数据库。但是当多线程去操作数据库的话,数据就会混乱。下面说下线程安全:
//插入数据,如果表中已有则执行更新操作(线程安全)
- (void)saveUserWithQueue:(User *)user
{
dispatch_queue_t q1 = dispatch_queue_create("saveUser", NULL);
dispatch_async(q1, ^{
[self.queue inDatabase:^(FMDatabase *db) {
[self.db open];
BOOL insert = [self.db executeUpdate:@"INSERT INTO user(user_phone,user_id,user_sex)VALUES(?,?,?)",user.phone,user.ID,user.sex];
if (!insert) {
//执行更新操作
NSLog(@"执行更新操作");
}
[self.db close];
}];
});
}
//更新数据,(线程安全)
- (void)updateUserWithQueue:(User *)user
{
dispatch_queue_t q1 = dispatch_queue_create("updateUser", NULL);
dispatch_async(q1, ^{
[self.queue inDatabase:^(FMDatabase *db) {
[self.db open];
[self.db executeUpdate:@"UPDATE 'user' SET user_id = ? WHERE user_phone = ? ",user.ID,user.phone];
[self.db executeUpdate:@"UPDATE 'user' SET user_sex = ? WHERE user_phone = ? ",user.sex,user.phone];
[self.db close];
}];
});
}
就简单的举着两个例子。