数据持久化 coredata、sqlite、fmdb和sqlitepersistentobject

来源:互联网 发布:越南旅游 知乎 编辑:程序博客网 时间:2024/04/30 13:17

在ios开发过程中,经常需要用到数据持久化工作。对于基本的配置信息等,NSUserDefault已经可以满足要求,但是对于大部分需要存储的信息,主要的方式有coredata建模或者sqlite写数据库的方法进行存储。现在针对coredata、sqlite以及常用的sqlite封装库进行研究和学习。 

    首先,针对四种数据持久化方式,进行基本的10w条数据插入得到性能耗时如下(由于sqlitepersistentobject未找到合适的批量插入方法,所以没有进行对比): 

  sqlite          1470ms 

  fmdb          3088ms 

  coredata    3418ms 

 

从中可以看出,sqlite最快,基本都只用fmdb以及coredata等的一半时间。接下来我们逐个解析相关技术/库的操作以及使用。 

 【coreData】 

    coredata相信基本都不陌生,我们常用的主要就是DataModel了,有两种方法添加Data Model:新建工程时勾选或者添加Core Data->DataModel文件。采用第一种方法默认会有相关的代码生成,为了更好的了解CoreData是怎么load进来的,我们采用第二种方法进行演示。 

 首先,我们添加CoreData->ModelData模型,名字输入为coreData,即可在文件列表中见到coreData.xcdatamodeld文件。我们先添加如下Entities: 

 我们已经有了模型,那么该怎样使用呢。有一个NSManagedObjectModel的类,专门用来管理数据模型的。先从coreData.xcdatamodeld中初始化模型: 

 

  1. // initilize  
  2.     NSURL*modelURL = [[NSBundle mainBundle] URLForResource:@"coreData"withExtension:@"momd"];  
  3.     coreDataModel= [[NSManagedObjectModel alloc]initWithContentsOfURL:modelURL];  
  4. 我们可以理解为,coreDataModel就代表了该模型。
  5. 接下来,我们需要考虑,有了模型后,数据最终应该存在哪里?答案是:文件。接下来我们有另外一个类来管理模型跟文件之间的对应关系:
  6. [code]NSString* strInfoPath = [[NSHomeDirectory()stringByAppendingPathComponent:@"Documents"]stringByAppendingPathComponent:@"coreData.sqlite"];  
  7. coreDataCoordinator = [[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:coreDataModel];  
  8.       
  9. [coreDataCoordinatoraddPersistentStoreWithType:NSSQLiteStoreType configuration:nilURL:[NSURL fileURLWithPath:strInfoPath] options:nilerror:nil];  
  10. OK,现在文件<->模型之间的映射关系也有了。那么接下来我们就需要去对模型数据库进行读写操作等。自然,有一个上下文用来执行/处理相关数据信息:
  11. [code]// 对context进行操作  
  12.     coreDataContext= [[NSManagedObjectContext alloc]init];  
  13.     [coreDataContextsetPersistentStoreCoordinator:coreDataCoordinator];  
  14. 我们尝试去插入一条数据:
  15. [code]NSManagedObject* object = [NSEntityDescriptioninsertNewObjectForEntityForName:@"TestCoreData"inManagedObjectContext:coreDataContext];  
  16.           
  17. [object setValue:[NSNumber numberWithInt:data->intType]forKey:@"intType"];  
  18. [object setValue:[NSNumber numberWithFloat:data->floatType]forKey:@"floatType"];  
  19. [object setValue:[NSNumbernumberWithDouble:data->doubleType]forKey:@"doubleType"];  
  20. [object setValue:[NSStringstringWithUTF8String:data->testString]forKey:@"stringType"];

 插入数据过程中,首先获取一个NSManagedObject的对象,可以这么理解,NSManagedObject就代表了一条数据信息,我们用insertNewObjectForEntityForName往TestCoreData这个数据模型中添加了一条数据:object。后面的[objectsetValue:"***" forkey:"***"]即为该条插入的数据进行赋值。 

添加完毕数据后,内存中模型已经有添加的数据了。但是文件中还没有同步进去,那么我们调用: 

[coreDataContext save:nil]; 

 即可将刚才插入的数据保存到文件中。 

关于其他修改、删除等操作,以及coredata中的releationship操作等,可以参考附件程序或查阅相关其他资料。 

【sqlite】 

    sqlite是一款轻量级数据库,在c、c++、java以及其他各种产品中都有涉及到,xcode对sqlite也提供的原生支持。使用sqlite也不难,只要在LinkBinary WithLibraries中添加ibsqlite3.0.dylib(我是用的3.0版本),然后在需要使用的地方添加头文件:#include即可。 

    sqlite的操作很方便,也很直观: 

  1. - (IBAction)onBtnSqlite:(id)sender {  
  2.       
  3.     //初始化数据库要保存的地方,如果存在则删除  
  4.     NSString*strSQLiteFilePath = [[NSHomeDirectory()stringByAppendingPathComponent:@"Documents"]stringByAppendingPathComponent:@"sqlite.sqlite"];  
  5.       
  6.     BOOLbIsDir = FALSE;  
  7.     if([[NSFileManager defaultManager] fileExistsAtPath:strSQLiteFilePathisDirectory:&bIsDir]){  
  8.         [[NSFileManagerdefaultManager] removeItemAtPath:strSQLiteFilePatherror:nil];  
  9.      
  10.       
  11.     sqlite3*sqlite = NULL;  
  12.       
  13.     //首先打开数据库路径,如果不存在则创建  
  14.     if(SQLITE_OK != sqlite3_open([strSQLiteFilePath UTF8String],&sqlite)) {  
  15.         NSLog(@"sqlite3:open error...");  
  16.      
  17.       
  18.     //create table  
  19.     //创建表,主要就是sql语句  
  20.     NSString*strCreateTable = @"CREATE TABLE TESTCOREDATA(intType INTEGER,floatType FLOAT, doubleType DOUBLE, stringTypeVARCHAR(256))";  
  21.     if(sqlite3_exec(sqlite, [strCreateTable UTF8String], nil, nil, nil)!= SQLITE_OK) {  
  22.         NSLog(@"sqliteCreate table error...");  
  23.      
  24.     //接下来是生成10w条测试数据  
  25.     NSArray*arrayTest = [selfarrayWithData:100000];  
  26.     NSLog(@"Beforesave...");  
  27.     //!!!这里很重要,将所有的insert操作作为一个transaction操作,这样避免每次insert的时候都去写文件,导致IO时间拖慢整个数据插入操作  
  28.     NSString*strBegin = @"BEGINTRANSACTION";  
  29.     sqlite3_exec(sqlite,[strBegin UTF8String], NULL, NULL,NULL);  
  30.     //遍历数据并插入,就是普通的sql语句操作  
  31.     for(NSValue* value inarrayTest)  
  32.      
  33.         Data*data = [valuepointerValue];  
  34.           
  35.         NSString*strSQLInsert = [NSString stringWithFormat:@"INSERT INTOTESTCOREDATA(intType, floatType, doubleType, stringType) values(%d,%f, %lf, '%s')", data->intType, data->floatType,data->doubleType,data->testString];  
  36.           
  37.         if(SQLITE_OK != sqlite3_exec(sqlite, [strSQLInsert UTF8String], NULL,NULL, NULL))  
  38.          
  39.             constchar* errormsg =sqlite3_errmsg(sqlite);  
  40.             NSLog(@"execError...");  
  41.          
  42.           
  43.         free(data);  
  44.      
  45.       
  46.     //提交所有的插入操作  
  47.     NSString*strEnd = @"COMMIT";  
  48.     sqlite3_exec(sqlite,[strEnd UTF8String], NULL, NULL,NULL);  
  49.       
  50.     NSLog(@"EndSave...");  
  51.       
  52.       
  53.     //不使用的时候关闭即可  
  54.     sqlite3_close(sqlite);  
  55.     sqlite= NULL;  
  56.       
  57. }

sqlite非常直观,并且依赖于sql语句,所以sqlite的有点在于灵活性高,上手简单并易于理解。缺点就是带来了很多底层数据库的操作,一般都需要自己再去进行数据建模并进行封装使用。 

相关读写步骤也很简单: 

  1. - (IBAction)onBtnSqliteRead:(id)sender { 
  2.     sqlite3*sqlite = NULL;  
  3.       
  4.     NSString*strSQLiteFilePath = [[NSHomeDirectory()stringByAppendingPathComponent:@"Documents"]stringByAppendingPathComponent:@"sqlite.sqlite"];  
  5.       
  6.     BOOLbIsDir = FALSE;  
  7.     if(![[NSFileManager defaultManager]fileExistsAtPath:strSQLiteFilePath isDirectory:&bIsDir]){  
  8.         NSLog(@"SqliteOpen Error....File NOtexist...");  
  9.         return;  
  10.      
  11.       
  12.     if(SQLITE_OK != sqlite3_open([strSQLiteFilePath UTF8String],&sqlite))  
  13.      
  14.         NSLog(@"sqliteopen error...");  
  15.         return;  
  16.      
  17.       
  18.     NSString*strSQL = @"select * fromTESTCOREDATA";  
  19.       
  20.     sqlite3_stmt*stmt;  
  21.     //将对应的操作信息跟stmt进行bind,如果有相关条件可以在prepare之后进行调整  
  22.     sqlite3_prepare_v2(sqlite,[strSQL UTF8String], -1, &stmt,NULL);  
  23.     //获取执行SQL的返回结果  
  24.     while(SQLITE_ROW == sqlite3_step(stmt)){  
  25.         intnIntType = sqlite3_column_int(stmt,0);  
  26.         floatfloatType = sqlite3_column_double(stmt,1);  
  27.         doubledoubleType = sqlite3_column_double(stmt,2);  
  28.         constunsigned char* strTest = sqlite3_column_text(stmt,3);  
  29.           
  30.         break;  
  31.      
  32.       
  33.       
  34.       
  35.     sqlite3_close(sqlite);  
  36. }  

【FMDB】 

    fmdb是一个开源的库(https://github.com/ccgus/fmdb),主要的操作就是针对sqlite进行封装并提供了很多针对线程、多个sqlite实例的管理等相关操作。避免了sqlite操作过程中多行的各种bind,getcolumn等操作。 

要使用fmdb,将获取到的包中src下的文件(除了fmdb.m)拖到自己工程中(最好用一个group管理起来),然后添加libsqlite3.0.dylib即可。 

照例我们先来看下fmdb的基础用法: 

  1. NSString* strSQLiteFilePath = [[NSHomeDirectory()stringByAppendingPathComponent:@"Documents"]stringByAppendingPathComponent:@"fmdb.sqlite"]; 
  2.       
  3.     BOOLbIsDir = FALSE;  
  4.     if([[NSFileManager defaultManager] fileExistsAtPath:strSQLiteFilePathisDirectory:&bIsDir]){  
  5.         [[NSFileManagerdefaultManager] removeItemAtPath:strSQLiteFilePatherror:nil];  
  6.      
  7.       
  8.     FMDatabase*db = [FMDatabasedatabaseWithPath:strSQLiteFilePath];  
  9.       
  10.     if(![db open]) {  
  11.         NSLog(@"dbOpen Error...");  
  12.      
  13.       
  14.     NSString*strCreateTable = @"CREATE TABLE TESTCOREDATA(intType INTEGER,floatType FLOAT, doubleType DOUBLE, stringTypeVARCHAR(256))";  
  15.       
  16.     [dbexecuteUpdate:strCreateTable];  
  17.       
  18.     NSLog(@"begin");  
  19.     [dbbeginTransaction];  
  20.       
  21.     NSArray*arrayTest = [selfarrayWithData:100000];  
  22.       
  23.     for(NSValue* value inarrayTest)  
  24.      
  25.         Data*data = [valuepointerValue];  
  26.           
  27.         NSString*strSQLInsert = [NSString stringWithFormat:@"INSERT INTOTESTCOREDATA(intType, floatType, doubleType, stringType) values(%d,%f, %lf, '%s')", data->intType, data->floatType,data->doubleType,data->testString];  
  28.         [dbexecuteUpdate:strSQLInsert];  
  29.         free(data);  
  30.      
  31.       
  32.     [dbcommit];  
  33.     NSLog(@"end...");  
  34.       
  35.     [dbclose];  
  36.     db= nil;

fmdb使用比直接调用sqlite要省略很多代码和相关参数(估计让很多人很头疼),整个使用过程感觉就是四个字:酣畅淋漓。只将必要的一些关键操作需要给出参数。符合大部分对性能要求不高的场合,非常方便。fmdb针对读取操作也一样方便,这里就不多讲。接下来我们看看fmdb中其他几个文件的用途,有几个比较有趣的东西可以细说。 

在我们拿到的src中,还有部分文件我们在基本的使用场景中很少用到的: 

FMDatabaseAdditions.*文件,我们可以打开h文件看下: 

 

  1.   
  2.   
  3. - (int)intForQuery:(NSString*)query,...;  
  4.   
  5.   
  6.   
  7. - (long)longForQuery:(NSString*)query, ...;

  很直观是不是,就是针对某些你确定只有一个返回值的select语句,就不需要再通过返回FMResultSet并遍历set获取了。提供简单、直观易懂的操作。 

 FMDatabaseQueue.*提供多线程下针对db操作的一个队列。使用FMDatabaseQueue,我们在任何线程中,都可以操作: 


  1. [queue inDatabase:^(FMDatabase *db){  
  2.         //操作db  
  3.     }];  
  4. 而由queue自己去保证执行的先后顺序和唯一性,避免同时操作时产生冲突等。
  5. FMDatabasePool*则提供了一个db池,每一个db实力都会放在FMDatabasePool的池中,使用完成后归还db即可,sqlite相关的生命周期都由db池进行管理。避免经常性的open和close操作。
  6. 具体的相关细节可以参考fmdb的源码。
  7. 【sqlitepersistentobject】
  8.     sqlitepersistentobject库是基于ORM模型编写,将没一条数据都封装成对应的一个对象,而且完全屏蔽相关表名、文件名等信息,采用sqlitepersistentobject时,将所有的信息都屏蔽在实现细节后面。操作的时候只要操作每一个对象即可。
  9. sqlitepersistentobject相关下载路径:https://code.google.com/p/sqlitepersistentobjects/
  10. 同样,要使用sqlitepersistentobject,首先将库下载下来后,将src中相关文件拉入到自己的工程。并添加libsqlite3.0.dylib。因为sqlitepersistentobject底层也是用的sqlite进行的操作。
  11. 先来看下基础的数据插入部分:
  12. [code]#import   
  13. #import"SQLitePersistentObject.h"  
  14.   
  15. @interface ZJSqlitePersistentobjectsPerson :SQLitePersistentObject  
  16.  
  17.     intintType;  
  18.     floatfloatType;  
  19.     doubledoubleType;  
  20.     NSString*stringType;  
  21.  
  22.   
  23. @property(assign, nonatomic) intintType;  
  24. @property(assign, nonatomic) floatfloatType;  
  25. @property(assign, nonatomic) doubledoubleType;  
  26. @property(copy, nonatomic) NSString*stringType;  
  27. @end 

首先我们有一个数据类,继承自SQLitePersistentObject,每一个ZJSqlitePersistentobjectPerson对象都对应数据库中的一条记录。 

接下来,我们添加一批ZJSqlitePersistentobjectsPerson数据: 

 

  1. NSArray* arrayTest = [selfarrayWithData:1000];  
  2.       
  3.     NSLog(@"begin...");  
  4.       
  5.     for(NSValue* value inarrayTest)  
  6.      
  7.         Data*data = [valuepointerValue];  
  8.           
  9.         ZJSqlitePersistentobjectsPerson*person = [[ZJSqlitePersistentobjectsPerson alloc]init];  
  10.         person.intType= data->intType;  
  11.         person.floatType= data->floatType;  
  12.         person.doubleType= data->doubleType;  
  13.         person.stringType= [NSStringstringWithUTF8String:data->testString];  
  14.           
  15.         [personsave];  
  16.         [personrelease];  
  17.         free(data);  
  18.      
  19.       
  20.     //[ZJSqlitePersistentobjectsPersonclearCache];  
  21.       
  22.     [ZJSqlitePersistentobjectsPersonclearCache];  
  23.       
  24.     NSLog(@"end...");  

可以从插入过程中看到,整个操作过程中完全屏蔽了相关sql语句、表结构等细节。非常方便简单,不过sqlitepesistentobject只支持基础数据类型和实现了NSCoding等相关类,由此,对于相关集合类型(NSArray,NSSet,NSDictionary)等是不支持的。关于sqlitepersistentobject的其他细节,可以参考sqlitepersistentobject源码。 

 

 

【总结】 

综合比较coredata、sqlite、fmdb和sqlitepersistentobject等四种数据永久化方式,个人认为: 

1、如果只是基础数据类型并且对sql不熟悉,用sqlitepersistenttobject是最理想的,我们需要的是数据,sqlitepersistentobject需要你面对的也是一条一条的数据。 

2、如果需要操作一般的比较复杂的数据库以及类型,个人推荐用fmdb,非常方便和便于操作,而且sqlite本身还支持对数据加密的借口。 

3、如果需要很好的性能,可以使用sqlite并自己封装相关接口。 

4、如果需要基本的数据模型,并且对xcode可视化情有独钟的话,可以使用coredata->datamodel等。 

0 0