iOS系統自帶Core Data
來進行持久化處理,並且Core Data
可使用圖形化界面來建立對象,可是Core Data
不是關係型數據庫,對於Core Data
來講比較擅長管理在設備上建立的數據持久化存儲用戶建立的對象,可是要處理大量的數據時就應該優先選擇SQL
關係型數據庫來存儲這些數據。
Core Data
在後臺也是使用SQLite來存儲數據的,可是開發人員不能直接訪問這些數據,只能經過Core Data
提供的API來操做,若是一旦人爲的經過SQLite
修改這些數據那麼使用Core Data
再次訪問這些數據時就會發生錯誤。java
SQLite是使用C
語言寫的開源庫,實現了一個自包含的SQL關係型數據庫引擎
,可使用SQLite
存儲操做大量的數據,做爲關係型數據庫咱們能夠在一個數據庫中創建多張相關聯的表來解決大量數據重複的問題。並且SQLite
庫也針對移動設備上的使用進行了優化。
由於SQLite
的接口使用C
寫的,並且Objective-C
是C
的超集因此能夠直接在項目中使用SQLite
。ios
開始以前首先要想到須要存什麼數據,而後怎麼設計這個數據庫。
首先咱們設計一個數據庫用來存儲人員信息以下:sql
id | name | age | region | |
---|---|---|---|---|
1 | jhon | 20 | jhon@mail | beijing |
2 | peter | 20 | peter@mail | shanghai |
3 | july | 20 | july@mail | beijing |
4 | elev | 20 | elev@mail | shenzhen |
5 | ribet | 20 | ribet@mail | beijing |
上面是全部的人員信息,實際可能比這個多不少。可是咱們發現region
這一行中有不少的數據重複出現。不少人可能來自同一個地方,爲了不這種狀況咱們應該再從新建立一張表來單獨存儲region
這列的信息而後在這個表中引用region
表中的信息。固然咱們還能夠在region
表中添加更多的信息好比詳細地址。如今建立兩張表people
與region
以下所示shell
id | name | age | region | |
---|---|---|---|---|
1 | jhon | 20 | jhon@mail | 1 |
2 | peter | 20 | peter@mail | 2 |
3 | july | 20 | july@mail | 1 |
4 | elev | 20 | elev@mail | 3 |
5 | ribet | 20 | ribet@mail | 1 |
regionid | regioninfo | address |
---|---|---|
1 | beijing | fengtai |
2 | shanghai | jingan |
3 | shenzhen | futian |
爲了熟悉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
的值數據庫將自動生成。後面的表示該張表中所含有的字段。ruby
由於要設計兩張表因此還須要建立region
表markdown
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
文件格式以下:app
1 jhon 20 jhon@mail 1
2 peter 20 peter@mail 2
3 july 20 july@mail 1
4 elev 20 elev@mail 3
5 ribet 20 ribet@mail 1
注意每一個字段之間的空隙是用製表符\t
來分割的,也就是建立文件是每一個字段用tab
鍵進行分割。字段的順序必須和表中的順序相同而後將people.txt
文件導入people
表中函數
.separator "\t"
根據\t
來分割字段,而後接着輸入
.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;
查詢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
開始以前應該在項目中引用SQLite庫。TARGETS->General->Linked Frameworks and Libraries
以下圖所示
將以前建立好的database.db文件導入項目中,並引入sqlite3.h
頭文件
#import <sqlite3.h>
使用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 beijing
name peter, region shanghai
name july, region beijing
name elev, region shenzhen
name ribet, region beijing
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, regionbeijing
name july, regionbeijing
name 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
經常使用的基礎方法,實際開發中數據庫可能要比這複雜許多,並且還要考慮數據競爭線程安全的問題。具體仍是要本身在開發中實踐。