爬爬爬之路:UI(十七) 数据持久化(SQLite3数据库实现)

来源:互联网 发布:织梦cms移动版静态化 编辑:程序博客网 时间:2024/05/23 19:29

常见的数据库

目前常见的数据库有MySQL,Oracle,sql server,SQLite等等
其中Oracle, MySQL 目前均属于Oracle(甲骨文)公司, sql server是属于微软的数据库, 这三者都是大型的企业级数据库

SQLite是轻量级数据库, 目前在iOS开发中, 使用的就是SQLite的新版本SQLite3.

数据库是以表的形式储存数据, 一张表就对应了一个相关联的数据集合. 将一系列数据根据符合该表的格式存入, 就可以实现将数据永久的保存在硬盘中.

用数据库存储数据便于数据的增删改查, 对于一些复杂类型的数据有着非常便利的处理手段.
数据库的查询也是以键值的模式进行的, 每张表都必须有一个主键, 而这个主键在一张表上必须是唯一的.
在SQLite3数据库中, 键值可以是一个字符串类型或者证书类型的数据. 若是整数数据, 默认是会自增的.

在iOS开发中, 一个应用对应这一个数据库, 而一个数据库中, 根据储存的设计模式, 可以有一张或者多张表. 数据库本身是存在应用的沙盒当中.

数据库使用的是SQL语言. SQLite3数据库的文件后缀名是.sqlite.

SQL语言是一种结构化查询语言, 语言本身比较简单, 系统关键字较少, 且这些系统关键字是不区分大小写的.
主要功能的关键字有:

  1. 增: insert
  2. 删: delete
  3. 改: updata
  4. 查: select
  5. 另外常用的还有创建表的关键字: create

SQLite3存储的数据格式主要有以下几种类型:

  1. NULL 值为空值
  2. INTEGER 值为整型数据
  3. REAL 值为浮点型数据
  4. TEXT 值为文本字符串类型
  5. BLOB 值为二进制类型

注意: 系统关键字虽然不区分大小写, 但是错一个字母都不行.

接下来主要介绍iOS开发中怎么使用SQLite3数据库.


使用准备

Xcode中已经内置了一个SQLite3数据库, 在我们需要使用SQLite数据库的时候, 只需要在工程中
添加框架libsqlite3.0.dylib如图所示(Xcode7.0后 框架名的后缀更为.tbd) 但是并不影响添加框架的步骤. 添加框架之后 还需要引入头文件, 语句如下

#import <sqlite3.h>

方法准备

完成这两步即可使用SQLite3数据库.
为了方便使用数据库, 可以将数据库相关的操作都写在一个单例类里. 以下会通过代码来介绍SQLite3数据库的操作方法.
代码如下:

// SQLiteManager.m#pragma mark ---- 数据库操作类的初始化方法+ (SQLiteManager *)shareManager {    static SQLiteManager *manager = nil;    if (manager = nil) {        manager = [[SQLiteManager alloc] init];        // 通常在调用本代理类的初始化方法时, 需要同时完成表的创建.        [manager createTable];    }    return manager;}// 特殊的, 需要声明一个全局调用的静态数据库指针, 方便以后的操作调用#pragma mark ---- 声明全局静态数据库指针static sqlite3 *db = nil;// 创建表的方法- (void)createTable {    // 具体内容下文介绍}#pragma mark ---- 打开数据库方法- (sqlite3 *)openDB {    if (db != nil) {        return db;    } else {        // 获得沙盒中document文件夹的路径        NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];        // 数据库名为MySqlite.sqlite, 存放路径为document文件夹下        NSString *dbPath = [documentPath stringByAppendingPathComponent:@"MySqlite.sqlite"];        // UTF8String为将NSString对象转化成char *类型的C语言字符串方法        int result = sqlite3_open(dbPath.UTF8String, &db);        /*          sqlite3_open()方法为打开数据库的C语言函数.          由于SQLite的操作方法都是C语言函数方法, 以下遇到会一一解释          本函数讲解:          第一个参数 数据库文件的路径(类型为C语言字符串)          第二个参数 数据库的指针地址          返回值为int类型, 以下的SQLite3的操作函数的返回值均为int型, 成功则返回值为0. 其余返回值大多表示失败, 本文最后会附上返回值的含义.        */        // SQLITE_OK为系统宏数据 值为0        if (result == SQLITE_OK) {            // NSLog(@"数据库打开成功");        } else {            // NSLog(@"数据库打开失败");        }        return db;    }}#pragma mark ---- 关闭数据库方法- (void)closeDB {    int result = sqlite3_close(db);    if (result == SQLITE_OK) {        // NSLog(@"关闭数据库成功");        // 成功关闭时, 为了数据安全, 需要将db指针置为空        db = nil;    } else {        // NSLog(@"关闭数据库失败");    }}

