iOS进阶第二节 数据读写之SQL数据库
一、数据库管理系统
1>. SQL语言概述
SQL是Structured Query Language(结构化查询语句)的缩写,SQL是专门为数据库而扩建的操作命令集,是一种功能齐全的数据库语言。
2>. 常见的数据库
- MySQL:MySQL是一个精巧的SQL数据库管理系统,而且是开源的数据管理系统。MySQL主要目标是快速、健全和易用。由于它的强大功能、灵活性、丰富的应用编程接口(API)以及精巧的系统结构,受到了广大自由软件爱好者甚至商业软件用户的青睐。
- Oracle:Oracle Database,又名Oracle RDNMS,或简称Oracle。是甲骨文公司的一款关系型数据库管理系统。系统可移植性好、使用方便、功能强。
3>. 数据库定义、分类、特征
- 数据库(Database)是按照数据结构来组织、存储和管理数据的仓库。
- 数据库可分为关系型数据库(主流)、对象型数据库、层次式数据库。
- 常见的关系型数据库有:Oracle、MySQL、SQL Server、Access、DB2、Sybase(前六种是PC端)、SQLite(嵌入式\移动客服端)。
- 特征: ①. 以一定方式存储在一起
②. 能为多个用户共享
③. 具有尽可能少的冗余代码
④. 是与程序彼此独立的数据集合
4>. SQLite数据库
SQLite是一个轻量级的关系数据库。SQLite最初的设计目标是用于嵌入式系统,它占用资源非常少,在嵌入式设备中,只需要几百K的内存就够了,目前应用于Android、iOS、Windows Phone等智能手机。iOS使用SQLite时,只需要加入libsqlite3.0.tbd依赖以及引入sqlite3.h头文件即可。
5>. 表、字段、记录(SQL详细学习可到 http://www.w3school.com.cn/sql/index.asp)
- 表:保存一类数据(例如学生表),是数据库中一个非常重要的对象,是其他对象的基础。根据信息的分类情况,一个数据库中可能包含若干个数据表。
- 字段:标识本列数据类型,表的"列"称为"字段",每个字段包含某一个专题的信息。
- 记录:表中一行的信息,是指对应于数据表中一行信息的一组完整的相关信息。
二、SQL语句
1>.SQLite数据库数据类型
- SQLite是无类型扥数据库,可以保存任何类型的数据(C语言),对于SQLite来说对字段不指定类型是完全有效的。(注:良好的编程习惯要为字段标注类型)
- 为了使SQLite和其他数据库之间的兼容性最大化,SQLite支持"类型近似"的观点,列的类型近似指的是存储在列上数据的推荐类型。
2>. SQLite字段的约束条件
3>. SQL数据库操作语句(不区分大小写)
①. 建表命令(Create table)
create table if not exists 表名 (字段1 约束1 约束2 ..., 字段2 约束1 约束2 ..., ...... );
②. 添加表中数据命令(Insert)
insert into 表名 (字段1, 字段2, ...... ) valuse (字段1值, 字段2值, ...... );
③. 更新表中数据命令(Update)
update 表名 set 字段名1 = 修改值1, 字段名2 = 修改值2, ...... where 条件;
④. 删除表中数据命令(Delete)
delete from 表名 where 条件;
⑤. 数据库检索命令(Select)
select 要查询字段 from 表名 where 条件;
注1:具体用法可上 w3school 学习(http://www.sqlite.org/docs.html)
注2:创建和编辑SQLite 数据库需要用到相关插件或者应用,一般的有火狐浏览器的SQLite Manager 插件和 Navicat Premium 应用
注3:数据定义语句(DDL:Data Definition Language):包括 create 和 drop 等操作,在数据库中创建新表或删除表(create table或 drop table)
数据操作语句(DML:Data Manipulation Language):包括insert、update、delete等操作,分别用于添加、修改、删除表中的数据
数据查询语句(DQL:Data Query Language):可以用于查询获得表中的数据,关键字select是DQL(也是所有SQL)用得最多的操作,其他DQL常用的关键字有where,order by,group by和havin
三、iOS的数据库技术的实现
1>. 打开/关闭数据库
①. 创建或打开数据库
· sqlite3_open() 将根据文件路径打开数据库,如果不存在,则会创建一个新的数据库。
· 如果result等于常量SQLITE_OK,则表示成功打开数据库。
· sqlite3 *db:一个打开的数据库实例。
· 数据库文件的路径必须以C语言字符串(而非NSSString)传入。
代码如下:
//创建一个数据库指针作为单例,防止重复打开关闭 static sqlite3 *db = nil; // 打开数据库 + (sqlite3 *)open { // 此方法的主要作用是打开数据库,准确的说是连接数据库 // 返回值是一个数据库指针 // 因为,这个数据库在很多的SQLitr API(函数)中都会使用到,我们声明一个类方法来获取,更加方便 if (db != nil) { return db; } // 获取Documents路径 NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; // 生成数据库文件在沙盒的路径 NSString *sqlPath = [docPath stringByAppendingPathComponent:@"studb.sqlite"]; // 获取文件管理对象 NSFileManager *fileManager = [NSFileManager defaultManager]; // 判断沙盒中是否存在数据库文件,如果存在就不执行拷贝操作,否则执行 if ([fileManager fileExistsAtPath:sqlPath] == NO) { // 获取数据库文件在包中的路径 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"studb" ofType:@"sqlite"]; // 使用文件管理对象,完成拷贝操作 [fileManager copyItemAtPath:filePath toPath:sqlPath error:nil]; } // 打开数据库 需要使用以下函数 // 第一个参数是数据库路径,是一个C语言的字符串 // 第二个参数是指向数据库指针的指针,保存数据库地址 sqlite3_open([sqlPath UTF8String], &db); return db; }
②. 关闭数据库
代码如下
// 关闭数据库 + (void)close { // 关闭数据库 sqlite3_close(db); // 置空数据库指针 db = nil; } // 单独创建一个DB类,继承自NSObject // 这个类主要是提供方法来操作数据库(打开和关闭数据库的方法) // 再我们的iOS工程中,一帮情况只有一个数据库,我们可以在数据库中创建多张表来保存不同的信息。
// 但是千万不要创建多个数据库,每个数据库中只有一张表,因为不断的连接、关闭数据库是很耗性能的。
2>. SQLite语法:常用系列函数
①. sqlite3_exec() 函数 可以执行任何SQL语句,比如建表、更新、插入、删除 操作。
但是一般不用它执行查询语句,因为它不会返回查询到的数据。
示例代码:创建表
// 1、准备SQL语句 NSString *sqlString = "create table if not exists stu(s_id integer primary primary key autoincrement not null, s_name text, s_age integer)"; // 2、执行SQL语句 int result = sqlite3_exec(db, sqlString.UTF8String, null, null, null); if (result == SQLITE_OK) { NSLog(@"建表成功!"); } else { NSog(@"建表失败!"); }
②. sqlite3_prepare() 函数
sqlite3_prepare(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail);
参数:db:数据库指针
zSql:SQL语句,使用UTF8编码;
nByte:如果nByte小于0,则函数取出zSql中从开始到第一个0终止符的内容;
如果nByte不是负的,那么它就是这个函数从zSql中读取的字节数的最大值。一般填 -1 ;
pzTail:上面提到zSql在遇见终止符或者到达nByte之后结束,假如zSql还有剩余的内容就存放在pzTail中,
不包括终止符;
ppStmt:能够使用sqlite3_step() 函数 执行的编译好的准备语句的指针,如果发生错误,它被置为NULL。
③. sqlite3_bind_*() 函数(以 sqlite3_bind_text() 为例)
sqlite3_bind_text(sqlite3_stmt *, int, const char *, int n, void (*)(void *))
参数:sqlite3_stmt *:伴随指针
int:参数序号(从1开始)
const char *:参数值
int n :参数长度
void (*)(void *) :函数指针,SQLITE3执行完操作后调此函数,通常用于释放字符串占用的内存
④. sqlite3_column_* () 函数
sqlite3_column_blob(sqlite3_stmt *, int iCol)
参数:sqlite3_stmt *:从sqlite3_prepare返回来的prepare statement对象的指针
int iCol:指定这一行中想要被返回的列的索引(从0开始)
附表:常用函数功能
3>. 具体实现的增删改查操作代码
①.获取所有数据方法(以一个Student 的数据库为例)
// 获取表中保存的所有学生 + (NSArray *)allStudents { // 查询需要使用Student语句 // 不管对数据库进行什么操作,都需要连接数据库 sqlite3 *db = [DB open]; // 创建一个语句对象 sqlite3_stmt *stmt = nil; // 声明数组对象 NSMutableArray *mArr = nil; // 此函数的作用是生成一个语句对象,此时SQL语句并没有执行,创建的语句对象,保存了关键的数据库,执行的SQL语句,SQL语句的长度等信息 int result = sqlite3_prepare_v2(db, "select * from Student", -1, &stmt, nil); if (result == SQLITE_OK) { // 为数组开辟空间 mArr = [NSMutableArray arrayWithCapacity:0]; // SQLITE_ROW仅用于查询语句,sqlite3_step()函数执行后的结果如果是SQLITE_ROW,说明结果集里面还有数据,会自动跳到下一条结果,如果是最后一条返回SQLITE_DONE,结束查询过程 while (sqlite3_step(stmt) == SQLITE_ROW) { // 获取记录中的字段值 // 第一个参数是语句对象,第二个参数是字段的下标,从0开始 int ID = sqlite3_column_int(stmt, 0); const unsigned char *cName = sqlite3_column_text(stmt, 1); const unsigned char *cGender = sqlite3_column_text(stmt, 2); // 将获取到得C语言字符串转换成OC字符串 NSString *name =[NSString stringWithUTF8String:(const char *)cName]; NSString *gender =[NSString stringWithUTF8String:(const char *)cGender]; Student *student = [Student studentWithID:ID name:name gender:gender]; [mArr addObject:student]; } } // 不管语句对象执行与否,释放语句队像 sqlite3_finalize(stmt); return mArr; }
②. 查询数据
// 根据指定ID,查找对应学生 + (Student *)findStudentByID:(int)ID { // 打开数据库 sqlite3 *db = [DB open]; // 创建一个语句对象 sqlite3_stmt *stmt = nil; Student *student = nil; int result = sqlite3_prepare_v2(db, "select * from Student where ID = ?", -1, &stmt, nil); if (result == SQLITE_OK) { // 如果查询语句或者其他SQL语句有条件,在准备语句对象的函数内部,SQL语句中用?来代替条件,那么在语句执行之前,一定要绑定 // 1代表SQL语句中的第一个问号,下标从1 开始 sqlite3_bind_int(stmt, 1, ID); if (sqlite3_step(stmt) == SQLITE_ROW) { // 获取记录中的字段信息 const unsigned char *cName = sqlite3_column_text(stmt, 1); const unsigned char *cGender = sqlite3_column_text(stmt, 2); // 将C语言字符串转换成OC字符串 NSString *name = [NSString stringWithUTF8String:(const char *) cName]; NSString *gender = [NSString stringWithUTF8String:(const char *) cGender]; student = [Student studentWithID:ID name:name gender:gender]; } } // 先释放语句对象 sqlite3_finalize(stmt); return student; }
③. 增加一条数据
// 插入一条记录 + (void)insertStudentWithID:(int)ID name:(NSString *)name gender:(NSString *)gender { // 打开数据库 sqlite3 *db = [DB open]; // 创建一个语句对象 sqlite3_stmt *stmt = nil; int result = sqlite3_prepare_v2(db, "insert into Student values(?, ?, ?)", -1, &stmt, nil); if (result == SQLITE_OK) { // 绑定问号 sqlite3_bind_int(stmt, 1, ID); sqlite3_bind_text(stmt, 2, [name UTF8String], -1, nil); sqlite3_bind_text(stmt, 3, [gender UTF8String], -1, nil); // 插入与查询不一样,执行结果没有返回值 sqlite3_step(stmt); } // 释放语句对象 sqlite3_finalize(stmt); }
④. 修改一条数据
// 更新指定ID的姓名和性别 + (void)updateStudentName:(NSString *)name gender:(NSString *)gender forID:(int)ID { // 打开数据库 sqlite3 *db = [DB open]; // 创建一个语句对象 sqlite3_stmt *stmt = nil; int result = sqlite3_prepare_v2(db, "update Student set name = ?, gender = ? where ID = ?", -1, &stmt, nil); if (result == SQLITE_OK) { // 绑定问号 sqlite3_bind_text(stmt, 1, [name UTF8String], -1, nil); sqlite3_bind_text(stmt, 2, [gender UTF8String], -1, nil); sqlite3_bind_int(stmt, 3, ID); sqlite3_step(stmt); } // 释放语句对象 sqlite3_finalize(stmt); }
⑤. 删除一条数据
// 根据指定ID删除学生 + (void)deleteStudnetByID:(int)ID { // 打开数据库 sqlite3 *db = [DB open]; // 创建一个语句对象 sqlite3_stmt *stmt = nil; int result = sqlite3_prepare_v2(db, "delete from Student where ID = ?", -1, &stmt, nil); if (result == SQLITE_OK) { // 绑定问号 sqlite3_bind_int(stmt, 1, ID); sqlite3_step(stmt); } // 释放语句对象 sqlite3_finalize(stmt); }
// 注:将对数据的具体操作方法,写到与表关联的模型类里面,这样让功能更加独立,代码更加清晰