VSCode之C++ SQLite3 SmartDB实现

news2024/12/23 18:19:20
  1. 背景
    承接上篇VSCode配置之C++ & SQLite3极简配置方案,参考《深入应用C++11: 代码优化与工程级应用》,基于VSCode+Cmake无痛实现SmartDB。
    GitHub路径: smartDB_tutorial

  2. 结果展示
    在这里插入图片描述

  3. 主要变化(与SmartDB1.3相比)
    1)使用最新的rapidjson库,弃用了GetKeyPtr(),GetKey()等方法,改用迭代器获取json对象,保证了不改变源码的大前提下,代码更简洁易懂

    bool ExcecuteJson(sqlite3_stmt *stmt, const rapidjson::Value& val)
    	{
    		int i = 0;
    		for(auto iter = val.MemberBegin(); iter != val.MemberEnd(); ++iter) {
    			auto key = iter->name.GetString();
    			if (SQLITE_OK != BindJsonValue(stmt, val[key], i + 1)){
    				return false;
    			}
    			i += 1;
    		}
    
    		m_code = sqlite3_step(stmt);
    		
    		sqlite3_reset(stmt);
    		return SQLITE_DONE == m_code;
    	}
    

    2)将namespace detail目录更改为对应的utils.hpp文件,合并detail的各个namesapce内容
    3)对smartDB的整个开发过程保留逐步测试代码,用于中间过程的调试(源码存在一定的编译问题,具体细节可参考文末的github仓库)

  4. 核心代码部分 (smartDB.hpp)
    1.1 smartDB.hpp

    	#pragma once
    	#include"SQLite3/sqlite3.h"
    	#include <string>
    	#include <unordered_map>
    	#include <vector>
    	#include <stdexcept>
    	#include <iostream>
    	#include <cctype>
    	#include <ctime>
    	#include <functional>
    	#include <unordered_map>
    	#include <memory>
    	#include <type_traits>
    	using namespace std;
    	
    	#include "utils.hpp"
    	#include "variant.hpp"
    	#include "non_copy.hpp"
    	#include"json_cpp.hpp"
    	
    		
    	typedef Variant<double, int, uint32_t, sqlite3_int64, char*, const char*, blob, string, nullptr_t> SqliteValue;
    	
    	class SmartDB : NonCopyable
    	{
    	public:
    		SmartDB() : m_jsonHelper(m_buf, m_code){}
    		explicit SmartDB(const string& fileName) : m_dbHandle(nullptr), m_statement(nullptr), m_isConned(false), m_code(0), m_jsonHelper(m_buf, m_code)
    		{
    			Open(fileName);
    		}
    	
    		~SmartDB()
    		{
    			Close();
    		}
    	
    		void Open(const string& fileName)
    		{
    			m_code = sqlite3_open(fileName.data(), &m_dbHandle);
    			if (SQLITE_OK == m_code)
    			{
    				m_isConned = true;
    			}
    		}
    	
    		bool Close()
    		{
    			if (m_dbHandle == nullptr)
    				return true;
    	
    			sqlite3_finalize(m_statement);
    			m_code = CloseDBHandle();
    			bool ret = (SQLITE_OK == m_code);
    			m_statement = nullptr;
    			m_dbHandle = nullptr;
    			return ret;
    		}
    	
    		bool IsConned() const
    		{
    			return m_isConned;
    		}
    	
    		bool Excecute(const string& sqlStr)
    		{
    			m_code = sqlite3_exec(m_dbHandle, sqlStr.data(), nullptr, nullptr, nullptr);
    			return SQLITE_OK == m_code;
    		}
    	
    	
    		template <typename... Args>
    		bool Excecute(const string& sqlStr, Args && ... args)
    		{
    			if (!Prepare(sqlStr))
    			{
    				return false;
    			}
    	
    			return ExcecuteArgs(std::forward<Args>(args)...);
    		}
    	
    		bool Prepare(const string& sqlStr)
    		{
    			m_code = sqlite3_prepare_v2(m_dbHandle, sqlStr.data(), -1, &m_statement, nullptr);
    			if (m_code != SQLITE_OK)
    			{
    				return false;
    			}
    	
    			return true;
    		}
    	
    	
    		template <typename... Args>
    		bool ExcecuteArgs(Args && ... args)
    		{
    			if (SQLITE_OK != detail::BindParams(m_statement, 1, std::forward<Args>(args)...))
    			{
    				return false;
    			}
    	
    			m_code = sqlite3_step(m_statement);
    	
    			sqlite3_reset(m_statement);
    			return m_code == SQLITE_DONE;
    		}
    	
    		template<typename Tuple>
    		bool ExcecuteTuple(const string& sqlStr, Tuple&& t)
    		{
    			if (!Prepare(sqlStr))
    			{
    				return false;
    			}
    	
    			m_code = detail::ExcecuteTuple(m_statement, detail::MakeIndexes<std::tuple_size<Tuple>::value>::type(), std::forward<Tuple>(t));
    			return m_code == SQLITE_DONE;
    		}
    	
    		bool ExcecuteJson(const string& sqlStr, const char* json)
    		{
    			rapidjson::Document doc;
    			doc.Parse<0>(json);
    			if (doc.HasParseError())
    			{
    				cout << doc.GetParseError() << endl;
    				return false;
    			}
    	
    			if (!Prepare(sqlStr))
    			{
    				return false;
    			}
    			return JsonTransaction(doc);
    		}
    	
    		template < typename R = sqlite_int64, typename... Args>
    		R ExecuteScalar(const string& sqlStr, Args&&... args)
    		{
    			if (!Prepare(sqlStr))
    				return GetErrorVal<R>();
    	
    			if (SQLITE_OK != detail::BindParams(m_statement, 1, std::forward<Args>(args)...))
    			{
    				return false;
    			}
    	
    			m_code = sqlite3_step(m_statement);
    	
    			if (m_code != SQLITE_ROW)
    				return GetErrorVal<R>();
    	
    			SqliteValue val = GetValue(m_statement, 0);
    			R result = val.Get<R>();// get<R>(val);
    			sqlite3_reset(m_statement);
    			return result;
    		}
    	
    		template <typename... Args>
    		std::shared_ptr<rapidjson::Document> Query(const string& query, Args&&... args)
    		{
    			if (!PrepareStatement(query, std::forward<Args>(args)...))
    				nullptr;
    	
    			auto doc = std::make_shared<rapidjson::Document>();
    	
    			m_buf.Clear();
    			m_jsonHelper.BuildJsonObject(m_statement);
    	
    			doc->Parse<0>(m_buf.GetString());
    	
    			return doc;
    		}
    	
    		bool Begin()
    		{
    			return Excecute(BEGIN);
    		}
    	
    		bool RollBack()
    		{
    			return Excecute(ROLLBACK);
    		}
    	
    		bool Commit()
    		{
    			return Excecute(COMMIT);
    		}
    	
    		int GetLastErrorCode()
    		{
    			return m_code;
    		}
    	
    	private:
    		int CloseDBHandle()
    		{
    			int code = sqlite3_close(m_dbHandle);
    			while (code == SQLITE_BUSY)
    			{
    				// set rc to something that will exit the while loop 
    				code = SQLITE_OK;
    				sqlite3_stmt * stmt = sqlite3_next_stmt(m_dbHandle, NULL);
    	
    				if (stmt == nullptr)
    					break;
    	
    				code = sqlite3_finalize(stmt);
    				if (code == SQLITE_OK)
    				{
    					code = sqlite3_close(m_dbHandle);
    				}
    			}
    	
    			return code;
    		}
    	
    		template <typename... Args>
    		bool PrepareStatement(const string& sqlStr, Args&&... args)
    		{
    			if (!Prepare(sqlStr))
    			{
    				return false;
    			}
    	
    			if (SQLITE_OK != detail::BindParams(m_statement, 1, std::forward<Args>(args)...))
    			{
    				return false;
    			}
    	
    			return true;
    		}
    	
    		bool JsonTransaction(const rapidjson::Document& doc)
    		{
    			for (int i = 0; i < doc.Size(); i++)
    			{	
    				if (!m_jsonHelper.ExcecuteJson(m_statement, doc[i]))
    				{
    					break;
    				}
    			}
    			
    			if (m_code != SQLITE_DONE) {
    				return false;
    			}
    			return true;
    		}
    	
    	private:
    	
    		SqliteValue GetValue(sqlite3_stmt *stmt, const int& index)
    		{
    			int type = sqlite3_column_type(stmt, index);
    			auto it = m_valmap.find(type);
    			if (it == m_valmap.end())
    				throw std::invalid_argument("can not find this type");
    	
    			return it->second(stmt, index);
    		}
    	
    		template<typename T>
    		typename std::enable_if <std::is_arithmetic<T>::value, T>::type
    			GetErrorVal()
    		{
    				return T(-9999);
    			}
    	
    		template<typename T>
    		typename std::enable_if <!std::is_arithmetic<T>::value, T>::type
    			GetErrorVal()
    		{
    				return "";
    			}
    	
    	private:
    		sqlite3* m_dbHandle;
    		sqlite3_stmt* m_statement;
    		bool m_isConned;
    		int m_code;
    	
    		//JsonBuilder m_jsonBuilder; 
    		detail::JsonHelper m_jsonHelper;
    		rapidjson::StringBuffer m_buf;
    	
    		static std::unordered_map<int, std::function <SqliteValue(sqlite3_stmt*, int)>> m_valmap;
    		
    	};
    	std::unordered_map<int, std::function <SqliteValue(sqlite3_stmt*, int)>> SmartDB::m_valmap =
    	{
    		{ std::make_pair(SQLITE_INTEGER, [](sqlite3_stmt *stmt, int index){return sqlite3_column_int64(stmt, index); }) },
    		{ std::make_pair(SQLITE_FLOAT, [](sqlite3_stmt *stmt, int index){return sqlite3_column_double(stmt, index); }) },
    		{ std::make_pair(SQLITE_BLOB, [](sqlite3_stmt *stmt, int index){return string((const char*) sqlite3_column_blob(stmt, index));/* SmartDB::GetBlobVal(stmt, index);*/ }) },
    		{ std::make_pair(SQLITE_TEXT, [](sqlite3_stmt *stmt, int index){return string((const char*) sqlite3_column_text(stmt, index)); }) },
    		{ std::make_pair(SQLITE_NULL, [](sqlite3_stmt *stmt, int index){return nullptr; }) }
    	};
    