通常, 在iOS开发中, 需要存储的数据 都是Model层对象.
以下以一个简单的Student类为例

// Student.h// 属性如下:// 姓名@property (nonatomic, retain) NSString *name;// 性别@property (nonatomic, retain) NSString *gender;// 年龄@property (nonatomic, assign) NSInteger age;// 照片的二进制文件@property (nonatomic, retain) NSData *imageData;// 学号@property (nonatomic, assign) NSInteger number;

根据Student类的模型, 可以将Student类设计成一张表, 表的表头就是这5个属性.
其中学号一定是唯一的, 所以可以将学号作为这张表的主键.

创建表

// 创建表语句如下   补充上文提到的内容#pragma mark ---- 创建表方法- (void)createTable {    // 注意, 在操作时候需要先打开数据库, 操作完毕后要几十关闭数据库    db = [self openDB];    // 写sql语句    NSString *sql = @"create table IF NOT EXISTS Student (number integer primary key NOT NULL,name text NOT NULL, gender text NOT NULL, age integer NOT NULL, imageData BLOB not NULL)";    int result = sqlite3_exec(db, sql.UTF8String, NULL, NULL, NULL);    /*      sqlite3_exec()函数的系统声明方法如下:      int sqlite3_exec(          sqlite3*,                                  /* An open database */          const char *sql,                           /* SQL to be evaluated */          int (*callback)(void*,int,char**,char**),  /* Callback function */          void *,                                    /* 1st argument to callback */          char **errmsg                              /* Error msg written here */      );      第一个参数为打开数据库的指针.      第二个参数为要执行的sql语句, 注意要转化成char * 类型.      第三个参数为回调函数, 可以根据此函数完成一些其他查看等操作, 不常用.      第四个参数为一个可以传递给回调函数值的参数.      第五个参数为一个用于查看错误信息      当sql语句中不包含二进制数据时, 可以直接用本方法执行sql语句    */    if (result == SQLITE_OK) {        // NSLog(@"创建表成功");    } else {        // NSLog(@"创建表失败");    }}

数据增加到数据库中

// 将对象数据写入表中#pragma mark ---- 插入数据方法- (void)insertToTableWithStudent:(Student *)student {    // 打开数据库    db = [self openDB];    // 写sql语句(带不定变量的sql语句写法, 由于数据中存在imageData这个二进制数据, 不能直接用sqlite3_exec()方法, 否则会被转化成字符串. 以下用绑定方法写入)    NSString *sql = @"insert into Student(number,name,gender,age,imageData) values(?,?,?,?,?)";    // 创建跟随指针    sqlite3_stmt *stmt = nil;    /*        跟随指针的作用是用指针将数据和sql语句中的 ? 一一绑定, 告诉数据库?对应的是哪个数据, 注意绑定的数据是和括号内的数据一一按顺序对应的    */    // 先预执行sql语句 (称为准备函数) 可以用来判断sql语句是否正确, 若正确会继续执行    // 需要跟随指针绑定问号, 然后一步步执行(也就是按顺序一步写入一个数据)    int result = sqlite3_prepare_v2(db, sql.UTF8String, -1, &stmt, NULL);    /*        系统函数定义如下        int sqlite3_prepare_v2(          sqlite3 *db,            /* Database handle */          const char *zSql,       /* SQL statement, UTF-8 encoded */          int nByte,              /* Maximum length of zSql in bytes. */          sqlite3_stmt **ppStmt,  /* OUT: Statement handle */          const char **pzTail     /* OUT: Pointer to unused portion of zSql */        );        第一个参数 为打开数据库的指针        第二个参数 为需要执行的sql语句 需要转化类型        第三个参数 为sql语句的长度, 填写-1为可以无限长        第四个参数 为跟随指针的地址        第五个参数 为不需要执行的sql    */    if (result == SQLITE_OK) {        // NSLog(@"插入数据成功");        // 若成功, 需要将数据和 sql语句中的?进行绑定        sqlite3_bind_int(stmt, 1, (int)student.number);        // sqlite3_bind_int()函数为绑定方法, 第一个参数为跟随指针, 第二个参数为?的顺序 从1开始. 第三个参数为需要绑定的数据        sqlite3_bind_text(stmt, 2, student.name.UTF8String, -1, NULL);        // 每种数据类型都有与类型对应绑定方法. 字符串绑定的方法中 第四个参数为内容长度, 最后一个参数为一个回调指针        sqlite3_bind_text(stmt, 3, student.gender.UTF8String, -1, NULL);        sqlite3_bind_int(stmt, 4, (int)student.age);        // NSData类型的数据绑定 第三个参数为数据, 需要转化成无类型指针, 第四个参数为数据长度, 通常此时不填-1        sqlite3_bind_blob(stmt, 5, [student.imageData bytes], (int)[student.imageData length], NULL);        // 绑定完成后执行绑定方法        sqlite3_step(stmt);    } else {        // NSLog(@"插入失败");    }    // 无论是否插入成功, 都要释放跟随指针    sqlite3_finalize(stmt);    // 操作完成后(无论是否成功)需要关闭数据库    [self closeDB];}

