摘要:關鍵點:建立、插入、查詢、數據格式化
第三方框架FMDB
------------------------------------------------------------------------------------------------------------
開源
1)什麼是FMDB
FMDB是iOS平臺的SQLite數據庫框架,FMDB以OC的方式封裝了SQLite的C語言API。
2)FMDB的優勢
a、使用起來更加面向對象,省去了不少麻煩、冗餘的C語言代碼
b、對比蘋果自帶的Core Data框架,更加輕量級和靈活
c、提供了多線程安全的數據庫操做方法,有效地防止數據混亂
---------------------------------------------------------------------------------
最主要的類
FMDatabase:對象表明數據庫,主要功能執行SQL語句
FMResultSet:對象表明查詢結果,主要用於保存和處理select語句的查詢結果,主要用於保存和處理SELECT語句的查詢結果
注意:
一、FMDB將全部建表、插入、更新、刪除的操做都叫Update,執行這些SQL語句的方法都是
executeUpdate、executeUpdateWithFormat:
二、查詢:
只有執行SELECT語句的方法叫executeQuery:
給SQL語句傳參數:
? :都是對象,表示爲其餘參數,NSString,NSArray, NSDictionary,或va_list等
Format:支持各類基本數據類型
處理查詢結果FMResultSet:用於存儲executeQuery查詢出來的結果
數據集 next:一次取一次記錄
每條記錄獨有幾個字段,取字段XXXForColumn,XXX表明字段的類型
FMResultSet 提供了不少方法來得到所需的格式的值:
intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumnIndex:
objectForColumn:
這些方法也都包括 {type}ForColumnIndex 的這樣子的方法,參數是查詢結果集的列的索引位置。
你無需調用 [FMResultSet close]來關閉結果集, 當新的結果集產生,或者其數據庫關閉時,會自動關閉。
數據格式化
支持的格式:
自動識別對象:?
字符串:%@, %c, %s
數值:%d, %D,%i,%u,%U,%ld, %lu,%lld, %llu ,%f
%hi, %hu, %qi, %qu,
%g
你須要在SQL語句中使用 % 字符,你應該使用 %%
除此以外的修飾符可能致使沒法預知的結果
executeUpdate:?,SQLite風格
execute*WithFormat: 這是NSString風格的參數
使用FMDB,插入數據前,你不要花時間審查你的數據。你可使用標準的SQLite數據綁定語法。
INSERT INTO myTable VALUES (?, ?, ?)
SQLite會識別 「?」 爲一個輸入的點位符, 這樣的執行會接受一個可變參數(或者表示爲其餘參數,如NSArray, NSDictionary,或va_list等),會正確爲您轉義。
你也能夠選擇使用命名參數語法。
INSERT INTO myTable VALUES (:id, :name, :value)
參數名必須以冒名開頭。SQLite自己支持其餘字符,當Dictionary key的內部實現是冒號開頭。注意你的NSDictionary key不要包含冒號。
NSDictionary *argsDict = [NSDictionary dictionaryWithObjectsAndKeys:@"My Name", @"name", nil];
[db executeUpdate:@"INSERT INTO myTable (name) VALUES (:name)" withParameterDictionary:argsDict];
並且,代碼不能這麼寫(爲何?想一想吧。答:由於字典提供的參數值不夠)
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @"this has \" lots of ' bizarre \" quotes '"];
你應該:
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", @"this has " lots of ' bizarre " quotes '"];
提供給 -executeUpdate: 方法的參數都必須是對象
就像如下的代碼就沒法工做,且會產生崩潰。
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", 42];
正確有作法是把數字打包成 NSNumber對象
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:42]];
或者,你可使用 -execute*WithFormat: ,這是NSString風格的參數
[db executeUpdateWithFormat:@"INSERT INTO myTable VALUES (%d)", 42];
-execute*WithFormat: 的方法的內部實現會幫你封裝數據, 如下這些修飾符均可以使用: %@, %c, %s, %d, %D,%i, %u, %U, %hi, %hu, %qi, %qu, %f, %g, %ld, %lu, %lld, and %llu. 除此以外的修飾符可能致使沒法預知的結果。 一些狀況下,你須要在SQL語句中使用 % 字符,你應該使用 %%。
//————————————-------------------—create—建立表------------------------------------------
@property (strong, nonatomic)FMDatabase *fmdb;
NSString *dbFilePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)firstObject]stringByAppendingPathComponent:@"demo3.db"];
//此對象表明數據庫
self.fmdb = [FMDatabase databaseWithPath:dbFilePath];
//1. 打開數據庫
if([self.fmdb open]){
//2. 執行建表SQL
BOOL res = [self.fmdb executeUpdate:@"CREATE TABLE IF NOT EXISTS emp(id integer PRIMARY KEY, name varchar(20) NOT NULL, age integer, salary double)"];
if(res){
NSLog(@"建表成功");
}else{
NSLog(@"建表失敗:%@", [self.fmdb lastError]);
}
}
[self.fmdb close];
//—————————------------—————insert into-插入數據------------------------------------------
一、插入數據:executeUpdateWithFormat
[self.fmdb open];
BOOL res = [self.fmdb executeUpdateWithFormat:@"INSERT INTO emp (name, age, salary) VALUES (%@, %d, %f)", self.nameTF.text, self.ageTF.text.intValue, self.salaryTF.text.doubleValue];
if(!res){
NSLog(@"插入數據失敗:%@", [self.fmdb lastError]);
}
flage=[self.fmDb executeUpdateWithFormat:@"insert into myRecord(pID,timer) values(%d,%@)",self.Mypoem.poemID,currentTime];
二、用問號代替字段,拼接方式:executeUpdate
NSString * sql = @"insert into user (name, password) values(?, ?) ";
NSString * name = [NSString stringWithFormat:@"queue111 %d", i];
BOOL res = [db executeUpdate:sql, name, @"boy"];
if (!res) {
debugLog(@"error to add db data: %@", name);
} else {
debugLog(@"succ to add db data: %@", name);
}
//----------------------------delete-刪除數據------------------------------------------
刪除某條語句
BOOL flag = [self.fmdb executeUpdate:@"DELETE FROM test_tab WHERE name=?",@"jiajia"];
刪除表
BOOL flag = [self.fmdb executeUpdate:@"DELETE FROM test_tab];
//----------------------------update-修改數據------------------------------------------
修改數據:
格式:update 表名 SET 修改內容 where 條件
update foods SET id = 800 - id
//----------------------------select-查詢數據------------------------------------------
一、查詢指定數據:executeQueryWithFormat
[self.fmDb open];
NSLog(@"%d",_Mypoem.poemID);
FMResultSet *poemRSet=[self.fmDb executeQueryWithFormat:@"select D_SHI,D_INTROSHI from T_SHI where D_ID = %d",self.Mypoem.poemID];
while ([poemRSet next]) {
NSString *temp1=[poemRSet stringForColumn:D_SHI];
NSString *temp2=[poemRSet stringForColumn:D_INTROSHI];
self.textView.text=[temp1 stringByAppendingString:temp2];
NSLog(@"%@",[temp1 stringByAppendingString:temp2]);
}
[self.fmDb close];
二、查詢個數:(只會返回一個值):executeQuery
1)select count(*) allrecord from myRecord //allrecord:給字段的取別名
2)查詢指定的個數:executeQueryWithFormat
FMResultSet *resultSet=[self.fmDb executeQueryWithFormat:@"select count(pID) allCollection from myCollection where pID = %d",self.Mypoem.poemID];
NSLog(@"self.Mypoem.poemID = %d",self.Mypoem.poemID);
[resultSet next];
int count=[resultSet intForColumn:@"allCollection"];
NSLog(@"判斷是否已經存在了這條記錄:%d",count);
三、
sql = @"INSERT INTO USER (name,phone,idcode) VALUES (?,?,?) ";
BOOL res = [db executeUpdate:sql,nameTextField.text,phoneTextField.text,IDTextField.text];
if (!res) {
NSLog(@"error to insert data");
mes = @"數據插入錯誤";
}else{
NSLog(@"insert succeed");
mes = @"數據插入成功";
}
四、
FMResultSet *resultSet = [self.fmdb executeQuery:@"SELECT * FROM emp WHERE id > ?", @2];
while([resultSet next]){//取一條記錄
//取這條記錄中的每一個字段值
int ID = [resultSet intForColumn:@"id"];
NSString *name = [resultSet stringForColumn:@"name"];
int age = [resultSet intForColumn:@"age"];
double salary = [resultSet doubleForColumn:@"salary"];
self.displayLabel.text = [self.displayLabel.text stringByAppendingFormat:@"\n%d,%@,%d,%.2lf", ID, name, age, salary];
}
[self.fmdb close];
//—————-----------------———補充:多表關聯 ---------------------------------
//———————方式一:
1、支持多表鏈接,例如
select * from student,class where student.cid=class.id;
select table1.abc from table1,table2 where table1.xxx=table2.xxx;
例子:
關於sqlite 多表查詢的,有3個表
A表
id user
1 admin
2 guest
B表
id app_name version
1 app_1 1.1
2 app_2 1.3
3 app_3 1.4
C表
id user_id app_name_id
1 1 1
2 2 2
3 1 3
要求:經過SELECT把A表中admin使用的app的app_name和version顯示出來
SQL語句:
select A.user,B.app_name,B.version from A,B,C where A.user='admin' and A.id=C.user_id and C.app_name_id=B.id
//———————方式二:
二、 inner join table on
記錄一下sqlite中多表查詢。
表1:品牌:brands( brandid vARCHAR(20),
brand VARCHAR(20),
remark vARCHAR(100))
表2:類型:types( typeid vARCHAR(20),
type VARCHAR(20),
remark vARCHAR(100))
表3:品牌:levels( levelid vARCHAR(20),
level VARCHAR(20),
remark vARCHAR(100))
表4:油品:oils( oilid vARCHAR(20),brandid VARCHAR(20),typeid vARCHAR(20),levelid vARCHAR(20),remark vARCHAR(100))
說明一下:前三個表爲基本信息,即油品的品牌、類型、級別,表4爲油品信息,那麼該如何顯示油品的基本信息呢?
select brands.brand,types.type,levels.level from oils
inner join brands on oils.brandid = brands.brandid
inner join types on oils.typeid =types.typeid
inner join levels on oils.levelid =levels.levelid
再如:
select table1.abc from table1 inner join table2 on table1.xxx=table2.xxx;
//———————方式三:
3、支持左外鏈接(left outer join)
例如:
select * from foods
left outer join food_types
on foods.id=food_types.food_id
4、不支持右外鏈接和全鏈接。
// 唐詩三百
[self.fmdb open];
NSString *sql=nil;
if ([self.myType isEqualToString:@"myCollection"]==YES) {
//個人收藏
要特別注意:是對哪些表進行操做的
sql=@"select T_SHI.D_AUTHOR,T_SHI.D_KIND,T_SHI.D_TITLE,T_SHI.D_ID,myCollection.timer from T_SHI,myCollection where T_SHI.D_ID = myCollection.pID";
}else{
//個人記錄
sql=@"select T_SHI.D_AUTHOR,T_SHI.D_KIND,T_SHI.D_TITLE,T_SHI.D_ID,myRecord.timer from T_SHI,myRecord where T_SHI.D_ID = myRecord.pID";
}
NSLog(@"SQL = %@",sql);
FMResultSet *poemRSet=[self.fmdb executeQuery:sql];
// NSLog(@"錯誤:%@",[self.fmdb lastError]);
while ([poemRSet next]) {
Poem *newPoem=[[Poem alloc]init];
newPoem.poemAuthor=[poemRSet stringForColumn:D_AUTHOR];
newPoem.poemKind=[poemRSet stringForColumn:D_KIND];
newPoem.poemTime=[poemRSet stringForColumn:TIMER];
newPoem.poemTitle=[poemRSet stringForColumn:D_TITLE];
newPoem.poemID=[poemRSet intForColumn:D_ID];
NSLog(@"%@",[newPoem description]);
[self.allPoemInfo addObject:newPoem];
}
[self.fmdb close];
//------------------------------------End---------------------------------------------
fmdb-master.zip
101.2 KB
摘要:FMDatabaseQueue、事務處理
FMDatabaseQueue 及線程安全
在多個線程中同時使用一個FMDatabase實例是不明智的。如今你能夠爲每一個線程建立一個FMDatabase對象。 不要讓多個線程分享同一個實例,它沒法在多個線程中同時使用。 若此,壞事會常常發生,程序會時不時崩潰,或者報告異常,或者隕石會從天空中掉下來砸到你Mac Pro. 總之很崩潰。因此,不要初始化FMDatabase對象,而後在多個線程中使用。請使用 FMDatabaseQueue,它是你的朋友並且會幫助你。如下是使用方法:
首先建立隊列。
//-----------------------------隊列FMDatabaseQueue------------------------------------------
使用隊列FMDatabaseQueue
在多線程環境下,爲了防止同時執行SQL語句時發生衝突,能夠考慮使用FMDatabaseQueue來執行SQL語句。
在inDatabase:^(FMDatabase *db){在這裏執行SQL語句},操做過程都是相同的
NSString *dbPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)firstObject]stringByAppendingPathComponent:@"demo4.db"];
self.dbPath = dbPath;
//至關於一個FMDatabase對象
self.queue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
1)建立表
[self.queue inDatabase:^(FMDatabase *db) {
BOOL res = [db executeUpdate:@"CREATE TABLE IF NOT EXISTS person(id integer PRIMARY KEY, name text NOT NULL)"];
if(res){
NSLog(@"建表成功");
}else{
NSLog(@"建表失敗");
}
}];
2)插入值
[self.queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO person(name) VALUES(?)", @"Daniel"];
}];
3)查詢:
[self.queue inDatabase:^(FMDatabase *db) {
FMResultSet *resultSet = [db executeQuery:@"SELECT * FROM person"];
while ([resultSet next]) {
int ID = [resultSet intForColumn:@"id"];
NSString *name = [resultSet stringForColumn:@"name"];
NSLog(@"ID:%d,name:%@", ID, name);
}
}];
//一段查詢數據庫的代碼
NSString *dbPath=[[NSBundle mainBundle]pathForResource:@"poem" ofType:@"db"];
if (dbPath == nil) {
NSLog(@"沒有找到數據庫!");
}
NSLog(@"dbpath = %@",dbPath);
FMDatabase *fmdb=[FMDatabase databaseWithPath:dbPath];//獲取數據庫
[fmdb open];//打開數據庫
NSLog(@"%d",_Mypoem.poemID);
FMResultSet *poemRSet=[fmdb executeQueryWithFormat:@"select D_SHI,D_INTROSHI from T_SHI where D_ID = %d",self.Mypoem.poemID];//條件查詢:where D_ID == self.Mypoem.poemID 的記錄,記得須要使用格式化的語句:executeQueryWithFormat
while ([poemRSet next]) {//獲取一條語句[poemRSet next]的返回值是BOOL類型的
NSString *temp1=[poemRSet stringForColumn:D_SHI];//讀取字段爲D_SHI下的內容
NSString *temp2=[poemRSet stringForColumn:D_INTROSHI];//讀取字段爲D_INTROSHI下的內容
self.textView.text=[temp1 stringByAppendingString:temp2];//拼接後,賦值給self.textView.text
NSLog(@"%@",[temp1 stringByAppendingString:temp2]);
}
[fmdb close];//有打開,就要有關閉
//--------------------------------------------------------------------------------
事務處理(Transaction)
FMDatabase *db = [FMDatabase databaseWithPath:self.dbPath];
[db open];
//開始一個事務
[db beginTransaction];
[db executeUpdate:@"INSERT INTO person(name) VALUES(?)", @"Shasha"];
//若是在執行某條SQL語句時出現問題,因爲咱們處在一個事務中,因此執行成功的SQL會自動回滾(恢復之前的數據)
//[db rollback];//主動回滾
[db executeUpdate:@"INSERT INTO person(name) VALUES(?)", @"Shanshan"];
//提交一個事務
[db commit];
//在隊列中執行事務
[self.queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"INSERT INTO person(name) VALUES(?)", @"AAA"];
[db executeUpdate:@"INSERT INTO person(name) VALUES(?)", @"BBB"];
//*rollback = YES;//主動回滾
}];