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):包括 createdrop 等操作,在数据库中创建新表或删除表(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);
}
// 注:将对数据的具体操作方法,写到与表关联的模型类里面,这样让功能更加独立,代码更加清晰
原文地址:https://www.cnblogs.com/hyl2012/p/5232728.html