如果没有 SQLite 的基础,我们只是从 Android 封装的 SQLite API 去学习的话,难免思路会受到限制。所以,我们还是需要老老实实从头开始学习 SQLite. 当我们有一身的 SQLite 武功之后,再去看 Android 的封装,就能更清楚如何发挥 SQLite 的特长。
SQLite 的核心只有一个 c 文件,访问的 db 也存在一个文件当中。所以,我们完全可以把它嵌入到另外一个程序中。
在 mac 上,可以通过 Homebrew 来安装。安装之后,我们就可以用 sqlite3 的 API 来写代码了。
我们找个网上找到的最简单的打开关闭 SQLite 数据库的例子:
- #include <stdio.h>
- #include <sqlite3.h>
- #include <stdlib.h>
- intmain(intargc,char*argv[])
- {
- sqlite3 *db;char*zErrMsg =0;intrc;
- rc = sqlite3_open("contacts.db", &db);if(rc)
- {fprintf(stderr,"Can't open database: %s\n", sqlite3_errmsg(db));exit(0);
- }else{fprintf(stderr,"Opened database successfully\n");
- }
- sqlite3_close(db);
- }
我们先不管它是什么意思,先编译一下试试:
- gcc-otest_sqlite test.c-lsqlite3
然后运行一下吧,需要本地有个叫 contacts.db 的数据库。
- ./test_sqlite
输出为:
- Opened database successfully
从上面的例子,我们可以学习到两个容易理解的 API: sqlite3_open 和 sqlite3_close.
有了能运行的环境之后,我们就来看看 SQLite 数据库引擎的结构吧:
从这张官方图上,我们可以看到,除了工具和测试代码之外,SQLite 的核心部分分为三部分:核心,编译器和后端。
核心部分就是对 SQL 命令的处理的部分,它通过编译器来编译成 VDBE(Virtual Database Engine)能执行的代码。 后端是真正对数据库进行操作的部分,包括 B - 树的查找结构等。
喜欢划重点的同学注意啦,重点来了:调用 SQLite3 数据库的代码优化的第一个点就是将编译好的字节码保存起来,下次用的时候直接调用。 这么重要的功能,SQLite3 API 中当然有提供,这就是后面我们会大量学习使用的 sqlite3_prepare 和 sqlite3_prepare_v2 函数。 Android 对此也有同样的封装,提供了 SQLiteStatement 来实现预编译代码的保存。
有同学问了,我的 SQL 语句并不是一成不变的,语句中的参数经常改变,这样的话,编译出来的代码就没有用了啊? 这在 SQLite3 的设计中当然是有考虑到的,编译好的语句,是可以支持参数的。我们首先使用 sqlite3_prepare_v2 编译,然后再通过 sqlite3_bind_* 函数来绑定参数。下次如果换了参数,先调用 sqlite3_reset 清除掉绑定信息,然后再重新用 sqlite3_bind_* 来做绑定新参数,就可以了。
一个调用 sqlite3 实现数据库操作的功能可以用下面的步骤来套用: 1. 根据业务需求,构造 sql 语句 2. 调用 sqlite3_prepare_v2 函数来编译 sql 语句 3. 如果有参数,调用 sqlite3_bind_* 函数来绑定参数 4. 调用 sqlite3_step 函数来执行一次 sql 操作,直至所有操作都完成 5. 下次再使用第 2 步编译出来的语句时,调用 sqlite3_reset 函数清理参数。然后重复第 3 步的操作 6. 最后,调用 sqlite3_finalize 来销毁预编译语句
下面我们直接开始实操,首先先举个 select 的例子。 我们以 Android 中联系人数据库为例,取其中的 calls 表的简化版:
- CREATE TABLEcalls (
- _idINTEGER PRIMARY KEYAUTOINCREMENT,
- sourceid TEXT,numberTEXT
- ...
- );
虽然字段很多,我们就关注 id 和号码就好。
我们先来一半,我们选_id 和 number 这两列,然后看看返回的数据中是不是两列: 核心代码如下:
- rc = sqlite3_prepare_v2(db, sql_select_caller, -1, &stmt, &tail);
- rc = sqlite3_step(stmt);intncols = sqlite3_column_count(stmt);printf("The column counts of calls is:%d\n", ncols);
- sqlite3_finalize(stmt);
完整版的代码,便于大家实验:
- #include <stdio.h>
- #include <sqlite3.h>
- #include <stdlib.h>
- intmain(intargc,char*argv[])
- {
- sqlite3 *db;char*zErrMsg =0;intrc;const char*sql_select_caller ="select _id, number from calls";
- sqlite3_stmt *stmt;const char*tail;
- rc = sqlite3_open("contacts.db", &db);if(rc)
- {fprintf(stderr,"Can't open database: %s\n", sqlite3_errmsg(db));exit(0);
- }else{fprintf(stderr,"Opened database successfully\n");
- }//select _id, number from callsrc = sqlite3_prepare_v2(db, sql_select_caller, -1, &stmt, &tail);
- rc = sqlite3_step(stmt);intncols = sqlite3_column_count(stmt);printf("The column counts of calls is:%d\n", ncols);
- sqlite3_finalize(stmt);
- sqlite3_close(db);
- }
下面我们直接调用 sqlite3_step 去读每一条记录,增加下面一段:
- while(rc == SQLITE_ROW){printf("calls ID=%d,\t",sqlite3_column_int(stmt,0));printf("number=%s\n",sqlite3_column_text(stmt,1));
- rc = sqlite3_step(stmt);
- }
如果 sqlite3_step 返回的结果是 SQLITE_ROW,说明这一次执行取到了一条符合条件的记录。每次取一条记录。
完整代码如下:
- #include <stdio.h>
- #include <sqlite3.h>
- #include <stdlib.h>
- intmain(intargc,char*argv[])
- {
- sqlite3 *db;char*zErrMsg =0;intrc;const char*sql_select_caller ="select _id, number from calls";
- sqlite3_stmt *stmt;const char*tail;
- rc = sqlite3_open("contacts.db", &db);if(rc)
- {fprintf(stderr,"Can't open database: %s\n", sqlite3_errmsg(db));exit(0);
- }else{fprintf(stderr,"Opened database successfully\n");
- }//select _id, number from callsrc = sqlite3_prepare_v2(db, sql_select_caller, -1, &stmt, &tail);
- rc = sqlite3_step(stmt);intncols = sqlite3_column_count(stmt);printf("The column counts of calls is:%d\n", ncols);while(rc == SQLITE_ROW)
- {printf("calls ID=%d,\t", sqlite3_column_int(stmt,0));printf("number=%s\n", sqlite3_column_text(stmt,1));
- rc = sqlite3_step(stmt);
- }
- sqlite3_finalize(stmt);
- sqlite3_close(db);
- }
输出如下例:
- The column countsofcallsis:2calls ID=1,number=18600009876calls ID=2,number=18600019876calls ID=3,number=18600029876calls ID=4,number=18600039876calls ID=5,number=18600049876calls ID=6,number=18600059876calls ID=7,number=18600069876calls ID=8,number=18600079876calls ID=9,number=18600089876calls ID=10,number=18600099876
上面的例子是针对查询语句的,我们再举个非查询语句的例子。比如我们试个插入的例子。
- voidinsert_item(sqlite3 *db)
- {const char*sql_insert_sample ="insert or ignore into calls (_id,number) values (?1,?2);";
- sqlite3_stmt *stmt = NULL;const char*tail = NULL;intrc = sqlite3_prepare_v2(db, sql_insert_sample, -1, &stmt, &tail);
- sqlite3_bind_int(stmt,1,1000);
- sqlite3_bind_text(stmt,2,"01084993677",11, NULL);
- rc = sqlite3_step(stmt);if(rc == SQLITE_DONE)
- {printf("Last inserted row id=%ld\n", (long)sqlite3_last_insert_rowid(db));
- }else{printf("Insert failed!");
- }
- sqlite3_finalize(stmt);
- }
输出如下:
- Last inserted row id=0
上面,我们就查询和非查询两种情况,学习了如何使用 SQLite3 的 API。 剩下的工作主要就是构造 SQL 语句以及处理返回结果了。
来源: http://blog.csdn.net/lusing/article/details/70809361