1.2 smartDB.hpp (test)

	void test_smartDB() {
		SmartDB db;
	    db.Open("test.db");
	
		// test excecute
	    const string sqlcreat = "CREATE TABLE if not exists PersonTable(ID INTEGER NOT NULL, Name Text, Address Text);";
	    if (!db.Excecute(sqlcreat))
	        return;
	
	    const string sqlinsert = "INSERT INTO PersonTable(ID, Name, Address) VALUES(?, ?, ?);";
	    int id = 1;
	    string name = "smartDB";
	    string city = "www.database";
	
	    if (!db.Excecute(sqlinsert, id, name, city)){
			return;
		}
	
		// test tuple
		db.ExcecuteTuple(sqlinsert, std::forward_as_tuple(id+1, "smartCpp", "www.cpp"));
	
		// test json
	    auto state = db.ExcecuteJson(sqlinsert, "[{\"ID\":3,\"Name\":\"Xwell\",\"Address\":\"www.csdn\"}]");
	
		// test jsoncpp
		JsonCpp jcp;
		jcp.StartArray();
		for(int i = 0; i < 5; i++) {
			jcp.StartObject();
			jcp.WriteJson("ID", i+4);
			jcp.WriteJson("Name", "smartXwell");
			jcp.WriteJson("Address", "www.csdn");
			jcp.EndObject();
		}
		jcp.EndArray();
		state = db.ExcecuteJson(sqlinsert, jcp.GetString());
	
		// test json doc
		auto p = db.Query("select * from PersonTable");
		cout << p -> Size() << endl;
		cout << p -> GetString() << endl;
	}
	
	void test_time() {
		SmartDB db;
	    db.Open("test_time.db");
	    const string sqlcreat = "CREATE TABLE if not exists TestTimeTable(ID INTEGER NOT NULL, KPIID INTEGER, CODE INTEGER, V1 INTEGER, V2 INTEGER, V3 REAL, V4 TEXT);";
	    if (!db.Excecute(sqlcreat))
	        return;
	
	    clock_t start = clock();
	    const string sqlinsert = "INSERT INTO TestTimeTable(ID, KPIID, CODE, V1, V2, V3, V4) VALUES(?, ?, ?, ?, ?, ?, ?);";
	    bool ret = db.Prepare(sqlinsert);
	    db.Begin();	// begin task
	    for (size_t i = 0; i < 1000000; i++)
	    {
	        ret = db.ExcecuteArgs(i, i, i, i, i, i + 1.25, "it is a test");
	        if (!ret)
	            break;
	    }
	
	    if (ret)
	        db.Commit(); //commit task
	    else
	        db.RollBack(); //rollback task
		clock_t end = clock();
	    cout << "insert 1000000 time cost: " << (double)(end - start) / CLOCKS_PER_SEC << " s" << endl;
	
	    auto p = db.Query("select * from TestTimeTable");
		clock_t end2 = clock();
	    cout << "query all time cost: " << (double)(end2 - end) / CLOCKS_PER_SEC << " s" << endl;
	    cout << "size: " << p->Size() << endl;
	}

