iOS中Sqlite和FMDB使用詳解
正文
本文重點(diǎn)說(shuō)下常用的本地?cái)?shù)據(jù)庫(kù)操作,Sqlite和封裝的FMDB的使用,以及Model的存與取。
效果圖
什么是數(shù)據(jù)庫(kù)
- 數(shù)據(jù)庫(kù)(Database)是按照數(shù)據(jù)結(jié)構(gòu)來(lái)組織、存儲(chǔ)和管理數(shù)據(jù)的倉(cāng)庫(kù)
- 數(shù)據(jù)庫(kù)可以分為2大種類 :關(guān)系型數(shù)據(jù)庫(kù)(主流)、對(duì)象型數(shù)據(jù)庫(kù)
iOS中的數(shù)據(jù)存儲(chǔ)方式
- Plist(NSArray\NSDictionary),只能存儲(chǔ)數(shù)組,字典,但是數(shù)組和字典里面不能有自定義對(duì)象
- Preference(偏好設(shè)置\NSUserDefaults)
- NSCoding(NSKeyedArchiver\NSkeyedUnarchiver)
- SQLite3
- Core Data (面對(duì)對(duì)象)
什么是SQLite
- SQLite是一款輕型的嵌入式數(shù)據(jù)庫(kù)
- 它占用資源非常的低,在嵌入式設(shè)備中,可能只需要幾百K的內(nèi)存就夠了
- 它的處理速度比Mysql、PostgreSQL這兩款著名的數(shù)據(jù)庫(kù)都還快
調(diào)試軟件使用的是Navicat,支持大部分主流數(shù)據(jù)庫(kù)(包括SQLite)
SQL語(yǔ)句的種類
數(shù)據(jù)定義語(yǔ)句(DDL:Data Definition Language)
- 包括create和drop等操作
- 在數(shù)據(jù)庫(kù)中創(chuàng)建新表或刪除表(create table或 drop table)
數(shù)據(jù)操作語(yǔ)句(DML:Data Manipulation Language)
- 包括insert(添加)、update(修改)、delete(刪除)等操作
數(shù)據(jù)查詢語(yǔ)句(DQL:Data Query Language)
- 可以用于查詢獲得表中的數(shù)據(jù)
- 關(guān)鍵字select是DQL(也是所有SQL)用得最多的操作
- 其他DQL常用的關(guān)鍵字有where,order by,group by和having
字段類型
SQLite將數(shù)據(jù)劃分為以下幾種存儲(chǔ)類型:
- integer : 整型值
- real : 浮點(diǎn)值
- text : 文本字符串
- blob : 二進(jìn)制數(shù)據(jù)(比如文件,模型) 實(shí)際上SQLite是無(wú)類型的,就算聲明為integer類型,還是能存儲(chǔ)字符串文本(主鍵除外),建表時(shí)聲明啥類型或者不聲明類型都可以,也就意味著創(chuàng)表語(yǔ)句可以這么寫:
create table t_student(name, age);
為了保持良好的編程規(guī)范、方便程序員之間的交流,編寫建表語(yǔ)句的時(shí)候最好加上每個(gè)字段的具體類型
Sqlite使用:
一、創(chuàng)建表
- create table 表名 (字段名1 字段類型1, 字段名2 字段類型2, …) ;
- create table if not exists 表名 (字段名1 字段類型1, 字段名2 字段類型2, …)(判斷表是否已存在,不存在則創(chuàng)建) ;
示例:create table t_student (id integer, name text, age inetger, score real) ;
良好的數(shù)據(jù)庫(kù)編程規(guī)范應(yīng)該要保證每條記錄的唯一性,為此,增加了主鍵約束,也就是說(shuō),每張表都必須有一個(gè)主鍵,用來(lái)標(biāo)識(shí)記錄的唯一性,在創(chuàng)表的時(shí)候用primary key聲明一個(gè)主鍵:
示例:create table t_student (id integer primary key, name text, age integer) ;
主鍵的設(shè)計(jì)原則:
- 主鍵應(yīng)當(dāng)是對(duì)用戶沒(méi)有意義的
- 永遠(yuǎn)也不要更新主鍵
- 主鍵不應(yīng)包含動(dòng)態(tài)變化的數(shù)據(jù)
- 主鍵應(yīng)當(dāng)由計(jì)算機(jī)自動(dòng)生成
integer類型的id作為t_student表的主鍵。
- 只要聲明為primary key,就說(shuō)明是一個(gè)主鍵字段
- 主鍵字段默認(rèn)就包含了not null 和 unique 兩個(gè)約束
如果想要讓主鍵自動(dòng)增長(zhǎng)(必須是integer類型),應(yīng)該增加autoincrement,
示例:create table t_student (id integer primary key autoincrement, name text, age integer) ;
二、刪表
注意:這個(gè)刪是將整個(gè)表刪除
- drop table 表名 ;
- drop table if exists 表名 ;(判斷表是否存在,存在則刪除)
示例:drop table if exists t_student ;
三、增(插入數(shù)據(jù)insert)
- insert into 表名 (字段1, 字段2, …) values (字段1的值, 字段2的值, …) ;
示例:insert into t_student (name, age) values (‘小虎牙’, 10) ;
數(shù)據(jù)庫(kù)中的字符串內(nèi)容應(yīng)該用單引號(hào) ‘ ’ 括住
四、刪(刪除數(shù)據(jù)delete)
- delete from 表名 ;
示例:delete from t_student ;
注意:上面的示例會(huì)將t_student表中所有記錄都刪掉
五、改(更新數(shù)據(jù)update)
- update 表名 set 字段1 = 字段1的值, 字段2 = 字段2的值, … ;
示例:
update t_student set name = ‘rc', age = 18 ;
注意:上面的示例會(huì)將t_student表中所有記錄的name都改為rc,age都改為18
六、查(查詢數(shù)據(jù)select)
- select 字段1, 字段2, … from 表名 ; (查詢字段1, 字段2數(shù)據(jù))
- select * from 表名;(查詢表中所有的字段)
示例 : select name, age from t_student ;select * from t_student ;
七、條件語(yǔ)句
如果只想更新或者刪除某些固定的記錄,那就必須在DML語(yǔ)句后加上一些條件 條件語(yǔ)句的常見格式
- where 字段 = 某個(gè)值 ; (不能用兩個(gè) =)
- where 字段 is 某個(gè)值 ; (is 相當(dāng)于 =)
- where 字段 != 某個(gè)值 ;
- where 字段 is not 某個(gè)值 ; (is not 相當(dāng)于 !=)
- where 字段 > 某個(gè)值 ;
- where 字段1 = 某個(gè)值 and 字段2 > 某個(gè)值 ; (and相當(dāng)于C語(yǔ)言中的 &&)
- where 字段1 = 某個(gè)值 or 字段2 = 某個(gè)值 ; (or 相當(dāng)于C語(yǔ)言中的 ||)
示例:
- 將t_student表中年齡大于10 并且 姓名不等于rc的記錄,年齡都改為 5
update t_student set age = 5 where age > 10 and name != ‘rc’ ;
- 刪除t_student表中年齡小于等于10 或者 年齡大于30的記錄
delete from t_student where age <= 10 or age > 30 ;
- 將t_student表中名字等于rc的記錄,score字段的值 都改為 age字段的值
update t_student set score = age where name = ‘rc’ ;
八、起別名
格式:
- select 字段1 別名 , 字段2 別名 , … from 表名 別名 ;
- select 字段1 別名, 字段2 as 別名, … from 表名 as 別名 ;
- select 別名.字段1, 別名.字段2, … from 表名 別名 ;
示例:
- 給name起個(gè)叫做myname的別名,給age起個(gè)叫做myage的別名
select name myname, age myage from t_student ;
- 給t_student表起個(gè)別名叫做s,利用s來(lái)引用表中的字段
select s.name, s.age from t_student s ;
九、計(jì)算記錄的數(shù)量
- select count (字段) from 表名 ;
- select count ( * ) from 表名 ; 示例:
select count (age) from t_student ;
select count ( * ) from t_student where score >= 60;
十、排序
按照某個(gè)字段的值,進(jìn)行排序搜索 select * from t_student order by 字段 ; 示例:select * from t_student order by age ;
默認(rèn)是按照升序排序(由小到大),也可以變?yōu)榻敌颍ㄓ纱蟮叫。?降序 :select * from t_student order by age desc ;
升序(默認(rèn)):select * from t_student order by age asc ;
用多個(gè)字段進(jìn)行排序 先按照年齡排序(升序),年齡相等就按照身高排序(降序) 示例:select * from t_student order by age asc, height desc ;
十一、limit
使用limit可以精確地控制查詢結(jié)果的數(shù)量,比如每次只查詢10條數(shù)據(jù)
- select * from 表名 limit 數(shù)值1, 數(shù)值2 ; 跳過(guò)最前面4條語(yǔ)句,然后取8條記錄 示例:
select * from t_student limit 4, 8 ;
limit常用來(lái)做分頁(yè)查詢,比如每頁(yè)固定顯示5條數(shù)據(jù),那么應(yīng)該這樣取數(shù)據(jù)
第1頁(yè):limit 0, 5 第2頁(yè):limit 5, 5 第3頁(yè):limit 10, 5 ... 第n頁(yè):limit 5*(n-1), 5
十二、簡(jiǎn)單約束
建表時(shí)可以給特定的字段設(shè)置一些約束條件,常見的約束有
- not null :規(guī)定字段的值不能為null
- unique :規(guī)定字段的值必須唯一
- 指定字段的默認(rèn)值
建議:盡量給字段設(shè)定嚴(yán)格的約束,以保證數(shù)據(jù)的規(guī)范性
name字段不能為null,并且唯一 age字段不能為null,并且默認(rèn)為1
示例:create table t_student (id integer, name text not null unique, age integer not null default 1) ;
FMDB使用:
- FMDB是iOS平臺(tái)的SQLite數(shù)據(jù)庫(kù)框架
- FMDB以O(shè)C的方式封裝了SQLite的C語(yǔ)言API
優(yōu)點(diǎn):
- 使用起來(lái)更加面向?qū)ο?,省去了很多麻煩、冗余的C語(yǔ)言代碼
- 對(duì)比蘋果自帶的Core Data框架,更加輕量級(jí)和靈活
- 提供了多線程安全的數(shù)據(jù)庫(kù)操作方法,有效地防止數(shù)據(jù)混亂
FMDB有三個(gè)主要的類
- FMDatabase :其對(duì)象就代表一個(gè)單獨(dú)的SQLite數(shù)據(jù)庫(kù),用來(lái)執(zhí)行SQL語(yǔ)句
- FMResultSet :用來(lái)執(zhí)行查詢后的結(jié)果集
- FMDatabaseQueue :用于在多線程中執(zhí)行多個(gè)查詢或更新,它是線程安全的
打開數(shù)據(jù)庫(kù)
通過(guò)指定SQLite數(shù)據(jù)庫(kù)文件路徑來(lái)創(chuàng)建FMDatabase對(duì)象
FMDatabase *db = [FMDatabase databaseWithPath:path]; if (![db open]) { NSLog(@"數(shù)據(jù)庫(kù)打開失??!"); }
文件路徑有三種情況
- 具體文件路徑 :如果不存在會(huì)自動(dòng)創(chuàng)建
- 空字符串@"" :會(huì)在臨時(shí)目錄創(chuàng)建一個(gè)空的數(shù)據(jù)庫(kù),當(dāng)FMDatabase連接關(guān)閉時(shí),數(shù)據(jù)庫(kù)文件也被刪除
- nil :會(huì)創(chuàng)建一個(gè)內(nèi)存中臨時(shí)數(shù)據(jù)庫(kù),當(dāng)FMDatabase連接關(guān)閉時(shí),數(shù)據(jù)庫(kù)會(huì)被銷毀
更新數(shù)據(jù)庫(kù)
在FMDB中,除查詢以外的所有操作,都稱為“更新”,create、drop、insert、update、delete等 使用executeUpdate:方法執(zhí)行更新
- (BOOL)executeUpdate:(NSString*)sql, ... - (BOOL)executeUpdateWithFormat:(NSString*)format, ... - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments
示例 :[db executeUpdate:@"UPDATE t_student SET age = ? WHERE name = ?;", @18, @"rc"]
執(zhí)行查詢
- (FMResultSet *)executeQuery:(NSString*)sql, ... - (FMResultSet *)executeQueryWithFormat:(NSString*)format, ... - (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments // 查詢數(shù)據(jù) FMResultSet *rs = [db executeQuery:@"SELECT * FROM t_student"]; // 遍歷結(jié)果集 while ([rs next]) { NSString *name = [rs stringForColumn:@"name"]; int age = [rs intForColumn:@"age"]; double score = [rs doubleForColumn:@"score"]; }
FMDatabaseQueue
FMDatabase這個(gè)類是線程不安全的,如果在多個(gè)線程中同時(shí)使用一個(gè)FMDatabase實(shí)例,會(huì)造成數(shù)據(jù)混亂等問(wèn)題 為了保證線程安全,F(xiàn)MDB提供方便快捷的FMDatabaseQueue類
// FMDatabaseQueue的創(chuàng)建 FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:path]; ... [queue inDatabase:^(FMDatabase *db) { [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"rc"]; [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"]; [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"]; FMResultSet *rs = [db executeQuery:@"select * from t_student"]; while ([rs next]) { // … } }];
事務(wù)、回滾 : 操作數(shù)據(jù)庫(kù)時(shí),會(huì)出現(xiàn)這種情況:更新10條記錄,當(dāng)更新到第5條時(shí),服務(wù)器宕機(jī)了,后面的麻煩就來(lái)了,我們要每次判斷哪條記錄更新了,哪條記錄沒(méi)更新!這時(shí)候就用到了事務(wù),將更新10條記錄放到一個(gè)事務(wù)中,成功完成所有更新操作時(shí)再提交,只要其中一條記錄更新失敗就回滾,回到初始狀態(tài),簡(jiǎn)單的說(shuō)就是要么全部成功,要么全部不成功!
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) { [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"rc"]; [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"]; [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"]; FMResultSet *rs = [db executeQuery:@"select * from t_student"]; while ([rs next]) { // … } }]; //事務(wù)回滾 *rollback = YES;
對(duì)模型進(jìn)行存取
以頭條新聞為例,一條新聞為一條記錄:
1.先在本地?cái)?shù)據(jù)庫(kù)查找是否存在緩存,存在則顯示緩存的新聞,
2.不存在則去頭條服務(wù)器請(qǐng)求數(shù)據(jù)顯示,同時(shí)緩存
思路很簡(jiǎn)單: 要做到請(qǐng)求了今日頭條的新聞緩存到本地?cái)?shù)據(jù)庫(kù)后,下次打開直接在數(shù)據(jù)庫(kù)取出數(shù)據(jù)后顯示。
兩種情況:
1、直接緩存后天返回的json數(shù)據(jù) ,好處是簡(jiǎn)單,避免了模型轉(zhuǎn)字典的過(guò)程,數(shù)據(jù)原始,但是如果對(duì)數(shù)據(jù)有更新,比如:新聞是否已讀等。
2、將服務(wù)器返回的json轉(zhuǎn)成model(模型)再緩存,缺點(diǎn)就是可能在模型中有為了方便開發(fā)而新增的字段,而這些字段是不需要進(jìn)行緩存的。
第1種比較簡(jiǎn)單,就以第2種為例: 寫了一個(gè)新聞緩存的工具類
NewsCacheTool.h /**緩存新聞數(shù)據(jù)到本地?cái)?shù)據(jù)庫(kù)*/ + (void)saveNewsToDatabase:(NSArray *)newsArray; /**讀取新聞(userID對(duì)應(yīng)用戶的id,同個(gè)應(yīng)用可能存在多個(gè)賬號(hào)登錄情況)*/ + (NSArray *)selectNewsToDatabase:(NSString *)userID; /**清除緩存*/ + (void)clearNewsCache:(void(^)(BOOL success))flag;
NewsCacheTool.m static FMDatabase *_db; // 第一次使用就開始創(chuàng)建表 + (void)initialize{ NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; NSString *filePath = [cachePath stringByAppendingPathComponent:@"News.sqlite"]; _db = [FMDatabase databaseWithPath:filePath]; if ([_db open]) { NSLog(@"打開數(shù)據(jù)庫(kù)成功"); // 自增主鍵、userID、二進(jìn)制數(shù)據(jù)流 NSString *sql = @"create table if not exists t_news (id integer primary key autoincrement,userID text,dict blob);"; BOOL success = [_db executeUpdate:sql]; if (success) { NSLog(@"創(chuàng)建表成功"); }else{ NSLog(@"創(chuàng)建表失敗"); } }else{ NSLog(@"打開數(shù)據(jù)庫(kù)失敗"); } } // 緩存數(shù)據(jù),這里為了安全起見應(yīng)該使用事務(wù) + (void)saveNewsToDatabase:(NSArray *)newsArray{ // 遍歷模型數(shù)組 for (NewsModel *nesw in newsArray){ // 用戶的id應(yīng)該從自己的服務(wù)器取得 NSString *userID = @"001"; // 這是模型轉(zhuǎn)字典的,自己用runtime簡(jiǎn)單實(shí)現(xiàn)了,有很多優(yōu)秀的第三方庫(kù)可以使用,自選 NSDictionary * newsDic = [nesw getDictionayFromModel]; NSError *error; NSData *data; if (@available(iOS 11.0, *)){ data = [NSKeyedArchiver archivedDataWithRootObject:newsDic requiringSecureCoding:YES error:&error]; }else{ data = [NSKeyedArchiver archivedDataWithRootObject:newsDic]; } if (data == nil || error) { NSLog(@"緩存失敗:%@", error); return; } BOOL success = [_db executeUpdate:@"insert into t_news (userID,dict) values(?,?)",userID,data]; if (success) { NSLog(@"插入成功"); }else{ NSLog(@"插入失敗"); } } } // 在數(shù)據(jù)庫(kù)中讀取數(shù)據(jù) + (NSArray *)selectNewsToDatabase:(NSString *)userID{ NSString *sql = [NSString stringWithFormat:@"select * from t_news where userID = '%@';",userID]; FMResultSet *set = [_db executeQuery:sql]; NSMutableArray *array = [NSMutableArray array]; while ([set next]) { NSData *data = [set dataForColumn:@"dict"]; NSError *error; NSDictionary *dic; if (@available(iOS 11.0, *)) { dic = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSObject class] fromData:data error:&error]; } else { dic = [NSKeyedUnarchiver unarchiveObjectWithData:data]; } if(dic){ NewsModel *news = [[NewsModel alloc]initWithDictionary:dic]; [array addObject:news]; } } return array; } // 清除新聞緩存 + (void)clearNewsCache:(void (^)(BOOL success))flag{ BOOL success = [_db executeUpdate:@"delete from t_news;"]; if(flag){ flag(success); } }
模型轉(zhuǎn)字典的過(guò)程中,在NewsModel類中,用runtime簡(jiǎn)單實(shí)現(xiàn)了,實(shí)際開發(fā)中可能會(huì)多層嵌套字典或數(shù)據(jù),市面上有很多成熟優(yōu)秀的輪子,可自行選擇。
以上就是iOS中Sqlite和FMDB使用詳解的詳細(xì)內(nèi)容,更多關(guān)于iOS Sqlite FMDB 使用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決移動(dòng)端 ios 系統(tǒng)鍵盤遮擋的問(wèn)題
下面小編就為大家分享一篇解決移動(dòng)端 ios 系統(tǒng)鍵盤遮擋的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12iOS 統(tǒng)計(jì)Xcode項(xiàng)目代碼行數(shù)的實(shí)例
下面小編就為大家分享一篇iOS 統(tǒng)計(jì)Xcode項(xiàng)目代碼行數(shù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01iOS應(yīng)用中使用AsyncSocket庫(kù)處理Socket通信的用法講解
這篇文章主要介紹了iOS應(yīng)用中使用AsyncSocket庫(kù)處理Socket通信的用法講解,AsyncSocket同時(shí)支持TCP和UDP,文中展示了其建立斷開連接及發(fā)送接收消息的操作,very好用,需要的朋友可以參考下2016-05-05IOS設(shè)計(jì)模式之組合設(shè)計(jì)模式
組合模式,Composite Pattern,是一個(gè)非常巧妙的模式。幾乎所有的面向?qū)ο笙到y(tǒng)都應(yīng)用到了組合模式,接下來(lái)通過(guò)本文給大家介紹IOS設(shè)計(jì)模式之組合設(shè)計(jì)模式,需要的朋友參考下2016-02-02iOS利用CoreImage實(shí)現(xiàn)人臉識(shí)別詳解
OS的人臉識(shí)別從iOS 5(2011)就有了,不過(guò)一直沒(méi)怎么被關(guān)注過(guò)。人臉識(shí)別API允許開發(fā)者不僅可以檢測(cè)人臉,也可以檢測(cè)到面部的一些特殊屬性,比如說(shuō)微笑或眨眼。下面這篇文章主要給大家介紹了iOS利用CoreImage實(shí)現(xiàn)人臉識(shí)別的相關(guān)資料,需要的朋友可以參考下。2017-05-05IOS 波紋進(jìn)度(waveProgress)動(dòng)畫實(shí)現(xiàn)
這篇文章主要介紹了IOS 紋進(jìn)度(waveProgress)動(dòng)畫實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2016-09-09