Sqlite数据库使用简介以Windows下简单的引入数据库到C++项目为例

2024-08-02 14:37:03 浏览数 (1)

Sqlite是什么?

SQLite 是一个开源的嵌入式关系型数据库管理系统。它不是作为一个独立进程运行的服务,而是作为一个库直接链接到应用程序中。这意味着使用 SQLite 的应用程序可以随身携带其数据库,并且可以在不需要单独服务器管理的情况下操作数据。

Sqlite的下载-以引入源文件到项目为例

打开官网界面

https://www.sqlite.org/download.html

下载对应平台的源代码

  • windows下.zip后缀
  • linux下.tar.gz后缀

解压缩这四个文件到你想要引入的项目根目录

例如:

如果不考虑防止SQL注入以及加密之类的

其实只需要包含Sqlite3.h和Sqlite3.c两个文件在项目内就够了

数据库的创造和表的建立

代码语言:cpp复制
	// 1. 打开数据库 或者创建数据库
	sqlite3* db = nullptr;

	int ret = sqlite3_open("test.db", &db);
	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_open error 打开文件失败" << std::endl;
		return -1;
	}
	// 2. 创建表
	const char* sql = "create table if not exists user(id integer primary key autoincrement, name text, age integer)";
	char* errmsg = nullptr;
	ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_exec error: " << errmsg << std::endl;
		sqlite3_free(errmsg);  // 释放错误消息内存
		return -1;
	}

相关api函数介绍

打开数据库 或者创建数据库用sqlite3_open

在执行sql语句不需要返回结果集时用sqlite3_exec

sqlite3_exec

代码语言:cpp复制
int sqlite3_exec(
  sqlite3 *db,          /* An open database */
  const char *zSql,     /* SQL to be evaluated */
  void (*callback)(void*, int, char**, char**), /* Callback function */
  void *pArg,           /* Argument passed to callback */
  char **pzErrmsg       /* Error msg written here */
);

参数说明如下:

  • db: 这是已经打开的数据库连接指针。
  • zSql: 包含要执行的 SQL 命令的字符串。多个 SQL 命令可以用分号分隔。
  • callback: 当 SQL 命令产生结果集时调用的回调函数。如果 SQL 命令不生成结果集,则此参数可以为 NULL。
  • pArg: 传递给回调函数的任意类型的数据指针。
  • pzErrmsg: 如果发生错误,这个指针会被设置为指向一个包含错误消息的字符串。调用者负责释放这个字符串(通过 sqlite3_free())。

函数返回值:

  • SQLITE_OK: 成功执行 SQL 命令。
  • 其他错误代码: 如 SQLITE_ERROR, SQLITE_BUSY, SQLITE_MISUSE 等,表示某种类型的错误。

回调函数的原型如下:

  • void callback(void *data, int argc, char argv, char azColName);
  • data: 在 sqlite3_exec() 中传递的任意数据指针。
  • argc: 结果集中列的数量。
  • argv: 指向数组的指针,数组中的每个元素都是一个指向结果集中相应列的字符串的指针。
  • azColName: 指向数组的指针,数组中的每个元素都是一个指向结果集中相应列名称的字符串的指针。 其使用例在下面的增删改查中

需要返回结果集时

需要返回结果集时一般用sqlite3_prepare_v2() 和 sqlite3_step()和sqlite3_finalize()结合使用

这里就不提供详细解释了,只是提供一个使用例,根据用户姓名和密码搜索用户ID

代码语言:cpp复制
int DBOperate::GetUserID(const char* name, const char* pwd)
{
	std::stringstream sql;
	sql << "SELECT UserID FROM Users WHERE Username = '" << name << "' AND Password = '" << pwd << "';";

	char* errMsg = nullptr;
	sqlite3_stmt* stmtUser;
	int rc = sqlite3_prepare_v2(_db, sql.str().c_str(), -1, &stmtUser, nullptr);
	if (rc != SQLITE_OK) {
		std::cerr << "Failed to prepare statement: " << sqlite3_errmsg(_db) << std::endl;
		return -1; // Return an error code or value indicating failure
	}

	int userId = -1;
	if (sqlite3_step(stmtUser) == SQLITE_ROW) {
		userId = sqlite3_column_int(stmtUser, 0);
	}
	else {
		std::cerr << "No matching user found." << std::endl;
	}

	sqlite3_finalize(stmtUser);

	return userId;
}