将数据从数据库中删除 (需要根据一个条件删除数据)

// 此处以根据年龄为例, 将满足年龄大于22岁的人的数据删除- (void)deleteWithAge:(NSInteger)age {    // 打开数据库    db = [self openDB];    // 写sql语句    NSString *sql = @"delete from Student where age > ?";    // 可用分步(绑定)方法或者直接执行方法, 由于分步方法应用范围更广 此处以分步方法为例    // 创建跟随指针    sqlite3_stmt *stmt = nil;    // 预执行sql语句    int result = sqlite3_prepare_v2(db, sql.UTF8String, -1, &stmt, NULL);    if (result == SQLITE_OK) {        // NSLog(@"删除数据成功");        // 将数据和?绑定        sqlite3_bind_int(stmt, 1, (int)age);        // 执行绑定        sqlite3_step(stmt);    } else {        // NSLog(@"删除失败");    }    // 释放跟随指针    sqlite3_finalize(stmt);    // 关闭数据库    [self closeDB];}

更新数据(改变数据)

// 需要根据一个条件来更新// 以下以根据姓名, 将某人的年龄修改成指定的值- (void)upDataAge:(NSInteger)age byName:(NSString *)name {    db = [self openDB];    NSString *sql = [NSString stringWithFormat:@"updata Student set age = '%ld' where name = '%@'", age, name];    // 由于没有二进制NSData的数据, 可以直接执行sql语句    int result = sqlite3_exec(db, sql.UTF8String, NULL, NULL, NULL);    if (result == SQLITE_OK) {        // NSLog(@"修改成功");    } else {        // NSLog(@"修改失败");    }    // 别忘了关闭数据库    [self closeDB];}

查询数据

// 通常需要根据某个条件查询符合该条件的数据// 本方法以查询 满足 姓名为name并且年龄为age的人的全部信息- (Student *)selectedStudentWithName:(NSString *)name andAge:(NSInteger)age {    db = [self openDB];    NSString *sql = @"select * from Student where name = ? and age = ?";    // 创建一个对象备用    Student *student = [[Student alloc] init];    sqlite3_stmt *stmt = nil;    // 预执行    int result = sqlite3_prepare_v2(db, sql.UTF8String, -1, &stmt, NULL);    // 判断结果    if (result == SQLITE_OK) {        // NSLog(@"查询成功");        sqlite3_bind_text(stmt, 1, name.UTF8String, -1, NULL);        sqlite3_bind_int(stmt, 2, (int)age);        // 执行绑定        // 查询时 用SQLITE_ROW 作为半段条件, 如果下一行准备好了, 返回        // 这是可以继续查询        while (sqlite3_step(stmt) == SQLITE_ROW) {            // 读取数据            // 第二个参数为列数            // 如果是查询所有字段的话 这个列数是根据建表时的顺序一样            // 注意从0开始            // 如果查询的是特定的字段, 那么这个列数的顺序是根据sql的顺序来写 也是从0开始            int number = sqlite3_column_int(stmt, 0);            char *name = (char *)sqlite3_column_text(stmt, 1);            char *gender = (char *)sqlite3_column_temt(stmt, 2);            int age = sqlite3_column_int(stmt, 3);            // 读取二进制数据 第一个参数为二进制文件 第二个参数为二进制文件的长度            NSData *imagaData = [NSData dataWithBytes:sqlite3_column_blob(stmt, 4) lenth:sqlite_column_bytes(stmt, 4)];            student.number = number;            student.name = [NSString stringWithUTF8String:name];            student.gender = [NSString stringWithUTF8String:gender];            student.age = age;            student.imageData = imageData;        }           } else {        // NSLog(@"查询失败");    }    // 释放stmt跟随指针    sqlite3_finalize(stmt);    // 关闭数据库    [self closeDB];    // 自动释放对象    return [student autorelease];}// 查询所有数据 (返回一个数组)- (NSArray *)selectAllStudents {    db = [self openDB];    // 写sql    NSString *sql = @"select * from lanOuStudent";    NSMutableArray *array = [NSMutableArray array];    sqlite3_stmt *stmt = nil;    // 预执行    int result = sqlite3_prepare_v2(db, sql.UTF8String, -1, &stmt, NULL);    if (result == SQLITE_OK) {        NSLog(@"成功");        while (sqlite3_step(stmt) == SQLITE_ROW) {            // 读取数据            int number = sqlite3_column_int(stmt, 0);            char *name = (char *)sqlite3_column_text(stmt, 1);            char *gender = (char *)sqlite3_column_text(stmt, 2);            int age = sqlite3_column_int(stmt, 3);            // 读取二进制数据 第一个参数为二进制文件 第二个参数为二进制文件的长度            NSData *imageData = [NSData dataWithBytes:sqlite3_column_blob(stmt, 4) length:sqlite3_column_bytes(stmt, 4)];            // 赋值Model            Student *student = [[Student alloc] init];            student.number = number;            student.name = [NSString stringWithUTF8String:name];            student.gender = [NSString stringWithUTF8String:gender];            student.age = age;            student.imageData = imageData;            [student release];            // 添加到数组            [array addObject:student];        }    } else {        NSLog(@"失败");    }    // 释放跟随指针    sqlite3_finalize(stmt);    [self closeDB];    return array;}

