【数据存储】coredata、sqlite、fmdb和sqlitepersistentobject

管理提醒: 本帖被 angellixf 从 OS X开发讨论区 移动到本区(2014-02-12)

  • 分享类型:应用开发相关

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

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

sqlite          1470ms
  fmdb           3088ms
  coredata    3418ms

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

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

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

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

复制代码

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

插入数据过程中,首先获取一个NSManagedObject的对象,可以这么理解,NSManagedObject就代表了一条数据信息,我们用insertNewObjectForEntityForName往TestCoreData这个数据模型中添加了一条数据:object。后面的[object setValue:"***" forkey:"***"]即为该条插入的数据进行赋值。
添加完毕数据后,内存中模型已经有添加的数据了。但是文件中还没有同步进去,那么我们调用:
[coreDataContext save:nil];

即可将刚才插入的数据保存到文件中。
关于其他修改、删除等操作,以及core data中的releationship操作等,可以参考附件程序或查阅相关其他资料。
【sqlite】

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

复制代码

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

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

复制代码

  1. - (IBAction)onBtnSqliteRead:(id)sender {
  2. sqlite3* sqlite = NULL;
  3. NSString* strSQLiteFilePath = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"sqlite.sqlite"];
  4. BOOL bIsDir = FALSE;
  5. if (![[NSFileManager defaultManager] fileExistsAtPath:strSQLiteFilePath isDirectory:&bIsDir]) {
  6. NSLog(@"Sqlite Open Error....File NOt exist...");
  7. return;
  8. }
  9. if (SQLITE_OK != sqlite3_open([strSQLiteFilePath UTF8String], &sqlite))
  10. {
  11. NSLog(@"sqlite open error...");
  12. return;
  13. }
  14. NSString* strSQL = @"select * from TESTCOREDATA";
  15. sqlite3_stmt* stmt;
  16. // 将对应的操作信息跟stmt进行bind,如果有相关条件可以在prepare之后进行调整
  17. sqlite3_prepare_v2(sqlite, [strSQL UTF8String], -1, &stmt, NULL);
  18. // 获取执行SQL的返回结果
  19. while (SQLITE_ROW == sqlite3_step(stmt)) {
  20. int nIntType = sqlite3_column_int(stmt, 0);
  21. float floatType = sqlite3_column_double(stmt, 1);
  22. double doubleType = sqlite3_column_double(stmt, 2);
  23. const unsigned char* strTest = sqlite3_column_text(stmt, 3);
  24. break;
  25. }
  26. sqlite3_close(sqlite);
  27. }

【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. BOOL bIsDir = FALSE;
  3. if ([[NSFileManager defaultManager] fileExistsAtPath:strSQLiteFilePath isDirectory:&bIsDir]) {
  4. [[NSFileManager defaultManager] removeItemAtPath:strSQLiteFilePath error:nil];
  5. }
  6. FMDatabase* db = [FMDatabase databaseWithPath:strSQLiteFilePath];
  7. if (![db open]) {
  8. NSLog(@"db Open Error...");
  9. }
  10. NSString* strCreateTable = @"CREATE TABLE TESTCOREDATA(intType INTEGER, floatType FLOAT, doubleType DOUBLE, stringType VARCHAR(256))";
  11. [db executeUpdate:strCreateTable];
  12. NSLog(@"begin ");
  13. [db beginTransaction];
  14. NSArray* arrayTest = [self arrayWithData:100000];
  15. for (NSValue* value in arrayTest)
  16. {
  17. Data* data = [value pointerValue];
  18. NSString* strSQLInsert = [NSString stringWithFormat:@"INSERT INTO TESTCOREDATA(intType, floatType, doubleType, stringType) values(%d, %f, %lf, '%s')", data->intType, data->floatType, data->doubleType, data->testString];
  19. [db executeUpdate:strSQLInsert];
  20. free(data);
  21. }
  22. [db commit];
  23. NSLog(@"end...");
  24. [db close];
  25. db = nil;

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

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