Sqlite的简单增删改查使用演示

以下使用例均建立在sqlite3_exec的使用下

代码语言:cpp复制
	// 3. 插入数据
	sql = "insert into user(name, age) values('张三', 18)";
	ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_exec error: 插入数据失败" << errmsg << std::endl;
		sqlite3_free(errmsg);  // 释放错误消息内存
		return -1;
	}

代码语言:cpp复制
	// 5.删除数据
	sql = "delete from user where age < 20";
	ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_exec error: 删除数据失败" << errmsg << std::endl;
		sqlite3_free(errmsg);  // 释放错误消息内存
		return -1;
	}

代码语言:cpp复制
	// 4.更新数据
	sql = "update user set age = 20 where name = '张三'";
	ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_exec error: 更新数据失败" << errmsg << std::endl;
		sqlite3_free(errmsg);  // 释放错误消息内存
		return -1;
	}

当我们想要把数据打印出来时,就需要使用sqlite3_exec里的回调函数参数了

代码语言:cpp复制
    int select_callback(void* ptr, int count, char** row, char** rowName)
    {
	std::vector<std::string>* pv = (std::vector<std::string> *) ptr;
	std::string str = "";
	for (int i = 0; i < count;   i)
	{
		//        std::cout << row[i] << " = " << rowName[i] << std::endl;
		str  = row[i];
		str  = " = ";
		str  = rowName[i];
		str  = "|||";
	}

	pv->push_back(str);
	return 0;
    }



	sql = "select * from user";
	std::vector<std::string> v;
	ret = sqlite3_exec(db, sql, select_callback, &v, &errmsg);

	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_exec error 查询数据失败" << errmsg << std::endl;
		return -1;
	}

    	for (auto& s : v)
	{
		std::cout << s << std::endl;
	}

全部代码

代码语言:cpp复制
#include <iostream>
#include "sqlite3.h"
#include <vector>
#include <string>

int select_callback(void* ptr, int count, char** row, char** rowName)
{
	std::vector<std::string>* pv = (std::vector<std::string> *) ptr;
	std::string str = "";
	for (int i = 0; i < count;   i)
	{
		//        std::cout << row[i] << " = " << rowName[i] << std::endl;
		str  = row[i];
		str  = " = ";
		str  = rowName[i];
		str  = "|||";
	}

	pv->push_back(str);
	return 0;
}

int main()
{
	// sqlite3是一个轻量级的数据库,不需要安装,只需要包含头文件即可

	// 1. 打开数据库 或者创建数据库
	sqlite3* db = nullptr;

	int ret = sqlite3_open("test.db", &db);
	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_open error 打开文件失败" << std::endl;
		return -1;
	}
	// 2. 创建表
	const char* sql = "create table if not exists user(id integer primary key autoincrement, name text, age integer)";
	char* errmsg = nullptr;
	ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_exec error: " << errmsg << std::endl;
		sqlite3_free(errmsg);  // 释放错误消息内存
		return -1;
	}


	// 3. 插入数据
	sql = "insert into user(name, age) values('张三', 18)";
	ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_exec error: 插入数据失败" << errmsg << std::endl;
		sqlite3_free(errmsg);  // 释放错误消息内存
		return -1;
	}

	// 4.更新数据
	sql = "update user set age = 20 where name = '张三'";
	ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_exec error: 更新数据失败" << errmsg << std::endl;
		sqlite3_free(errmsg);  // 释放错误消息内存
		return -1;
	}

	// 5.删除数据
	sql = "delete from user where age < 20";
	ret = sqlite3_exec(db, sql, nullptr, nullptr, &errmsg);
	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_exec error: 删除数据失败" << errmsg << std::endl;
		sqlite3_free(errmsg);  // 释放错误消息内存
		return -1;
	}

	// 6. 查询数据
	sql = "select * from user";
	std::vector<std::string> v;
	ret = sqlite3_exec(db, sql, select_callback, &v, &errmsg);

	if (ret != SQLITE_OK)
	{
		std::cout << "sqlite3_exec error 查询数据失败" << errmsg << std::endl;
		return -1;
	}

	for (auto& s : v)
	{
		std::cout << s << std::endl;
	}

	// 5. 关闭数据库
	sqlite3_close(db);

	return 0;
}

Sqlite3引入项目的简单登陆注册使用例

新建一个单例模式下的数据库类

