SQLite3数据库Native C++封装类(Unicode)CppSQLite3U的初步认识与使用 by斜风细雨QQ:253786989 2012-02-12
(1)
http://www.sqlite.org/cvstrac/wiki?p=SqliteWrappers
从上面的网址可以找到对SQLite数据库的C API的各种语言的封装。包括c、c++、vb、c#.net、delphi、Lisp、D、Java、Javascript、Objective-C、Perl、PHP、Python、Ruby、Lua、Fortran等等。从这也可以看出SQLite数据库作为本地数据存储工具而得到的广泛应用。单单是“C++ Wrappers”也有几十种,常见的如:easySQLite、SQLite++、CppSQLite、CppSQLiteU等等。因为我通常是在Windows CE嵌入式操作系统上开发APP,而Windows CE操作系统只支持Unicode字符集,所以今天要学习和测试的是Unicode版“C++ Wrappers”:CppSQLiteU。
(2)
因为是在Windows CE操作系统上使用SQLite,所以在使用CppSQLiteU之前,要先编译针对Windows CE平台的SQLite3 DLL。这一步省了,我直接借用了别人已经编译好的。
(3)
下面是一个简单测试示例,主要是利用class CppSQLite3DB新建数据库、创建表、插入删除更新记录、使用事务、查询记录等等。
#define SQLITE3_FILE_NAME TEXT("sqlite.db3")
// 获取程序当前路径
void GetCurrentDirectory(CString &szPath)
{
TCHAR buf[256] = {0};
GetModuleFileName(NULL, buf, sizeof(buf)/sizeof(TCHAR));
szPath = buf;
szPath = szPath.Left(szPath.ReverseFind('\\') + 1);
}
CString strDbPath;
GetCurrentDirectory(strDbPath);
strDbPath += SQLITE3_FILE_NAME;
CppSQLite3DB db;
try
{
// 打开或新建一个数据库
db.open(strDbPath);
// 判断表名是否已经存在
if(!db.tableExists(TEXT("Customers")))
{
// 不存在,新建表Customers
db.execDML(TEXT("CREATE TABLE Customers(cust_name varchar(50) NOT NULL PRIMARY KEY, cust_address varchar(50));"));
}
// 插入1条记录
db.execDML(TEXT("INSERT INTO Customers VALUES('Village Toys', '200 Maple Lane');"));
// 插入1条记录
db.execDML(TEXT("INSERT INTO Customers VALUES('Kids Place', '333 South Lake Drive');"));
// 删除1条记录
db.execDML(TEXT("DELETE FROM Customers WHERE cust_name = 'Village Toys';"));
// 使用显示事务插入10条记录
TCHAR buf[256] = {0};
db.execDML(TEXT("BEGIN TRANSACTION;"));
for (int i = 0; i < 10; ++i)
{
memset(buf, 0, sizeof(buf));
wsprintf(buf, TEXT("INSERT INTO Customers VALUES ('Fun%dALL', '%d Sunny Place');"), i, i);
db.execDML(buf);
}
db.execDML(TEXT("COMMIT TRANSACTION;"));
// 更新1条记录
db.execDML(TEXT("UPDATE Customers SET cust_address = '4545 53rd Street' WHERE cust_name = 'Fun0ALL';"));
// 获取总记录条数
int nCount = db.execScalar(TEXT("SELECT COUNT(*) FROM Customers;"));
TCHAR szCount[50] = {0};
memset(szCount, 0, sizeof(szCount));
wsprintf(szCount, TEXT("Record count: %d."), nCount);
AfxMessageBox(szCount);
// 获取每一条记录
CppSQLite3Query q = db.execQuery(TEXT("SELECT * FROM Customers;"));
while (!q.eof())
{
AfxMessageBox(q.fieldValue(0));
q.nextRow();
}
// 销毁语句
q.finalize();
// 关闭数据库
db.close();
AfxMessageBox(TEXT("测试完成!"));
}
catch(CppSQLite3Exception ex)
{
AfxMessageBox(ex.errorMessage());
}
(4)
CppSQLite3U封装了4个类:CppSQLite3Exception、CppSQLite3DB、CppSQLite3Statement、CppSQLite3Query。
a) CppSQLite3Exception用于捕捉异常,errorCode以整数类形返回错误码,errorMessage以Unicode字符串类型返回错误码。
class CppSQLite3Exception
{
public:
… …
const int errorCode() { return mnErrCode; }
LPCTSTR errorMessage() { return mpszErrMess; }
static LPCTSTR errorCodeAsString(int nErrCode);
… …
};
通常用法如:
try
{
… …
}
catch(CppSQLite3Exception ex)
{
AfxMessageBox(ex.errorMessage());
}
b) CppSQLite3DB用于新建数据库,打开关闭数据库连接,执行DML、DDL,检索数据等。如:open打开数据库连接,close关闭数据库连接,tableExists检查某表是否存在,execDML执行SQL命令,execQuery检索记录,setBusyTimeout设置SQLite内部的busy handler的超时时间,SQLiteVersion返回SQLite版本。
class CppSQLite3DB
{
public:
… …
void open(LPCTSTR szFile);
void close();
bool tableExists(LPCTSTR szTable);
int execDML(LPCTSTR szSQL);
CppSQLite3Query execQuery(LPCTSTR szSQL);
int execScalar(LPCTSTR szSQL);
CString execScalarStr(LPCTSTR szSQL);
CppSQLite3Statement compileStatement(LPCTSTR szSQL);
sqlite_int64 lastRowId();
void interrupt() { sqlite3_interrupt(mpDB); }
void setBusyTimeout(int nMillisecs);
static const char* SQLiteVersion() { return SQLITE_VERSION; }
… …
};
c) CppSQLite3Statement也可以执行SQL命令,它最大的特点是支持参数绑定。对于参数绑定的用处,参考“SQlite数据库的C编程接口(四) 绑定参数(Bound Parameters) ——《Using SQlite》读书笔记”学习。该类导出的接口函数中,bind用于给SQL语句中的参数绑定指定的值,reset函数用于重置SQL语句,finalize用于销毁语句。
class CppSQLite3Statement
{
public:
… …
CppSQLite3Statement& operator=(const CppSQLite3Statement& rStatement);
int execDML();
CppSQLite3Query execQuery();
void bind(int nParam, LPCTSTR szValue);
void bind(int nParam, const int nValue);
void bind(int nParam, const double dwValue);
void bind(int nParam, const unsigned char* blobValue, int nLen);
void bindNull(int nParam);
void reset();
void finalize();
… …
};
用法如:
try
{
CppSQLite3DB db;
db.execDML("CREATE TABLE emp(empno int, empname char(20));");
db.execDML("BEGIN TRANSACTION;");
CppSQLite3Statement stmt = db.compileStatement("INSERT INTO emp VALUES (:empno, : empname);");
for (i = 0; i < nRowsToCreate; ++i)
{
char buf[16];
sprintf(buf, "EmpName%06d", i);
stmt.bind(":empno", i);
stmt.bind(":empname ", buf);
stmt.execDML();
stmt.reset();
}
db.execDML("COMMIT TRANSACTION; ");
}
catch (CppSQLite3Exception& e)
{
cerr << e.errorCode() << ":" << e.errorMessage() << endl;
}
d) CppSQLite3Query用于检索记录并读出结果。它导出的public接口函数大多都是SQLite3的C API _sqlite3_column_xxx函数的封装,通过该API函数读取结果集中某一行的某一列。nextRow函数用于检索下一行,eof用于判断是否到结果集的结尾。
class CppSQLite3Query
{
public:
… ….
int numFields();
int fieldIndex(LPCTSTR szField);
LPCTSTR fieldName(int nCol);
LPCTSTR fieldDeclType(int nCol);
int fieldDataType(int nCol);
LPCTSTR fieldValue(int nField);
LPCTSTR fieldValue(LPCTSTR szField);
int getIntField(int nField, int nNullValue=0);
int getIntField(LPCTSTR szField, int nNullValue=0);
double getFloatField(int nField, double fNullValue=0.0);
double getFloatField(LPCTSTR szField, double fNullValue=0.0);
LPCTSTR getStringField(int nField, LPCTSTR szNullValue=_T(""));
LPCTSTR getStringField(LPCTSTR szField, LPCTSTR szNullValue=_T(""));
const unsigned char* getBlobField(int nField, int& nLen);
const unsigned char* getBlobField(LPCTSTR szField, int& nLen);
bool fieldIsNull(int nField);
bool fieldIsNull(LPCTSTR szField);
bool eof();
void nextRow();
void finalize();
… …
};
SQLite3数据库Native C++封装类(Unicode)CppSQLite3U的初步认识与使用 by斜风细雨QQ:253786989 2012-02-12