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
经常使用的基础方法,实际开发中数据库可能要比这复杂许多,并且还要考虑数据竞争线程安全的问题。具体仍是要本身在开发中实践。