关于单例模式的讲解可以看我之前的这篇文章

C 设计模式-单例模式讲解

https://cloud.tencent.com/developer/article/2439703

新建一个DBopreate.h头文件

代码语言:cpp复制
#pragma once
#include "sqlite3.h"
#include <iostream>
#include <string>
struct sqlite3;
struct sqlite3_stmt;
class DBOperate
{
private:
	//数据库文件存储路径
	std::string _path;
	// 打开数据库或者创造数据库
	sqlite3* _db; 
	//检查是否初始化过
	bool _isInitialized; 

public:
	static DBOperate& GetInstance();
	void Init(std::string path);
public:
	bool DBRegister(const char* name, const char* pwd);
	bool DBLogin(const char* name, const char* pwd);
	int	GetUserID(const char* name, const char* pwd);
private:
	DBOperate() : _db(nullptr), _isInitialized(false){};
	DBOperate(DBOperate const&) = delete;
	void operator=(DBOperate const&) = delete;
	~DBOperate() 
	{ 
		if (_db) 
			sqlite3_close(_db);
		_db = nullptr;
	}
};

新建一个DBopreate.cpp文件

代码语言:cpp复制
#include "DBOperate.h"

DBOperate& DBOperate::GetInstance()
{
	static DBOperate instance;
	return instance;
}

void DBOperate::Init(std::string path)
{
	if (_isInitialized)
		return;
	//数据库文件存储路径
	_path = path;
	// Open the database
	int rc = sqlite3_open(_path.c_str(), &_db);
	if (rc != SQLITE_OK) {
		std::cerr << "Can't open database: " << sqlite3_errmsg(_db) << std::endl;
		return;
	}

	// Create users table
	const char* sqlCreateUsersTable =
		"CREATE TABLE IF NOT EXISTS Users ("
		"UserID INTEGER PRIMARY KEY AUTOINCREMENT,"
		"Username TEXT UNIQUE NOT NULL,"
		"Password TEXT NOT NULL"
		");";

	// Create tokens table
	const char* sqlCreateTokensTable =
		"CREATE TABLE IF NOT EXISTS Tokens ("
		"UserID INTEGER NOT NULL,"
		"Token TEXT NOT NULL UNIQUE"
		");";

	// Execute SQL statements
	char* errMsg = nullptr;
	rc = sqlite3_exec(_db, sqlCreateUsersTable, nullptr, nullptr, &errMsg);
	if (rc != SQLITE_OK) {
		std::cerr << "SQL error: " << errMsg << std::endl;
		sqlite3_free(errMsg);
		return;
	}
	rc = sqlite3_exec(_db, sqlCreateTokensTable, nullptr, nullptr, &errMsg);
	if (rc != SQLITE_OK) {
		std::cerr << "SQL error: " << errMsg << std::endl;
		sqlite3_free(errMsg);
		return;
	}

	_isInitialized = true;
}

bool DBOperate::DBRegister(const char* name, const char* pwd)
{
	// Prepare and execute the INSERT statement
	std::stringstream sql;
	sql << "INSERT INTO Users (Username, Password) VALUES ('" << name << "', '" << pwd << "');";

	char* errMsg = nullptr;
	int rc = sqlite3_exec(_db, sql.str().c_str(), nullptr, nullptr, &errMsg);
	if (rc != SQLITE_OK) {
		std::cerr << "Failed to register user: " << errMsg << std::endl;
		sqlite3_free(errMsg);
		return false;
	}

	return true;
}

bool DBOperate::DBLogin(const char* name, const char* pwd)
{
	std::stringstream sql;
	sql << "SELECT UserID FROM Users WHERE Username = '" << name << "' AND Password = '" << pwd << "';";

	char* errMsg = nullptr;
	sqlite3_stmt* stmtUser;
	int rc = sqlite3_prepare_v2(_db, sql.str().c_str(), -1, &stmtUser, nullptr);
	if (rc != SQLITE_OK) {
		std::cerr << "Failed to prepare statement: " << sqlite3_errmsg(_db) << std::endl;
		return false;
	}
	return true;
}

