如何在Native Visual C++ Application里面集成和使用SQLite

前言

笔者这几天研究SQLite, 然后想要再VS2012里面玩一下, 走了不少弯路, 在这里记录一下这些遇到的问题和走过的弯路, 以及自己对SQLite的一些想法和理解. 且记一笔, 以飨后人.

在Native Visual C++ Application使用SQLite的具体方法步骤

首先使用SQLite基本上有三种方法:  第一, 使用exe可执行文件, 也就是从官网下载二进制文件, 都不用安装, 就会打开一个命令行, 然后类似MySQL的那种交互式的方法就可以了. 第二, 使用官网提供的DLL, 这种需要配置一下; 第三, 直接使用源代码, 作为程序的一部分, 这种最简单, 基本上是零配置, 因为SQLite就是我们的程序的一部分了.  下面具体后两种方法使得我们可以在自己的C++程序里面使用SQLite.

I. 使用SQLite DLL进行集成:

1)  新建一个空的C++ Win32 Console application.

2) 从官网把Win32的DLL压缩包下载下来, 这里面只有两个文件: sqlite3.dll, sqlite3.def 把这两个文件考培到任意目录(比如D:\test)

3) 第二步中的这两个文件并不是我们程序中使用SQLite真正需要或者依赖的文件, 我们只是需要这两个文件生成sqlite3.lib (这个lib才是我们需要的), 那么如何生成这个sqlite3.lib着实让笔者走了不少弯路. 具体步骤和遇到的问题如下:

  1. 首先打开 “Developer Command Prompt for VS 2012″, change director (CD command) 到 VS 2012安装目录下的VC, 全路径应当是”安装目录\Microsoft Visual Studio 11.0\VC”
  2. 把” C:\Windows\System32“加入到系统的PATH变量里面(右键点击我的电脑然后选择高级属性设置)
  3. 回车调用  ”安装目录\Microsoft Visual Studio 11.0\VC>vcvarsall.bat” 或者 “安装目录\Microsoft Visual Studio 11.0\VC\bin>vcvars32.bat” 命令执行成功
  4. 当前目录切换到 “安装目录\Microsoft Visual Studio 11.0\VC\bin”, 使用LIB命令生成我们需要的.lib, 建议使用全路径, 具体的路径可根据自己的电脑配置进行修改, 我这里是这样的完整命令(假设当前目录已经在bin了):  LIB /DEF:D:\test\sqlite3.def    成功以后会在bin目录下生成两个文件sqlite3.lib和sqlite3.exp, 我们只需要sqlite3.lib

然后笔者解释一下这里的1-4步的由来以及可能遇到的问题. 是这样的: 如果你运气好, 或者VS 2012安装的顺顺利利, 其实你是不需要1-3步骤的, 只需要第4步. 笔者一开始也就是没有前三步, 直接开始做步骤4, 然后系统报错说“mspdb110.dll is missing” 于是Google一通, 说VS2012没有安装好, 解决办法就是步骤3, 使用vcvars32.bat或者vcvarsall.bat批处理进行修复, 让系统重新找到mspdb110.dll或者重新生成一个mspdb110.dll. 于是笔者又去做步骤3, 可是还是出错, 这个时候系统告诉我的错误是”Cannot determine the location of the VS Common Tools folder.“, 于是笔者再次Google一通, 找到原因是说新的VS2012有点问题, 设置系统变量PATH的时候不知道怎么的就是没有设置正确, 应该把” C:\Windows\System32″加入到PATH中去, 这样才能找到正确的VS Common Tools Folder. 于是才有了步骤2, 所以笔者的弯路是倒序进行的, 从4到3到2发现各种问题, 最后才能顺利到达步骤4成功生成sqlite3.lib.

4) 我们把生成的sqlite3.lib加入到我们的工程里面让Linker自己去找到函数体, 进入Project Properties -> Configuration Properties -> Linker ->Input-> Additional Dependencies, 我sqlite3.lib加进去.
5) 下载SQLite的源代码, 把里面的sqlite3.h 这个头文件放到工程的根目录下
6) 右键点击Header Files, 吧sqlite3.h加入到工程里面
7) 在源代码(比如包含main函数的source code)加入 #include “sqlite3.h” 就可以使用sqlite的函数了, 注意要使用双引号不要使用尖括号去include sqlite3.h

8) 在Source Files里面add a new item作为我们的main程序, 代码如下:

#include
#include "sqlite3.h"