2 main.cpp

	#include<iostream>
	#include<string>
	// #include"sqlite.hpp"
	// #include"json_cpp.hpp"
	#include"smartDB.hpp"
	#include"utils.hpp"
	
	
	using namespace std;
	
	int main() {
	    // test_sqlite();
	    // test_json();
	    test_smartDB();
	    // test_time();
	    return 0;
	}
  1. 其余部分,参见github项目地址
  2. 附VSCode Windows下的调试配置launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "request": "launch",
            "name": "Debug",
            "program": "${workspaceFolder}/bin/Debug/main.exe", #debug exe path
            "args": [],
            "cwd": "${workspaceFolder}"
        }
    ]
}
  1. 小结
    1)结合代码,理解smartDB的工作原理,改进数据库增删改查的效率,“让接口容易被正确使用,不易被误用”
    2)step by step测试、调试中间结果,涉及到新的数据类型如Tuple, Variant, nonCopyable对象,type_traits, template等内容,还需进一加深理解和应用
    3)使用新的rapidjson库,查看源码,理解document部分的设计,从而替换原始方法(实际上,更改第三方库的头文件是不安全的,个人推荐非必要不更改
    4) VSCode的Debug配置,方便实际中遇到的问题调试,本次在ExcecuteJson部分花费了较长时间,定位并修复了Json数据可以绑定,但数据库无法更新的问题,具体方法参看个人github仓库。
    5) 代码最终上传路径:smartDB_tutorial

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/905051.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【【萌新的STM32学习-11】】