int DBOperate::GetUserID(const char* name, const char* pwd)
{
	std::stringstream sql;
	sql << "SELECT UserID FROM Users WHERE Username = '" << name << "' AND Password = '" << pwd << "';";

	char* errMsg = nullptr;
	sqlite3_stmt* stmtUser;
	int rc = sqlite3_prepare_v2(_db, sql.str().c_str(), -1, &stmtUser, nullptr);
	if (rc != SQLITE_OK) {
		std::cerr << "Failed to prepare statement: " << sqlite3_errmsg(_db) << std::endl;
		return -1; // Return an error code or value indicating failure
	}

	int userId = -1;
	if (sqlite3_step(stmtUser) == SQLITE_ROW) {
		userId = sqlite3_column_int(stmtUser, 0);
	}
	else {
		std::cerr << "No matching user found." << std::endl;
	}

	sqlite3_finalize(stmtUser);

	return userId;
}

数据库类使用例子

在需要引入数据库交互的.cpp文件里包含数据库类的所在头文件

服务端完整的TcpSocket类代码我就不放了,这里采用cJSON进行CS之间的数据交互

cJSON在CS之间的数据交互可以参考我写的这篇文章

cJSON使用介绍以及如何利用cJSON在服务端和客户端中进行数据传输

代码语言:cpp复制
void TcpSocket::SC_LoginRespond(cJSON* root)
{
	cJSON* username = cJSON_GetObjectItem(root, "username");
	cJSON* password = cJSON_GetObjectItem(root, "password");
	if (username == nullptr || username->type != cJSON_String && password == nullptr || password->type != cJSON_String)
	{
		Close();
		return;
	}

	cJSON* sc_login = cJSON_CreateObject();

	std::string strUsername = username->valuestring;
	std::string strPassword = password->valuestring;

	bool loginFlag = DBOperate::GetInstance().DBLogin(strUsername.c_str(), strPassword.c_str());

	if (loginFlag)
	{
		// 用户名密码正确,登录成功
		cJSON_AddStringToObject(sc_login, "cmd", "SC_Login");
		cJSON_AddNumberToObject(sc_login, "result", 1);
		cJSON_AddStringToObject(sc_login, "msg", "Login Success");
		cJSON* player = cJSON_CreateObject();
		std::string token = DBOperate::GetInstance().DBGetToken(_userId);
		cJSON_AddNumberToObject(player, "playerId", _userId);
		cJSON_AddStringToObject(player, "token", token.c_str());
		cJSON_AddStringToObject(player, "username", username->valuestring);
		cJSON_AddStringToObject(player, "password", password->valuestring);
		cJSON_AddItemToObject(sc_login, "player", player);

	}
	else
	{
		// 用户名或者密码错误
		cJSON_AddStringToObject(sc_login, "cmd", "SC_Login");
		cJSON_AddNumberToObject(sc_login, "result", 0);
		cJSON_AddStringToObject(sc_login, "msg", "User Or Password Failed");
	}


	std::string msg = cJSON_Print(sc_login);
	cJSON_Delete(sc_login);

	SendData(msg.c_str(), msg.size());
}

void TcpSocket::SC_RegisterRespond(cJSON* root)
{
	//cJSON *nike = cJSON_GetObjectItem(root, "nike");
	//cJSON *username = cJSON_GetObjectItem(root, "username");
	cJSON* username = cJSON_GetObjectItem(root, "username");
	cJSON* password = cJSON_GetObjectItem(root, "password");
	// password  username

	if (username == nullptr || password == nullptr ||
		username->type != cJSON_String || password->type != cJSON_String)
	{
		Close();
		return;
	}


	cJSON* sc_register = cJSON_CreateObject();

	//验证是否注册成功
	bool registerFlag = DBOperate::GetInstance().DBRegister(username->valuestring, password->valuestring);

	if (registerFlag)
	{
	cJSON_AddStringToObject(sc_register, "cmd", "SC_Register");
	cJSON_AddStringToObject(sc_register, "username", username->valuestring);
	cJSON_AddStringToObject(sc_register, "password", password->valuestring);
	cJSON_AddNumberToObject(sc_register, "result", 1);
	cJSON_AddStringToObject(sc_register, "msg", "Register Success");
	}
	else
	{
	// 用户名已存在,注册失败
	cJSON_AddStringToObject(sc_register, "cmd", "SC_RegisterFailed");
	cJSON_AddStringToObject(sc_register, "msg", "Register Failed");
	}

	char* msg = cJSON_Print(sc_register);

	cJSON_Delete(sc_register);

	SendData(msg, strlen(msg));

	free(msg);

}

0 人点赞