摘要
本文介紹iOS中常用的應(yīng)用數(shù)據(jù)存儲(chǔ)方式及其詳細(xì)用法;本章介紹: SQLite3 和 Core Data.
iOS中的數(shù)據(jù)存儲(chǔ)(下)
SQLite3
- SQLite3是一款開源的嵌入式關(guān)系型數(shù)據(jù)庫,可移植性好,易使用,內(nèi)存開銷小.
- SQLite3是無類型的,意味著你可以保存任何類型的數(shù)據(jù)到任意表的任意字段中.
- SQLite3常用的4種數(shù)據(jù)類型:text(文本字符串), integer(整型值), real(浮點(diǎn)值), blob(二進(jìn)制數(shù)據(jù)(比如文件)).
在iOS中使用SQLite3,首先要添加庫文件'libsqlite3.dylib'
和導(dǎo)入主頭文件#import<sqlite3.h>
SQL語句的特點(diǎn):
1> 不區(qū)分大小寫;
2> 每條語句都必須以分號(hào)**;**結(jié)尾
SQL中常用的關(guān)鍵字:
pselect、insert曲饱、update胜臊、delete冠绢、from延届、create龄减、where、desc渣淳、order、by鳄橘、group暗挑、table贝奇、alter该镣、view、index等等
數(shù)據(jù)庫中不可以使用關(guān)鍵字來命名表,字段.
SQL語句種類:
1> 數(shù)據(jù)定義語句(DDL:Data Definition Language)
包括create和drop等操作 ;
在數(shù)據(jù)庫中創(chuàng)建新表或刪除表(create table或 drop table).
2> 數(shù)據(jù)操作語句(DML:Data Manipulation Language)
包括insert厂僧、update、delete等操作 ;
上面的3種操作分別用于添加、修改、刪除表中的數(shù)據(jù) .
3> 數(shù)據(jù)查詢語句(DQL:Data Query Language)
可以用于查詢獲得表中的數(shù)據(jù) ;
關(guān)鍵字select是DQL(也是所有SQL)用得最多的操作 ;
其他DQL常用的關(guān)鍵字有where呆瞻,order by前域,group by和having創(chuàng)建.
創(chuàng)表:
create table if not exists t_student (id integer, name text, age inetger, score real) ;
刪表:
``drop table if exists t_student;``
插入數(shù)據(jù)(insert):
``insert into t_student (name, age) values ('JN', 22) ;``
注意:數(shù)據(jù)庫中的字符串內(nèi)容應(yīng)該用單引號(hào)''括住.
更新數(shù)據(jù)(updata):
``pupdate t_student set name = 'jack', age = 20 ; ``
注意:上面的更新會(huì)將t_student表中所有記錄的name都改為jack,age都改為20;
刪除數(shù)據(jù)(delete):
``delete from t_student;``
會(huì)將t_student表中所有記錄都刪掉.
如果只想更新或者刪除某些固定的記錄,那就必須在DML語句后加上一些條件.示例如下:
// 將t_student表中年齡大于10 并且 姓名不等于jack的記錄,年齡都改為 5
update t_student set age = 5 where age > 10 and name != ‘jack’ ;
// 刪除t_student表中年齡小于等于10 或者 年齡大于30的記錄
delete from t_student where age <= 10 or age > 30 ;
查詢語句(DQL):
select * from t_student where age > 10 ; // 條件查詢條件語句:
主鍵約束:
每張表都必須有一個(gè)主鍵,用來標(biāo)識(shí)記錄的唯一性.
什么是主鍵:
主鍵(Primary Key,簡(jiǎn)稱PK),用來唯一的標(biāo)識(shí)某一條記錄.
例如t_student可以增加一個(gè)id字段作為主鍵,相當(dāng)于人的身份證.
主鍵可以是一個(gè)字段或多個(gè)字段.
外鍵約束:
利用外鍵約束可以來建立表與表之間的聯(lián)系.
外鍵的一般情況是:一張表的某個(gè)字段引用著另一張表的主鍵字段.
打開,關(guān)閉數(shù)據(jù)庫
創(chuàng)建或打開數(shù)據(jù)庫:
// path為:~/Documents/person.db
sqlite3 *db;int result = sqlite3_open([path UTF8String], &db);
代碼解析:
sqlite3_open()將根據(jù)文件路徑打開數(shù)據(jù)庫,如果不存在,則會(huì)創(chuàng)建一個(gè)新的數(shù)據(jù)庫.如果result等于常量SQLITE_OK,則表示成功打開數(shù)據(jù)庫.
sqlite *db:一個(gè)打開的數(shù)據(jù)庫實(shí)例.
數(shù)據(jù)庫文件的路徑必須以C字符串(而非NSString)傳入.
關(guān)閉數(shù)據(jù)庫:sqlite3_close(db);
執(zhí)行不返回語句的SQL語句
char *errorMsg; // 用來存儲(chǔ)錯(cuò)誤信息
char *sql = "create table if not exists t_person(id integer primary key autoincrement, name text, age integer);";
int result = sqlite3_exec(db, sql, NULL, NULL, &errorMsg);
代碼解析:
sqlite3_exec()可以執(zhí)行任何SQL語句,比如創(chuàng)表, 更新, 插入和刪除操作.但是一般不用它執(zhí)行查詢語句,因?yàn)樗粫?huì)返回查詢到得數(shù)據(jù).
sqlite3_exec()還可以執(zhí)行的語句:
1> 開啟事務(wù):begain transaction;
2> 回滾事務(wù):rollback
3> 提交事務(wù):commit
SQLite函數(shù)總結(jié):
1.打開數(shù)據(jù)庫
int sqlite3_open(
const char *filename, // 數(shù)據(jù)庫的文件路徑
sqlite3 **ppDb // 數(shù)據(jù)庫實(shí)例
);
2.執(zhí)行任何SQL語句
int sqlite3_exec(
sqlite3*, // 一個(gè)打開的數(shù)據(jù)庫實(shí)例
const char *sql, // 需要執(zhí)行的SQL語句
int (*callback)(void*,int,char**,char**), // SQL語句執(zhí)行完畢后的回調(diào)
void *, // 回調(diào)函數(shù)的第1個(gè)參數(shù)
char **errmsg // 錯(cuò)誤信息
);
3.檢查SQL語句的合法性(查詢前的準(zhǔn)備)
int sqlite3_prepare_v2(
sqlite3 *db, // 數(shù)據(jù)庫實(shí)例
const char *zSql, // 需要檢查的SQL語句
int nByte, // SQL語句的最大字節(jié)長度
sqlite3_stmt **ppStmt, // sqlite3_stmt實(shí)例给梅,用來獲得數(shù)據(jù)庫數(shù)據(jù)
const char **pzTail
);
4.查詢一行數(shù)據(jù)
int sqlite3_step(
sqlite3_stmt*); // 如果查詢到一行數(shù)據(jù),就會(huì)返回SQLITE_ROW
5.利用stmt獲得某一字段的值(字段的下標(biāo)從0開始)
double sqlite3_column_double(sqlite3_stmt*, int iCol); // 浮點(diǎn)數(shù)據(jù)
int sqlite3_column_int(sqlite3_stmt*, int iCol); // 整型數(shù)據(jù)
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); // 長整型數(shù)據(jù)
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); // 二進(jìn)制文本數(shù)據(jù)
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); // 字符串?dāng)?shù)據(jù)
CoreData
Core Data框架提供了對(duì)象-關(guān)系映射(ORM)的功能,即能夠?qū)C對(duì)象轉(zhuǎn)化成數(shù)據(jù),保存在SQLite3數(shù)據(jù)庫文件中,也能將保存在數(shù)據(jù)庫中的數(shù)據(jù)還原成OC對(duì)象.在次數(shù)據(jù)操作期間,不需要編寫任何SQL語句.
使用此功能,要添加CoreData.framework和導(dǎo)入主頭文件<CoreDate/CoreData.h>.
模型文件:在CoreData中,需要進(jìn)行映射的對(duì)象稱為實(shí)體(entity),而且需要使用CoreData的模型文件來描述應(yīng)用的所有實(shí)體和實(shí)體屬性.
NSManagedObject
通過Core Data從數(shù)據(jù)庫中取出的對(duì)象,默認(rèn)情況下都是NSManagedObject對(duì)象. NSManagedObject的工作模式有點(diǎn)類似于NSDictionary對(duì)象,通過鍵-值對(duì)來存取所有的實(shí)體屬性. setValue:forkey:存儲(chǔ)屬性值(屬性名為key); valueForKey:獲取屬性值(屬性名為key).
CoreData主要對(duì)象
NSManagedObjectContext:負(fù)責(zé)數(shù)據(jù)和應(yīng)用庫之間的交互(CRUD); NSPersistentStoreCoordinator:添加持久化存儲(chǔ)庫(比如SQLite數(shù)據(jù)庫); NSManagedObjectModel:代表Core Data的模型文件; NSEntityDescription:用來描述實(shí)體;
搭建CoreData上下文環(huán)境:
// 從應(yīng)用程序包中加載模型文件
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
// 傳入模型双揪,初始化
NSPersistentStoreCoordinator NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
// 構(gòu)建SQLite文件路徑
NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];NSURL *url = [NSURL fileURLWithPath:[docs stringByAppendingPathComponent:@"person.data"]];
// 添加持久化存儲(chǔ)庫动羽,這里使用SQLite作為存儲(chǔ)庫
NSError *error = nil;
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
if (store == nil) { // 直接拋異常
[NSException raise:@"添加數(shù)據(jù)庫錯(cuò)誤" format:@"%@", [error localizedDescription]];
}
// 初始化上下文,設(shè)置persistentStoreCoordinator屬性
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];context.persistentStoreCoordinator = psc;
// 用完之后渔期,還是要[context release];
/*持久化存儲(chǔ)庫的類型:NSSQLiteStoreType SQLite數(shù)據(jù)庫
NSBinaryStoreType 二進(jìn)制平面文件
NSInMemoryStoreType 內(nèi)存庫运吓,無法永久保存數(shù)據(jù)
雖然這3種類型的性能從速度上來說都差不多,但從數(shù)據(jù)模型中保留下來的信息卻不一樣
在幾乎所有的情景中疯趟,都應(yīng)該采用默認(rèn)設(shè)置拘哨,使用SQLite作為持久化存儲(chǔ)庫
*/
添加數(shù)據(jù):
// 傳入上下文,創(chuàng)建一個(gè)Person實(shí)體對(duì)象
NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
// 設(shè)置簡(jiǎn)單屬性
[person setValue:@"JN" forKey:@"name"];
[person setValue:[NSNumber numberWithInt:22] forKey:@"age"];
// 傳入上下文信峻,創(chuàng)建一個(gè)Card實(shí)體對(duì)象
NSManagedObject *card = [NSEntityDescription insertNewObjectForEntityForName:@"Card" inManagedObjectContext:context];
[card setValue:@"447640819" forKey:@"no"];
// 設(shè)置Person和Card之間的關(guān)聯(lián)關(guān)系
[person setValue:card forKey:@"card"];
// 利用上下文對(duì)象倦青,將數(shù)據(jù)同步到持久化存儲(chǔ)庫
NSError *error = nil;BOOL success = [context save:&error];
if (!success) {
[NSException raise:@"訪問數(shù)據(jù)庫錯(cuò)誤" format:@"%@", [error localizedDescription]];
}
// 如果是想做更新操作:只要在更改了實(shí)體對(duì)象的屬性后調(diào)用[context save:&error],就能將更改的數(shù)據(jù)同步到數(shù)據(jù)庫
查詢數(shù)據(jù):
// 初始化一個(gè)查詢請(qǐng)求
NSFetchRequest *request = [[NSFetchRequest alloc] init];
// 設(shè)置要查詢的實(shí)體
NSEntityDescription *desc = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
// 設(shè)置排序(按照age降序)
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];
request.sortDescriptors = [NSArray arrayWithObject:sort];
// 設(shè)置條件過濾(name like '%JN-1%')
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@", @"*JN-1*"];
request.predicate = predicate;
注意:設(shè)置條件過濾時(shí)盹舞,數(shù)據(jù)庫里面的%要用*來代替
// 執(zhí)行請(qǐng)求
NSError *error = nil;
NSArray *objs = [context executeFetchRequest:request error:&error];
if (error) {
[NSException raise:@"查詢錯(cuò)誤" format:@"%@", [error localizedDescription]];
}
// 遍歷數(shù)據(jù)
for (NSManagedObject *obj in objs) {
NSLog(@"name=%@", [obj valueForKey:@"name"]
}
刪除數(shù)據(jù):
// 傳入需要?jiǎng)h除的實(shí)體對(duì)象
[context deleteObject:managedObject];
// 將結(jié)果同步到數(shù)據(jù)庫NSError *error = nil;
[context save:&error];
if (error) {
[NSException raise:@"刪除錯(cuò)誤" format:@"%@",[error localizedDescription]];
}
Core Data的延遲加載:
Core Data不會(huì)根據(jù)實(shí)體中的關(guān)聯(lián)關(guān)系立即獲取相應(yīng)的關(guān)聯(lián)對(duì)象;比如通過Core Data取出Person實(shí)體時(shí),并不會(huì)立即查詢相關(guān)聯(lián)的Card實(shí)體,當(dāng)應(yīng)用真的需要使用Card時(shí),才會(huì)查詢數(shù)據(jù)庫,加載Card實(shí)體信息.
創(chuàng)建NSManagedObject的子類:
默認(rèn)情況下,利用Core Data取出的實(shí)體都是NSManagedObject類型的,能夠利用鍵-值對(duì)來存取數(shù)據(jù).
但是一般情況下,實(shí)體在存取數(shù)據(jù)的基礎(chǔ)上,有時(shí)還需要添加一些業(yè)務(wù)方法來完成一些其他任務(wù),那么就必須創(chuàng)建NSManagedObject的子類.
// 那么生成一個(gè)Person實(shí)體對(duì)象就應(yīng)該這樣寫
Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
person.name = @"JN";person.age = [NSNumber numberWithInt:24];
Card *card = [NSEntityDescription insertNewObjectForEntityForName:@”Card" inManagedObjectContext:context];
card.no = @”447640819";
person.card = card;