萌新的STM32学习-11 终于进入了正点原子的入门篇进行一些简单的设计 GPIO General Purpose Input Output 通用输入输出端口 简称GPIO 作用 &#xff1a;负责采集外部器件的信息或者控制外部器件工作&#xff0c;即输入输出 GPIO 的特点 1&#xff0c;不同型号&#xff0c;IO数…

HTML浪漫动态表白代码+音乐(附源码)(二)

一. 前言 七夕马上就要到了&#xff0c;为了帮助大家高效表白&#xff0c;下面再给大家加几款实用的HTML浪漫表白代码(附源码)背景音乐&#xff0c;可用于520&#xff0c;情人节&#xff0c;生日&#xff0c;求爱表白等场景&#xff0c;可直接使用。 来吧&#xff0c;展示&am…

第 7 章 排序算法(3)(选择排序)

7.6选择排序 7.6.1基本介绍 选择式排序也属于内部排序法&#xff0c;是从欲排序的数据中&#xff0c;按指定的规则选出某一元素&#xff0c;再依规定交换位置后达到排序的目的。 7.6.2选择排序思想: 选择排序&#xff08;select sorting&#xff09;也是一种简单的排序方法…

13.PV和PVC

文章目录 PV和PVC概念定义PV定义PVC创建静态PV和PVC创建动态PV和PVCNFS配置创建 Service Account和角色创建存储卷插件创建 StorageClass创建PVC创建pod 总结 PV和PVC 概念 PV 全称叫做 Persistent Volume&#xff0c;持久化存储卷。它是用来描述或者说用来定义一个存储卷的&…

1593页54万字电力行业数字化转型智慧电力云平台整体解决方案WORD

导读&#xff1a;原文《1593页54万字电力行业数字化转型智慧电力一体化监管云平台整体解决方案WORD》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 南方电网一体化…

基于Pytorch实现的声纹识别系统

前言 本项目使用了EcapaTdnn、ResNetSE、ERes2Net、CAM等多种先进的声纹识别模型&#xff0c;不排除以后会支持更多模型&#xff0c;同时本项目也支持了MelSpectrogram、Spectrogram、MFCC、Fbank等多种数据预处理方法&#xff0c;使用了ArcFace Loss&#xff0c;ArcFace loss…

TCP协议报文结构

TCP是什么 TCP&#xff08;传输控制协议&#xff09;是一种面向连接的、可靠的、全双工的传输协议。它使用头部&#xff08;Header&#xff09;和数据&#xff08;Data&#xff09;来组织数据包&#xff0c;确保数据的可靠传输和按序传递。 TCP协议报文结构 下面详细阐述TCP…

SSH远程直连--------------Docker容器

文章目录 1. 下载docker镜像2. 安装ssh服务3. 本地局域网测试4. 安装cpolar5. 配置公网访问地址6. SSH公网远程连接测试7.固定连接公网地址8. SSH固定地址连接测试 在某些特殊需求下,我们想ssh直接远程连接docker 容器,下面我们介绍结合cpolar工具实现ssh远程直接连接docker容器…

excel 动态表头与合并列