SQLite3 操作函数返回值附表

#define SQLITE_OK                 0                     /* 成功 | Successful result *//* 错误码开始 */#define SQLITE_ERROR              1                     /* SQL错误 或 丢失数据库 | SQL error or missing database */#define SQLITE_INTERNAL           2                     /* SQLite 内部逻辑错误 | Internal logic error in SQLite */#define SQLITE_PERM               3                     /* 拒绝访问 | Access permission denied */#define SQLITE_ABORT              4                     /* 回调函数请求取消操作 | Callback routine requested an abort */#define SQLITE_BUSY               5                     /* 数据库文件被锁定 | The database file is locked */#define SQLITE_LOCKED             6                     /* 数据库中的一个表被锁定 | A table in the database is locked */#define SQLITE_NOMEM              7                     /* 某次 malloc() 函数调用失败 | A malloc() failed */#define SQLITE_READONLY           8                     /* 尝试写入一个只读数据库 | Attempt to write a readonly database */#define SQLITE_INTERRUPT          9                     /* 操作被 sqlite3_interupt() 函数中断 | Operation terminated by sqlite3_interrupt() */#define SQLITE_IOERR              10                    /* 发生某些磁盘 I/O 错误 | Some kind of disk I/O error occurred */#define SQLITE_CORRUPT            11                    /* 数据库磁盘映像不正确 | The database disk image is malformed */#define SQLITE_NOTFOUND           12                     /* sqlite3_file_control() 中出现未知操作数 | Unknown opcode in sqlite3_file_control() */#define SQLITE_FULL               13                     /* 因为数据库满导致插入失败 | Insertion failed because database is full */#define SQLITE_CANTOPEN           14                     /* 无法打开数据库文件 | Unable to open the database file */#define SQLITE_PROTOCOL           15                     /* 数据库锁定协议错误 | Database lock protocol error */#define SQLITE_EMPTY              16                     /* 数据库为空 | Database is empty */#define SQLITE_SCHEMA             17                     /* 数据结构发生改变 | The database schema changed */#define SQLITE_TOOBIG             18                     /* 字符串或二进制数据超过大小限制 | String or BLOB exceeds size limit */#define SQLITE_CONSTRAINT         19                     /* 由于约束违例而取消 | Abort due to constraint violation */#define SQLITE_MISMATCH           20                     /* 数据类型不匹配 | Data type mismatch */#define SQLITE_MISUSE             21                     /* 不正确的库使用 | Library used incorrectly */#define SQLITE_NOLFS              22                     /* 使用了操作系统不支持的功能 | Uses OS features not supported on host */#define SQLITE_AUTH               23                     /* 授权失败 | Authorization denied */#define SQLITE_FORMAT             24                     /* 附加数据库格式错误 | Auxiliary database format error */#define SQLITE_RANGE              25                     /* 传递给sqlite3_bind()的第二个参数超出范围 | 2nd parameter to sqlite3_bind out of range */#define SQLITE_NOTADB             26                     /* 被打开的文件不是一个数据库文件 | File opened that is not a database file */          #define SQLITE_ROW                100                     /* sqlite3_step() 已经产生一个行结果 | sqlite3_step() has another row ready */#define SQLITE_DONE               101                     /* sqlite3_step() 完成执行操作 | sqlite3_step() has finished executing *//* 错误码结束 */
2 0
原创粉丝点击