iOS系統自帶Core Data
來進行持久化處理,而且Core Data
可以使用圖形化界面來創建對象,但是Core Data
不是關系型數據庫,對於Core Data
來說比較擅長管理在設備上創建的數據持久化存儲用戶創建的對象,但是要處理大量的數據時就應該優先選擇SQL
關系型數據庫來存儲這些數據。 Core Data
在後台也是使用SQLite來存儲數據的,但是開發人員不能直接訪問這些數據,只能通過Core Data
提供的API來操作,如果一旦人為的通過SQLite
修改這些數據那麼使用Core Data
再次訪問這些數據時就會發生錯誤。
SQLite庫
SQLite是使用C
語言寫的開源庫,實現了一個自包含的SQL關系型數據庫引擎
,可以使用SQLite
存儲操作大量的數據,作為關系型數據庫我們可以在一個數據庫中建立多張相關聯的表來解決大量數據重復的問題。而且SQLite
庫也針對移動設備上的使用進行了優化。
因為SQLite
的接口使用C
寫的,而且Objective-C
是C
的超集所以可以直接在項目中使用SQLite
。
設計一個數據庫
開始之前首先要想到需要存什麼數據,然後怎麼設計這個數據庫。
首先我們設計一個數據庫用來存儲人員信息如下:
上面是所有的人員信息,實際可能比這個多很多。但是我們發現region
這一行中有很多的數據重復出現。很多人可能來自同一個地方,為了避免這種情況我們應該再重新創建一張表來單獨存儲region
這列的信息然後在這個表中引用region
表中的信息。當然我們還可以在region
表中添加更多的信息比如詳細地址。現在創建兩張表people
與region
如下所示
people表
region表
使用SQLite創建數據庫
為了熟悉SQLite語句,打開shell使用SQLite命令行來創建一個數據庫
打開創建數據庫
打開shell切換到指定目錄輸入
sqlite3 database.db
這行命令是啟動sqlite命令行並且創建新的數據庫database.db並附加該數據到命令行
此時已經進入sqlite命令行通過輸入.help可以顯示可以使用哪些命令,通過輸入.databases來查看當前有哪些數據庫附加到當前的命令行工具中。輸入.quit或.exit退出當前命令行工具
創建表
create table "main"."people" ("id" integer primary key autoincrement not null, "name" text,"age" integer,"email" text,"region" integer);
這條命令是創建一個people的表,並且將id字段設為primary key主鍵將其指定為一個autoincrement自動增長的字段。表示不用提供id的值數據庫將自動生成。後面的表示該張表中所含有的字段。
因為要設計兩張表所以還需要創建region表
create table "main"."region" ("regionid" integer primary key autoincrement not null, "regioninfo" text,"address" text not null);
添加數據
此時已經成功創建了兩張表我們要添加數據進去
insert into "main"."people" ("name","age","email","region") values ('jhon','20','jhon@mail','1');
這樣成功往people表成功的插入了一條數據。這樣寫效率比較低。每次只能插入一條數據不要擔心SQLite支持將文件直接導入數據庫中。可以是普通的文件文件也可以是excel文件。下面我們創建一個people.txt文件格式如下:
1 jhon 20 jhon@mail 12 peter 20 peter@mail 23 july 20 july@mail 14 elev 20 elev@mail 35 ribet 20 ribet@mail
注意每個字段之間的空隙是用制表符來分割的,也就是創建文件是每個字段用tab鍵進行分割。字段的順序必須和表中的順序相同然後將people.txt文件導入people表中
.separator ""
根據來分割字段,然後接著輸入
.import "people.txt" people
導入people.txt文件到people表中此時會提示如下錯誤信息
people.txt:1: INSERT failed: UNIQUE constraint failed: people.id
不用擔心這個意思是說已經存在了一個id為1的數據所以這條數據插入失敗,是因為我們之前手動了插入了一條數據。可以通過以下指令來查插入的數據
select * from people;
然後用同樣的方法創建一個region.txt的文件並將其導入region表中。
注意
使用SQLite命令行可能會出現...>這表示指令輸入錯誤,按ctrl+d即可退出
查詢數據上面已經添加完數據通過select指令可以查詢這些數據
select * from people;11
查詢popple表中的所有數據
鏈接表數據
select name,regioninfo from people,region where people.region=region.regionid;
輸出結果
jhon beijing peter shanghai july beijing elev shenzhen ribet beijing
從people和region表中查找name與regioninfo字段並且只查詢people.region=region.regionid相匹配的結果,如果沒有這個條件那麼將出現5*3=15條數據
如果要查找某個地區的人使用where來篩選條件
select name,regioninfo from people,region where people.region=region.regionid and region.regioninfo="beijing";
輸出結果
jhon beijing july beijing ribet beijing
iOS中SQLite的使用
開始之前應該在項目中引用SQLite庫。TARGETS->General->Linked Frameworks and Libraries如下圖所示
將之前創建好的database.db文件導入項目中,並引入sqlite3.h頭文件
#import
使用SQLite需要一下幾個步驟:
聲明類變量sqlite3來保存對數據庫的引用
使用sqlite3_open打開數據庫
創建SQLite語句
創建SQLite語句對象sqlite3_stmt
准備SQLite語句sqlite3_prepare_v2
開始遍歷結果sqlite3_step
初始化打開數據庫
sqlite3 * database; -(void)initDatabase { NSString *path = [[NSBundle mainBundle] pathForResource:@"database" ofType:@"db"]; if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) { NSLog(@"open database"); } else{ sqlite3_close(database); NSLog(@"error %s",sqlite3_errmsg(database)); } }
打開數據庫如果返回的狀態碼不是SQLITE_OK那麼打開失敗關閉數據庫並且輸出錯誤信息
查詢數據
-(void)operateDatabase { const char * sql = "select name,regioninfo from people,region where people.region=region.regionid"; sqlite3_stmt *statement; //創建sql語句對象 int sqlResult = sqlite3_prepare_v2(database, sql, -1, &statement, NULL); //准備sql語句 if ( sqlResult== SQLITE_OK) //是否准備結束 { while (sqlite3_step(statement) == SQLITE_ROW) //開始遍歷查詢結果 { NSLog(@"name %s, region %s",sqlite3_column_text(statement, 0),sqlite3_column_text(statement, 1)); } } }
輸出結果:
name jhon, region beijingname peter, region shanghainame july, region beijingname elev, region shenzhenname ribet, region beijing1234512345
sqlite3_prepare_v2的參數第一個是數據庫連接,第二個是sql語句,第三個是這個語句的長度傳入-1表示地道第一個null終止符為止,第四個是返回一個語句對象,第五個是返回一個指向該sql語句的第一個字節的指針。
當sqlite3_prepare_v2返回狀態碼SQLITE_OK時開始遍歷結果。
sqlite3_step用來遍歷結果如果返回為SQLITE_ROW表示下一行准備結束可以開始查詢。所以此處用一個while來便利所以查詢的結果
遍歷的過程中要取到結果通過一下的函數獲取遍歷結果
SQLITE_API const void *SQLITE_STDCALL sqlite3_column_blob(sqlite3_stmt*, int iCol); SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes(sqlite3_stmt*, int iCol); SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes16(sqlite3_stmt*, int iCol); SQLITE_API double SQLITE_STDCALL sqlite3_column_double(sqlite3_stmt*, int iCol); SQLITE_API int SQLITE_STDCALL sqlite3_column_int(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_column_int64(sqlite3_stmt*, int iCol); SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_column_text(sqlite3_stmt*, int iCol); SQLITE_API const void *SQLITE_STDCALL sqlite3_column_text16(sqlite3_stmt*, int iCol); SQLITE_API int SQLITE_STDCALL sqlite3_column_type(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_column_value(sqlite3_stmt*, int iCol);
上面是所支持的結果類型,第一個參數為sql語句對象,第二個為獲取哪一列的信息。
參數化查詢
上面的情況每次sql語句都寫死了,如果想要改變某個條件就需要重新寫一條語句,幸好sqlite支持參數化查詢,每次只需要更改查詢條件就可以而不用更改整條sql語句,如果現在只想查詢北京地區的人口信息使用參數化查詢如下:
-(void)operateDatabase { const char * sql = "select name,regioninfo from people,region where people.region=region.regionid and regioninfo=?"; sqlite3_stmt *statement; //創建sql語句對象 int sqlResult = sqlite3_prepare_v2(database, sql, -1, &statement, NULL); //准備sql語句 sqlite3_bind_text(statement, 1, "beijing", -1,SQLITE_TRANSIENT); //綁定參數 if ( sqlResult== SQLITE_OK) //是否准備結束 { while (sqlite3_step(statement) == SQLITE_ROW) //開始遍歷查詢結果 { NSLog(@"name %s, region %s",sqlite3_column_text(statement, 0),sqlite3_column_text(statement, 1)); } } }
輸出結果:
name jhon, regionbeijingname july, regionbeijingname ribet, regionbeijing
可見需要更改的條件sql中用?來代替,然後用sqlite3_bind_text函數來綁定參數。根據類型不同綁定的函數也不同
SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,void(*)(void*)); SQLITE_API int SQLITE_STDCALL sqlite3_bind_double(sqlite3_stmt*, int, double); SQLITE_API int SQLITE_STDCALL sqlite3_bind_int(sqlite3_stmt*, int, int); SQLITE_API int SQLITE_STDCALL sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); SQLITE_API int SQLITE_STDCALL sqlite3_bind_null(sqlite3_stmt*, int); SQLITE_API int SQLITE_STDCALL sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); SQLITE_API int SQLITE_STDCALL sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); SQLITE_API int SQLITE_STDCALL sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,void(*)(void*), unsigned char encoding); SQLITE_API int SQLITE_STDCALL sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
上面列出了所有支持綁定類型的函數。
結束
本篇只是列出了SQLite常用的基礎方法,實際開發中數據庫可能要比這復雜許多,而且還要考慮數據競爭線程安全的問題。具體還是要自己在開發中實踐。