sqlite3数据库之blob数据类型读写
sqlite3数据库之blob数据类型读写近期在开发过程中需要在数据库中保存二进制文件,于是去了解了一下sqlite3的数据类型,发现blob可以存储二进制数据。但是blob要如何操作呢?用什么语句呢?其实它是需要操作sqlite3库的一些底层API来实现。下面我们就来做实验。先上测试代码:#include "Wdebug.h"#include "sqlite/Wsqlite.h"#include
sqlite3数据库之blob数据类型读写
近期在开发过程中需要在数据库中保存二进制文件,于是去了解了一下sqlite3的数据类型,发现blob可以存储二进制数据。
但是blob要如何操作呢?用什么语句呢?
其实它是需要操作sqlite3库的一些底层API来实现。
下面我们就来做实验。
先上测试代码:
#include "Wdebug.h"
#include "sqlite/Wsqlite.h"
#include <stdlib.h>
#define TABLE_NAME "TEST_BLOB"
typedef struct {
TUint16 len;
void* pdata;
} TBlobData;
void PrintList (TBlobData data)
{
if (data.pdata) {
TUint8* pvalue = (TUint8*)data.pdata;
printf ("len(%d):", data.len);
for (int i = 0; i < data.len; i++) {
printf ("%d ", pvalue[i]);
}
printf ("\n");
} else {
printf ("data is NULL!\n");
}
}
int main (int argc, char* argv[])
{
TBlobData data;
struct TsqlCtrl* This = CreateSqlCtrl("database.db");
if (This) {
TsqlCtrlDelTable (This, TABLE_NAME);
if (TsqlCtrlExec(This, "CREATE TABLE "TABLE_NAME" ("
"id INTEGER PRIMARY KEY"
",NAME TEXT NOT NULL"
",age INTEGER"
",address TEXT"
",Data1 BLOB"
");"
, NULL, NULL) != WSQLITE_TRUE) {
Werr ("创建表失败!");
}
TsqlCtrlExec (This, "INSERT INTO "TABLE_NAME" (id, NAME, age, address) VALUES (NULL, '张三', 19, '北京')", NULL, NULL);
TsqlCtrlExec (This, "INSERT INTO "TABLE_NAME" (id, NAME, age, address) VALUES (NULL, '李四', 7, '上海')" , NULL, NULL);
TsqlCtrlExec (This, "INSERT INTO "TABLE_NAME" (id, NAME, age, address) VALUES (NULL, '刘五', 39, '广州')", NULL, NULL);
TUint8 value[10];
for (int i = 0; i < sizeof (value); i++) {
value[i] = i + 1;
}
TsqlCtrlWriteBlob (This, "UPDATE "TABLE_NAME" SET Data1=? WHERE NAME='张三'", value, sizeof (value), 1);
for (int i = 0; i < sizeof (value); i++) {
value[i] = i + 5;
}
TsqlCtrlWriteBlob (This, "UPDATE "TABLE_NAME" SET Data1=? WHERE NAME='李四'", value, sizeof (value) - 3, 1);
for (int i = 0; i < sizeof (value); i++) {
value[i] = i + 20;
}
TsqlCtrlWriteBlob (This, "INSERT INTO "TABLE_NAME" (id, NAME, age, address, Data1) VALUES (NULL, '老六', 24, '深圳', ?)", value, sizeof (value), 1);
data = TsqlCtrlReadBlob (This, "SELECT Data1 FROM "TABLE_NAME" WHERE NAME='张三'", 0, value, sizeof (value));
PrintList (data);
data = TsqlCtrlReadBlob (This, "SELECT Data1 FROM "TABLE_NAME" WHERE NAME='李四'", 0, value, sizeof (value));
PrintList (data);
data = TsqlCtrlReadBlob (This, "SELECT id FROM "TABLE_NAME" WHERE NAME='李四'", 0, value, sizeof (value));
PrintList (data);
data = TsqlCtrlReadBlob (This, "SELECT Data1 FROM "TABLE_NAME" WHERE NAME='老六'", 0, value, sizeof (value));
PrintList (data);
TsqlCtrlDestroy(This);
}
}
TsqlCtrl是我封装的一个结构体。
首先先创建一个带blob数据类型的表。然后再插入几条信息。通过UPDATE和INSERT语句修改和添加blob数据。blob数据部分用?代替。
底层实现代码如下:
sql::Ret sql::sqlite::WriteBlob (const char* const sqlCmd, void* pdata, const TUint32 length, const TUint8 column_index)
{
sql::Ret ret = sql::False;
sqlite3_stmt* stmt = NULL;
if (!pdata || length == 0) {
return sql::False;
}
std::lock_guard<std::mutex> guard(this->mtx);
if(sqlite3_prepare(this->db, sqlCmd, -1, &stmt, NULL)==SQLITE_OK) {
sqlite3_bind_blob(stmt, column_index, pdata, length, NULL);
if (sqlite3_step(stmt)==SQLITE_DONE) {
ret = sql::True;
}
sqlite3_finalize(stmt);
}
return ret;
}
TBlobData sql::sqlite::ReadBlob (const char* const sqlCmd, const TUint8 column_index, void* pdata, const TUint32 MaxSize)
{
TBlobData Data;
sqlite3_stmt* stmt = NULL;
Data.len = 0;
Data.pdata = nullptr;
if (pdata&&MaxSize==0) {
return Data;
}
std::lock_guard<std::mutex> guard(this->mtx);
if(sqlite3_prepare(this->db, sqlCmd, -1, &stmt, NULL) == SQLITE_OK) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
const void* val = sqlite3_column_blob(stmt, column_index);
Data.len = sqlite3_column_bytes(stmt, column_index);
if (pdata) {
Data.pdata = pdata;
if (Data.len > MaxSize) Data.len = MaxSize;
} else {
Data.pdata = malloc (Data.len);
}
if (Data.pdata) {
memcpy (Data.pdata, val, Data.len);
} else {
Data.len = 0;
}
}
sqlite3_finalize(stmt);
}
return Data;
}
1、写入过程
这是一条指令修改一个blob数据的代码,先用sqlite3_prepare函数解析sql语句获得sqlite3_stmt。
sqlite3_prepare
第一个参数为数据库句柄
第二个参数为sql语句
第三个参数为sql语句的最大字节长度
第四个参数为获取到的声明句柄sqlite3_stmt*
第五个参数为指向sql语句中未使用部分的指针
然后用sqlite3_bind_blob绑定blob数据到sqlite3_stmt
sqlite3_bind_blob
第一个参数为sqlite3_stmt句柄
第二个参数为?所在列号
第三个参数为需要写入的blob数据起始地址
第四个参数为写入长度
第五个参数类型为void()(void),暂时还不清楚,应该是回调函数指针,置为空即可。
第三步就是用sqlite3_step执行sqlite3_stmt句柄内容;执行完用sqlite3_finalize销毁句柄即可。
注意:
在写入时sqlite3_bind_blob的第二个参数为?所在的列号是从1开始数的,并非从0开始!具体可参考示例代码。
2、读取过程
也是先通过sqlite3_prepare函数解析sql语句获得句柄,然后通过sqlite3_step执行句柄内容获得查询结果。最后通过sqlite3_column_blob函数获得对应列的存储数据以及用sqlite3_column_bytes函数获得对应列的数据长度。最后用sqlite3_finalize释放句柄。
注意:
sqlite3_column_blob的第二个参数为列号,跟写入过程有所不同,这个是从0开始的,具体可参考测试代码。
以上测试代码的执行结果如下:
看第四行输出为len(1):50
由此可知
data = TsqlCtrlReadBlob (This, “SELECT id FROM “TABLE_NAME” WHERE NAME=‘李四’”, 0, value, sizeof (value));
这一行查询的结果是id的值,50为字符2的ASCII码,而李四这条记录就是在第二行。
于是sqlite3_column_blob和sqlite3_column_bytes的第二个参数如何确定就显而易见了。
看TsqlCtrlWriteBlob (This, “UPDATE “TABLE_NAME” SET Data1=? WHERE NAME=‘李四’”, value, sizeof (value) - 3, 1);和TsqlCtrlWriteBlob (This, “INSERT INTO “TABLE_NAME” (id, NAME, age, address, Data1) VALUES (NULL, ‘老六’, 24, ‘深圳’, ?)”, value, sizeof (value), 1);的执行结果,显然插入与更新的时候?的列号是不受其他字段影响的,只计算?是在所有?中的第几列,且是从1开始递增。
如一次读写多个blob数据,则在在sqlite3_step之前绑定多个数据或读出时读出多个数据即可。
以一次读写两个blob数据类型为例:
sql::Ret sql::sqlite::WriteBlob (const char* const sqlCmd, TBlobData data)
{
sql::Ret ret = sql::False;
sqlite3_stmt* stmt = NULL;
std::lock_guard<std::mutex> guard(this->mtx);
if(sqlite3_prepare(this->db, sqlCmd, -1, &stmt, NULL)==SQLITE_OK) {
// 第二个参数data.GetData(i)->index代表sql语句中?的位置(相对于VALUES中的位置从0开始,如:VALUES(1, ?),则问号的位置为1)
sqlite3_bind_blob(stmt, data.column[0], data.pdata[0], data.len[0], NULL);
sqlite3_bind_blob(stmt, data.column[1], data.pdata[1], data.len[1], NULL);
if (sqlite3_step(stmt)==SQLITE_DONE) {
ret = sql::True;
}
sqlite3_finalize(stmt);
}
return ret;
}
TBlobData sql::sqlite::ReadBlob (const char* const sqlCmd, TBlobData data)
{
TBlobData errdata;
sqlite3_stmt* stmt = NULL;
std::lock_guard<std::mutex> guard(this->mtx);
if(sqlite3_prepare(this->db, sqlCmd, -1, &stmt, NULL) == SQLITE_OK) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
TUint32 slen;
const void* val;
val = sqlite3_column_blob(stmt, data.column[0]);
slen = sqlite3_column_bytes(stmt, data.column[0]);
if (data.len[0] > slen) data.len[0] = slen;
memcpy (data.pdata[0], val, data.len[0]);
val = sqlite3_column_blob(stmt, data.column[1]);
slen = sqlite3_column_bytes(stmt, data.column[1]);
if (data.len[1] > slen) data.len[1] = slen;
memcpy (data.pdata[1], val, data.len[1]);
} else {
sqlite3_finalize(stmt);
return errdata;
}
sqlite3_finalize(stmt);
} else {
return errdata;
}
return data;
}
void main(int argc, char* argv[])
{
TBlobData data;
TBlobData data2;
struct TsqlCtrl* This = CreateSqlCtrl("database.db");
if (This) {
TsqlCtrlDelTable (This, TABLE_NAME);
if (TsqlCtrlExec(This, "CREATE TABLE "TABLE_NAME" ("
"id INTEGER PRIMARY KEY"
",NAME TEXT NOT NULL"
",age INTEGER"
",address TEXT"
",Data1 BLOB"
",Data2 BLOB"
");"
, NULL, NULL) != WSQLITE_TRUE) {
Werr ("创建表失败!");
}
TsqlCtrlExec (This, "INSERT INTO "TABLE_NAME" (id, NAME, age, address) VALUES (NULL, '张三', 19, '北京')", NULL, NULL);
TsqlCtrlExec (This, "INSERT INTO "TABLE_NAME" (id, NAME, age, address) VALUES (NULL, '李四', 7, '上海')" , NULL, NULL);
TsqlCtrlExec (This, "INSERT INTO "TABLE_NAME" (id, NAME, age, address) VALUES (NULL, '刘五', 39, '广州')", NULL, NULL);
TUint8 value[10];
TUint8 value2[10];
data2.len[0] = sizeof (value);
data2.len[1] = sizeof (value2);
data2.column[0] = 1;
data2.column[1] = 2;
data2.pdata[0] = value;
data2.pdata[1] = value2;
for (int i = 0; i < sizeof (value); i++) {
value[i] = i + 1;
value2[i] = i + 3;
}
TsqlCtrlWriteBlob (This, "UPDATE "TABLE_NAME" SET Data1=?,Data2=? WHERE NAME='张三'", data2);
for (int i = 0; i < sizeof (value); i++) {
value[i] = i + 5;
value2[i] = i + 9;
}
TsqlCtrlWriteBlob (This, "UPDATE "TABLE_NAME" SET Data1=?,Data2=? WHERE NAME='李四'", data2);
for (int i = 0; i < sizeof (value); i++) {
value[i] = i + 20;
value2[i] = i + 23;
}
TsqlCtrlWriteBlob (This, "INSERT INTO "TABLE_NAME" (id, NAME, age, address, Data1, Data2) VALUES (NULL, '老六', 24, '深圳', ?, ?)", data2);
data2.column[0] = 0;
data2.column[1] = 1;
data = TsqlCtrlReadBlob (This, "SELECT Data1,Data2 FROM "TABLE_NAME" WHERE NAME='张三'", data2);
PrintList (data);
data = TsqlCtrlReadBlob (This, "SELECT Data1,Data2 FROM "TABLE_NAME" WHERE NAME='李四'", data2);
PrintList (data);
data = TsqlCtrlReadBlob (This, "SELECT id,Data1 FROM "TABLE_NAME" WHERE NAME='李四'", data2);
PrintList (data);
data = TsqlCtrlReadBlob (This, "SELECT Data1,Data2 FROM "TABLE_NAME" WHERE NAME='老六'", data2);
PrintList (data);
TsqlCtrlDestroy(This);
}
}
以下为执行结果:
呀!不知不觉已经过了十点半了。。。粗略写写,后面完善了代码,我再上传我的数据库操作接口吧。得赶紧回家洗澡睡觉了。。。
完善的完整代码已上传:源代码下载入口
类使用说明及测试案例
基础篇
各API的使用说明将在后续陆陆续续发表博客进行帮助说明,源代码附带的注释也很详尽,通过注释也可轻松理解代码及框架思路等
更多推荐
所有评论(0)