SQLite是一款轻型的嵌入式数据库,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就足够了。它的处理速度比Mysql、PostgreSQL这两款著名的数据库都还快。SQLite提供的是一些C函数接口,你可以用这些函数操作数据库。通过使用这些接口,传递一些标准 sql 语句(以 char * 类型)给 SQLite函数,SQLite就会为你操作数据库。
1.SQLite 3简介(常用函数)
- sqlite3_open(const char *filename, sqlite3 **ppDb);
用来创建和打开数据库文件,接收两个参数,第一个是数据库的名字,第二个是数据库的句柄(我也不知道为啥叫这个名字,后面所有对数据库得操作都会用到这个句柄,所以暂时请先记着)。如果数据库文件不存在,将首先新建它,然后再打开它,否则只是打开它。
- sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail);
使用格式化的字符串来获得sql准备语句(prepared statement),然后转化为可被SQLite3识别的执行语句。(实际上这个函数并不执行这个SQL语句)
- sqlite3_step(sqlite3_stmt *);
这个函数执行上一个函数创建的准备语句,这个语句执行到结果集的第一行可用的位置,再次调用sqlite3_setp(),会继续前进到结果集的第二行。当执行插入、更新、删除操作时会被调用一次,当执行取回数据时可以执行多次。这个函数不能在sqlite3_preprare_v2之前调用。
- sqlite3_column_count(sqlite3_stmt *pStmt);
返回表的列数
- sqlite3_column_text(sqlite3_stmt *, int iCol);
以text的格式返回列的内容(实际上是C的char*类型)。它接收两个参数,SQLite语句和列的索引。
- sqlite3_column_name(sqlite3_stmt *, int N);
返回列的名字,参数和上一个函数一样。
- sqlite3_changes(sqlite3 *);
返回执行语句后受影响的行数。
- sqlite3_last_insert_rowid(sqlite3 *);
返回最后插入的行的id。
- sqlite3_errmsg(sqlite3 *);
返回SQLite错误描述。
- sqlite3_finalize(sqlite3_stmt *pStmt);
从内存删除之前sqlite3_prepare_v2函数创建的准备语句。
- sqlite3_close(sqlite3 *);
关闭数据库连接,在结束任何数据库数据修改后调用,它将释放其存储的系统资源。
2.在项目中创建一个SQLite 3数据库
- 首先我们需要将SQLite 3的库文件导入我们的项目中(iOS 9之前库文件为libsqlite3.dylib):
- 下面需要在使用到数据库的类中导入sqlite3的头文件,并创建一个sqlite3句柄:
- #import <sqlite3.h>
-
- @interface ViewController (){
- //sqlite3句柄
- sqlite3 *database;
- }
- //获取沙盒路径
- NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
- //获取名字为student的数据库路径
- NSString *dbFile = [path stringByAppendingPathComponent:@"student.sqlite"];
- NSLog(@"%@", dbFile);
- //打开数据库,若沙盒中没有此数据库则系统会新建一个
- if (sqlite3_open([dbFile UTF8String], &database) == SQLITE_OK) {
- //打开数据库成功
- NSLog(@"open success");
- //创建一条sql语句,创建一张名为student的数据表,表中有4个字段,id(整型,主键,自增),name(字符串类型),sex(字符串类型),class(字符串类型),sql语句编译器不会纠错,一定要仔细填写,sql语句不区分大小写
- const char *createSql = "create table if not exists student (id integer primary key autoincrement, name text, sex text, class text);";
- //创建错误信息
- char *err;
- //执行上面的sql语句
- if (sqlite3_exec(database, createSql, NULL, NULL, &err) != SQLITE_OK) {
- //返回值不为0,sql语句执行失败
- NSLog(@"create table failed!");
- }
- } else {
- //创建或打开数据库失败
- NSLog(@"create/open database failed!");
- }
- //如果用 sqlite3_open 开启了一个数据库,结尾时不要忘了用这个函数关闭数据库。
- sqlite3_close(database);
那么怎么知道我们新建成功了呢?别急,有图有真相:
这是一款名字叫SQLite Professional的Mac版SQLite可视化管理工具(并没有收广告费!),通过代码里的沙盒路径我们就找到了刚刚我们创建的数据库与数据表,且表中字段都正确,此次操作成功!
1.添加数据
由于sqlite3是由C语言底层实现的,所以sqlite的函数参数必须是C语言数据类型,如果我们使用NSString创建sql语句,我们需要将string类型转换为char *类型之后再作为参数传入sqlite调用的函数中去,这里我们向student表中插入两条记录
- //创建一条数据库插入语句,向student表中添加一条记录,name为jack,sex为male,class为ios
- const char *insertSql1 = "insert into student(id, name, sex, class) values (1, 'jack', 'male', 'ios')";
- const char *insertSql2 = "insert into student(id, name, sex, class) values (2, 'james', 'male', 'ios')";
- //调用sqlite3_exec函数,传入我们写好的sql语句,返回值是SQLITE_OK时表示操作执行成功,否则失败
- if (sqlite3_exec(database, insertSql1, NULL, NULL, &err) != SQLITE_OK) {
- NSLog(@"insert record failed!");
- }
- if (sqlite3_exec(database, insertSql2, NULL, NULL, &err) != SQLITE_OK) {
- NSLog(@"insert record failed!");
- }
那么我们可以用sqlite可视化工具打开我们的数据库查看这两条记录是否插入到表中了:
2.更新数据
- //创建数据库更新语句,将student表中id=1的记录的name字段值改为kobe
- const char *updateSql = "update student set name = 'kobe' where id = 1";
- //执行更新操作
- if (sqlite3_exec(database, updateSql, NULL, NULL, &err) != SQLITE_OK) {
- NSLog(@"update record failed!");
- }
更新后id=1的记录的name已经更新为kobe了:
3.删除数据
- //创建一条数据库删除语句,删除student表中所有id=1的记录
- const char *deleteSql = "delete from student where id = 1";
- //执行删除操作
- if (sqlite3_exec(database, deleteSql, NULL, NULL, &err) != SQLITE_OK) {
- NSLog(@"delete row failed!");
- }
删除id=1的记录后表中数据:
4.查询数据
sqlite3_exec也可以用来执行查询的sql语句,可是由于返回值是int型,所以需要我们传入一个回调函数,回调函数需要我们自己实现,回调函数必须定义为下面函数的类型:
typedef int (*sqlite3_callback)(void*,int,char**, char**);
- //sqlite3_exec执行select语句时需要的回调函数,第一个参数是sqlite3_exec中传入的void *参数,第二个参数是表中的字段个数,第三个参数是字段值,第四个参数是字段名,后两个参数都是字符串数组
- int myCallBack(void *para, int column_count, char **column_value, char **column_name) {
- for (int i = 0; i < column_count; i++) {
- NSLog(@"%s = %s", column_name[i], column_value[i]);
- }
- return 0;
- }
- //创建一条数据库查询语句,查询student表中所有数据
- //(查询特定记录时使用select from 表名 where 字段名 = 你想要查询的值)
- const char *selectSql = "select * from student";
- //执行查询操作,将定义好的回调函数myCallBack作为参数传入
- if (sqlite3_exec(database, selectSql, myCallBack, NULL, &err) != SQLITE_OK) {
- NSLog(@"select data failed!");
- }
5.二进制文件操作
用以上的方法加上sql语句操作,基本就可以应付大部分的数据库操作了,如果涉及到处理二进制文件的操作(例如图片) ,就需要使用到下面介绍的方法了:
sqlite 操作二进制数据需要用一个辅助的数据类型:sqlite3_stmt * 。
这个数据类型记录了一个“sql语句”。为什么我把 “sql语句” 用双引号引起来?因为你可以把 sqlite3_stmt * 所表示的内容看成是 sql语句,但是实际上它不是我们所熟知的sql语句。它是一个已经把sql语句解析了的、用sqlite自己的方式标记记录的内部数据结构。正因为这个结构已经被解析了,所以你可以往这个语句里插入二进制数据。
下面我们使用刚刚的select语句来简单了解一下使用过程:
- //声明 sqlite3_stmt * 类型变量
- sqlite3_stmt *statement;
- //把一个sql语句解析到statement结构里去,当prepare成功之后(返回值是SQLITE_OK),开始查询数据
- if (sqlite3_prepare_v2(database, selectSql, -1, &statement, NULL) == SQLITE_OK) {
- //sqlite3_step的返回值是SQLITE_ROW 时表示成功(不是SQLITE_OK )。
- while (sqlite3_step(statement) == SQLITE_ROW) {
- //使用sqlite3_column函数来返回数据库中查询到记录的各个字段值
- //第一个参数传入sqlite3_stmt *变量,第二个参数表示需要查询第几个字段(从0开始计算)
- NSLog(@"id = %d, name = %s, sex = %s, class = %s", sqlite3_column_int(statement, 0), sqlite3_column_text(statement, 1), sqlite3_column_text(statement, 2), sqlite3_column_text(statement, 3));
- }
- } else {
- NSLog(@"search data failed!");
- }
- //析构上面sqlite3_prepare_v2创建的准备语句
- sqlite3_finalize(statement);