1. CppSQLite 库
SQLite 是一个用c写的超轻量级的开源数据库;
CppSQLite3 是对SQLite进行二次封装后的C++类, 使用时需要依赖 sqlite3 库及 sqlite3.h;
在 windows 环境下使用这个类的时候,你需要确保几件事情:
1. 你要下载下面 5 个文件。 https://github.com/neosmart/CppSQLite
CppSQLite3.h 和CppSQLite3.cpp
sqlite3.h、sqlite3.lib 和 sqlite3.dll (windows环境)
2. 工程中引入 sqlite3.lib,最后要将CppSQLite3.h 和CppSQLite3.cpp添加到工程中
2. 制作 CppSQLite3 动态库
// 制作 CppSQLite3 动态库
#ifdef XXXLIB_EXPORTS
# define XXX_API __declspec(dllexport)
#else
# define XXX_API __declspec(dllimport)
#endif
添加导出宏 XXX_API 及 [改进版]新增的 5 个函数
// CppSQLite3.h
//1. 类名前面增加导出符号 XXX_API
class XXX_API CppSQLite3Exception
class XXX_API CppSQLite3Buffer
class XXX_API CppSQLite3Binary
class XXX_API CppSQLite3Query
class XXX_API CppSQLite3Table
class XXX_API CppSQLite3Statement // 这个是预编译,防注入用的
class XXX_API CppSQLite3DB
// 2. CppSQLite3DB 类中增加 5 个函数
const char* get_errormsg();
int get_errorcode();
unsigned int op_begin(const char *src_dbfile,const char *asdb);
unsigned int op_append(const char *src_table,const char *dst_table,const char *key,const char *asdb);
unsigned int op_end(const char *asdb);
// 下面是对应的函数实现, 在 CppSQLite3.cpp 中增加
const char* CppSQLite3DB::get_errormsg() {
return sqlite3_errmsg(mpDB);
}
int CppSQLite3DB::get_errorcode() {
return sqlite3_errcode(mpDB);
}
//
unsigned int CppSQLite3DB::op_begin(const char *src_dbfile,const char *asdb)
{
// TODO: 判断 srcdbfile 是否文件存在
char sql[512];
sprintf_s(sql,sizeof(sql),"detach database %s;",asdb);
execDML(sql); // 分离数据库
sprintf_s(sql, sizeof(sql),"attach DataBase '%s' as %s;",src_dbfile, asdb);
int iError = execDML(sql); // 附加数据库
if ((iError) && (iError == get_errorcode()) ) {
return -1;
}
iError = execDML("begin transaction;");
return 0;
}
unsigned int CppSQLite3DB::op_append(const char *src_table, const char *dst_table, const char *key, const char *asdb)
{
if (!tableExists(dst_table)) {
return 0;
}
int iError = 0;
char sql[512];
if (key)
{
sprintf_s(sql,sizeof(sql),"delete from %s where %s.%s in (select %s from %s.%s);",dst_table, dst_table,key, key, asdb, src_table);
iError = execDML(sql);
}
sprintf_s(sql,sizeof(sql),"insert into %s select A.* from %s.%s as A ;", dst_table, asdb, src_table);
iError = execDML(sql);
if (iError < 0) {
return 0;
}
return iError;
}
unsigned int CppSQLite3DB::op_end(const char *asdb)
{
char sql[64];
sprintf_s(sql,sizeof(sql),"detach database %s;",asdb); // 分离数据库
int iError = execDML(sql);
iError = execDML("commit transaction;");
if (iError < 0) {
return 0;
}
return iError;
}
3. sqlite 处理数据很慢的原因
sqlite在没有显式使用事务的时候会为每条 insert 都使用事务操作,而
sqlite数据库是以文件的形式存在磁盘中,就相当于每次访问时都要打开一次文件,
如果对数据进行大量的操作,时间都耗费在I/O操作上,所以很慢。
解决方法:
显式使用事务的形式提交, 因为我们开始事务后,进行的大量操作的语句都保存在内存中,
当提交时才全部写入数据库,此时,数据库文件也就只用打开一次。
4. sqlite 处理单引号/双引号
错误处理方式:
//错误处理方式: 字符串拼接, 当 %s 本身包含 ' 或是 " 或是 一些注入攻击字符串时, 会出错.
CppSQLite3DB bsdb;
bsdb.open(file);
sprintf_s(dstsql,sizeof(dstsql),"insert into task_total (taskId,gdsId,taskName,taskBrief,taskDetail,beginTime,endTime) values (%llu,%u,'%s','%s','%s','%s','%s');",
(uint64)row[0], (DWORD)row[1], (const char*)row[2], (const char*)row[3], (const char*)row[4], (const char*)row[5],(const char*)row[6];
bsdb.execDML(dstsql);
正确处理方式:
//正确处理方式:借助 SQLite 的预编译 compileStatement 方法
char dstsql[1024]={0};
sprintf_s(dstsql, sizeof(dstsql), "INSERT INTO xxx (col1,col2,col3....) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);");
CppSQLite3Statement stmt = bsdb.compileStatement(dstsql);
char srcsql[1024]={0};
sprintf_s(srcsql,sizeof(srcsql),"SELECT **** FROM **** WHERE ****;");
mysqlpp::Query query = sqlconn->m_conn->query(srcsql);
mysqlpp::UseQueryResult res = query.use();
for (;res;)
{
mysqlpp::Row row = res.fetch_row();
if (!row) break;
//进行绑定
int i = 0;
for (;i<6; ++i)
stmt.bind(i+1, (const int)row[i]);// int
for (;i<8; ++i)
stmt.bind(i+1, (const double)row[i]);// float/double
for (;i<16; ++i)
stmt.bind(i+1, (const char*)row[i]);// char*
stmt.execDML();
}
参考
mysqlpp 封装了C++对MySQL操作
CppSQLite3 封装了C++对SQLite操作
CppSQLite3Statement参数绑定
SQLite 基础教程 [事务]
SQLite 基础教程 [附加]
SQLite 基础教程 [分离]