复制代码

  1. /** Return `int` value for query
  2. @param query The SQL query to be performed.
  3. @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query.
  4. @return `int` value.
  5. */
  6. - (int)intForQuery:(NSString*)query, ...;
  7. /** Return `long` value for query
  8. @param query The SQL query to be performed.
  9. @param ... A list of parameters that will be bound to the `?` placeholders in the SQL query.
  10. @return `long` value.
  11. */
  12. - (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 <Foundation/Foundation.h>
  13. #import "SQLitePersistentObject.h"
  14. @interface ZJSqlitePersistentobjectsPerson : SQLitePersistentObject
  15. {
  16. int intType;
  17. float floatType;
  18. double doubleType;
  19. NSString* stringType;
  20. }
  21. @property(assign, nonatomic) int intType;
  22. @property(assign, nonatomic) float floatType;
  23. @property(assign, nonatomic) double doubleType;
  24. @property(copy, nonatomic) NSString* stringType;
  25. @end

首先我们有一个数据类,继承自SQLitePersistentObject,每一个ZJSqlitePersistentobjectPerson对象都对应数据库中的一条记录。
接下来,我们添加一批ZJSqlitePersistentobjectsPerson数据:

复制代码

  1. NSArray* arrayTest = [self arrayWithData:1000];
  2. NSLog(@"begin...");
  3. for (NSValue* value in arrayTest)
  4. {
  5. Data* data = [value pointerValue];
  6. ZJSqlitePersistentobjectsPerson* person = [[ZJSqlitePersistentobjectsPerson alloc] init];
  7. person.intType = data->intType;
  8. person.floatType = data->floatType;
  9. person.doubleType = data->doubleType;
  10. person.stringType = [NSString stringWithUTF8String:data->testString];
  11. [person save];
  12. [person release];
  13. free(data);
  14. }
  15. // [ZJSqlitePersistentobjectsPerson clearCache];
  16. [ZJSqlitePersistentobjectsPerson clearCache];
  17. 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->data model等。

由于对四种数据存储类型只是浅尝辄止,其中不免疏漏的地方。

 

数据持久化 技术比较相关推荐

  1. Android数据持久化技术

    Android 数据持久化技术 数据持久化 文件存储 将数据存储到文件中 从文件中读取数据 SharedPreferences存储 使用SharedPreferences存储数据 从SharedPre ...

  2. Android之数据持久化技术

    一.数据持久化技术概述 1.数据持久化:指将那些内存中的瞬时数据保存到存储设备当中,保证即使在手机或电脑关机的情况下,这些数据仍然不会丢失. 2.持久化技术提供了一种机制可以让数据在瞬时状态和持久状态 ...

  3. 第二行代码学习笔记——第六章:数据储存全方案——详解持久化技术

    本章要点 任何一个应用程序,总是不停的和数据打交道. 瞬时数据:指储存在内存当中,有可能因为程序关闭或其他原因导致内存被回收而丢失的数据. 数据持久化技术,为了解决关键性数据的丢失. 6.1 持久化技 ...

  4. Android数据存储全方案——持久化技术

    数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不会丢失.保存在内存中的数据是处于瞬时状态的,而保存在存储设备 中的数据是处于持久状态的,持久化技 ...

  5. 第一行代码学习笔记第六章——详解持久化技术

    知识点目录 6.1 持久化技术简介 6.2 文件存储 * 6.2.1 将数据存储到文件中 * 6.2.2 从文件中读取数据 6.3 SharedPreferences存储 * 6.3.1 将数据存储到 ...

  6. 第006天:APP的数据存储技术

    任何一个应用程序,其实说白了就是在不停地和数据打交道,我们聊QQ.看新闻.刷微博, 所关心的都是里面的数据,没有数据的应用程序就变成了一个空壳子,对用户来说没有任何实际 用途.那么这些数据都是从哪来的 ...

  7. Java 数据持久化系列之池化技术

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 在上一篇文章Java 数据持久化系列之JDBC中,我们了解到使用 ...

  8. 第6章 数据存储全方案,详解持久化技术

    所有的App都可以说是与数据打交道的,离开数据它们什么都不是.那么平时我们怎么存储一些关键的数据呢? 1 持久化技术简介 数据持久化就是指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关 ...

  9. 后端技术:数据持久化框架为什么放弃 Hibernate、JPA、Mybatis,最终选择 JDBCTemplate!...

    因为项目需要选择数据持久化框架,看了一下主要几个流行的和不流行的框架,对于复杂业务系统,最终的结论是,JOOQ是总体上最好的,可惜不是完全免费,最终选择JDBC Template. Hibernate ...

最新文章

  1. jquery textSlider 文字滚动
  2. Linux 常用检测命令
  3. mysql 排名_微服务架构下,如何利用Mysql的limit配合orderby进行排名统计
  4. matlab dsp实验报告,matlab实验报告14.pdf
  5. WebClient 上传文件
  6. mysql的unique_MySQL使用UNIQUE实现数据不重复插入
  7. 长文剖析经典论文,揭晓 Facebook 广告排序模型!
  8. 读取文件时,程序经历了什么?
  9. 【SimpleITK】分割label覆盖到原图上显示
  10. unix linux性能对比,各有所长!对比LINUX和UNIX系统优势
  11. 几个Gerrit代码检查checkstyle问题总结
  12. 你真的知道什么是元音什么是辅音吗?
  13. 寒假的一点笔记《123速通》
  14. 初中数学抽象教学的案例_初中数学教学案例及反思
  15. android 主流框架与技术
  16. js前端计算两个日期的间隔时间(时间差)
  17. Merriam-Webster's Vocabulary Builder 学习笔记 Unit 19
  18. 北京丁丁租房招聘JAVA开发人员
  19. R语言学习笔记及总结二
  20. php购物网站毕设ppt,购物网站设计与实现毕业设计展示PPT.ppt

热门文章

  1. 【052】测试数据引发的骚乱
  2. 计算机网络共享是什么意思,网络共享与便携式热点是什么意思
  3. mysql 开启远程
  4. sqlite fts5 编译 xcode other c flags
  5. iphone全部机型_iPhone12卖爆 产业链喜迎5G时代 股价天花板打开?|iphone|iphone12|运营商|智能机...
  6. XamarinSQLite教程添加索引
  7. Nessus漏洞扫描教程之使用Nmap工具扫描识别指纹
  8. python的concat函数_python concat函数
  9. 文科生自学python要多久_怎么自学python,大概要多久?
  10. python list 和矩阵的切片