int main(int argc, char* argv[])
{
	int rc;
	char *error;

	// Open Database
	std::cout << "Opening MyDb.db ..." << std::endl;
	sqlite3 *db; // db

	// if not exist, the db would be created
	rc = sqlite3_open("MyDb.db", &db);
	if (rc)
	{
		std::cerr << "Error opening SQLite3 database: " << sqlite3_errmsg(db) << std::endl << std::endl;
		sqlite3_close(db);
		return 1;
	}
	else
	{
		std::cout << "Opened MyDb.db." << std::endl << std::endl;
	}

	// Execute SQL
	std::cout << "Creating MyTable ..." << std::endl;
	const char *sqlCreateTable = "CREATE TABLE MyTable (id INTEGER PRIMARY KEY, value STRING);";
	rc = sqlite3_exec(db, sqlCreateTable, NULL, NULL, &error);
	if (rc)
	{
		std::cerr << "Error executing SQLite3 statement: " << sqlite3_errmsg(db) << std::endl << std::endl;
		sqlite3_free(error);
	}
	else
	{
		std::cout << "Created MyTable." << std::endl << std::endl;
	}

	// Execute SQL
	std::cout << "Inserting a value into MyTable ..." << std::endl;
	const char *sqlInsert = "INSERT INTO MyTable VALUES(NULL, 'A Value');";
	rc = sqlite3_exec(db, sqlInsert, NULL, NULL, &error);
	if (rc)
	{
		std::cerr << "Error executing SQLite3 statement: " << sqlite3_errmsg(db) << std::endl << std::endl;
		sqlite3_free(error);
	}
	else
	{
		std::cout << "Inserted a value into MyTable." << std::endl << std::endl;
	}

	// Display MyTable
	std::cout << "Retrieving values in MyTable ..." << std::endl;
	const char *sqlSelect = "SELECT * FROM MyTable;";
	char **results = NULL;
	int rows, columns;
	// the following api is not recommended
	sqlite3_get_table(db, sqlSelect, &results, &rows, &columns, &error);
	if (rc)
	{
		std::cerr << "Error executing SQLite3 query: " << sqlite3_errmsg(db) << std::endl << std::endl;
		sqlite3_free(error);
	}
	else
	{
		// Display Table
		for (int rowCtr = 0; rowCtr <= rows; ++rowCtr)
		{
			for (int colCtr = 0; colCtr < columns; ++colCtr)
			{
				// Determine Cell Position
				int cellPosition = (rowCtr * columns) + colCtr;

				// Display Cell Value
				std::cout.width(12);
				std::cout.setf(std::ios::left);
				std::cout << results[cellPosition] << " "; // results is just a string?
			}

			// End Line
			std::cout << std::endl;

			// Display Separator For Header
			if (0 == rowCtr)
			{
				for (int colCtr = 0; colCtr < columns; ++colCtr)
				{
					std::cout.width(12);
					std::cout.setf(std::ios::left);
					std::cout << "~~~~~~~~~~~~ ";
				}
				std::cout << std::endl;
			}
		}
	}
	sqlite3_free_table(results);

	// Close Database
	std::cout << "Closing MyDb.db ..." << std::endl;
	sqlite3_close(db);
	std::cout << "Closed MyDb.db" << std::endl << std::endl;

	// Wait For User To Close Program
	std::cout << "Please press any key to exit the program ..." << std::endl;
	std::cin.get();

	return 0;
}

源代码实例修改自这里

9) 点击运行就可以看到输出结果, 并且工程根目录下面出现了MyDb.db这个文件.

II. 直接把SQLite的源代码Source Code放到自己写的应用程序中, 在程序中使用SQLite:

1) 在VS 2012里面新建一个C++ Win32 Console application, 下一步下一步勾选empty project, 我们不需要所谓的预编译头什么的.

2) 从官网下载稳定版本的SQLite的Source Code, 笔者使用的是版本3.8.5, 解压缩, 把sqlite.h和sqlite.c拷贝到工程的根目录下面.

3) 在solution explorer里面分别右击Header Files和Source Files目录, Add exsiting items, 把刚刚拷贝过来的头文件和c源代码添加进去.

4) 接下来的步骤同上面的 7) –> 8) –> 9)

对SQLite的感觉:

  1. SQLite并没有自己独立起进程, 而是应用程序的一部分.

  2. SQLite更像是文件读写操作的替代, 感觉像是用SQL语言和相关API封装了文件读写操作(文件就是我们的数据库: XXX.db), 类似HIVE也是用SQL封装了分布式的文件系统数据读写操作等.

Written on July 15, 2014