零、希望Springboot-java导出excel文件&#xff0c;包括动态表头与下边合并的列 使用 org.apache.poi 与自己封装工具类实现相关功能。代码如下 一、代码 1、依赖 implementation(group: org.apache.poi,name: poi-ooxml,version: 4.1.0)implementation(group: org.apache.po…

【Docker】存储卷Volume

Docker Volume概念 什么是存储卷 存储卷就是将宿主机的本地文件系统中存在的某个目录直接与容器内部的文件系统上的某一目录建立绑定关系。这就意味着&#xff0c;当我们在容器中的这个目录下写入数据时&#xff0c;容器会将其内容直接写入到宿主机上与此容器建立了绑定关系的…

1139. 最大的以 1 为边界的正方形;2087. 网格图中机器人回家的最小代价;1145. 二叉树着色游戏

1139. 最大的以 1 为边界的正方形 核心思想&#xff1a;枚举正方向的右下角坐标&#xff08;i&#xff0c;j&#xff09;&#xff0c;然后你只需要判断四条边的连续一的最小个数即可&#xff0c;这里是边求连续一的个数同时求解结果。 087. 网格图中机器人回家的最小代价 核心…

PHP自己的框架实现操作成功失败跳转(完善篇四)

1、实现效果&#xff0c;操作成功后失败成功自动跳转 2、创建操作成功失败跳转方法CrlBase.php /**成功后跳转*跳转地址$url* 跳转显示信息$msg* 等待时间$wait* 是否自动跳转$jump*/protected function ok($urlNULL,$msg操作成功,$wait3,$jump1){$code1;include KJ_CORE./tp…

FPGA原理与结构——可配置逻辑块CLB(Configurable Logic Block)

一、什么是CLB 1、CLB简介 可配置逻辑块CLB&#xff08;Configurable Logic Block&#xff09;是xilinx系类FPGA的基本逻辑单元&#xff08;在各系列中CLB可能有所不同&#xff0c;以下我们主要讨论Xilinx 7系类&#xff09;&#xff0c;是实现时序逻辑电路和组合逻辑电…

基于Pytorch构建DenseNet网络对cifar-10进行分类

DenseNet是指Densely connected convolutional networks&#xff08;密集卷积网络&#xff09;。它的优点主要包括有效缓解梯度消失、特征传递更加有效、计算量更小、参数量更小、性能比ResNet更好。它的缺点主要是较大的内存占用。 DenseNet网络与Resnet、GoogleNet类似&#…

如何下载英伟达NVIDIA旧版本驱动,旧版本驱动官方网址

https://www.nvidia.cn/Download/Find.aspx?langcn 也可以直接搜索英伟达官网&#xff0c;点击驱动程序&#xff0c;然后点击试用版驱动程序&#xff0c;里面不但有试用版的驱动&#xff0c;还有之前发布的所有驱动

redis乐观锁+启用事务解决超卖

乐观锁用于监视库存&#xff08;watch&#xff09;&#xff0c;然后接下来就启用事务。 启用事务&#xff0c;将减库存、下单这两个步骤&#xff0c;放到一个事务当中即可解决秒杀问题、防止超卖。 但是&#xff01;&#xff01;&#xff01;乐观锁&#xff0c;会带来" …

Docker 使用归纳总结

mongodb 的 terminal 可执行的命令是基于这个文件夹

【LeetCode】剑指 Offer Ⅱ 第4章:链表(9道题) -- Java Version

题库链接&#xff1a;https://leetcode.cn/problem-list/e8X3pBZi/ 类型题目解决方案双指针剑指 Offer II 021. 删除链表的倒数第 N 个结点双指针 哨兵 ⭐剑指 Offer II 022. 链表中环的入口节点&#xff08;环形链表&#xff09;双指针&#xff1a;二次相遇 ⭐剑指 Offer I…

5.7.webrtc线程的启动与运行

那在上一节课中呢&#xff1f;我向你介绍了web rtc的三大线程&#xff0c;包括了信令线程&#xff0c;工作线程以及网络线程。那同时呢&#xff0c;我们知道了web rtc 3大线程创建的位置以及运行的时机。 对吧&#xff0c;那么今天呢&#xff1f;我们再继续深入了解一下&#…

SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第一天)Mybatis的学习

SSM框架的学习与应用(Spring Spring MVC MyBatis)-Java EE企业级应用开发学习记录&#xff08;第一天&#xff09;Mybatis的学习 一、当前的主流框架介绍(这就是后期我会发出来的框架学习) Spring框架 ​ Spring是一个开源框架&#xff0c;是为了解决企业应用程序开发复杂…