在传统的SQLite操作中,开发者常常面临以下几个痛点:
- 手动编写CREATE TABLE语句:不仅繁琐,还容易因类型映射错误导致问题;
- 手动绑定参数:容易出现类型不匹配或参数索引错误;
- 频繁单条插入性能低下:大量磁盘I/O严重影响效率;
- 资源管理复杂:数据库连接、事务等需手动管理,容易发生资源泄露。
为解决上述问题,我提供了一种自动化方法,能够自动生成表创建语句、插入语句,实现类型映射与缓存管理。尽管Python代码生成或C++模板元编程均可实现该功能,本文选择基于C++模板元编程与变长参数机制,以兼顾性能与使用便捷性。
类型映射
通过模板将c++类型转换成SQLite类型。
cpp
template<typename T>
struct SQLiteType {
static const char *value;
};
template<> const char *SQLiteType<int64_t>::value = "INTEGER";
template<> const char *SQLiteType<int32_t>::value = "INTEGER";
template<> const char *SQLiteType<int8_t>::value = "INTEGER";
template<> const char *SQLiteType<float>::value = "REAL";
template<> const char *SQLiteType<double>::value = "REAL";
template<> const char *SQLiteType<const char *>::value = "TEXT";
template<> const char *SQLiteType<std::string>::value = "TEXT";
template<> const char *SQLiteType<bool>::value = "INTEGER";
生成表创建和数据插入语句
利用变长参数模板递归生成SQL语句,避免手动编写。
cpp
template<typename Last>
static void generate_sql_var_type(std::ostringstream& oss, const std::pair<const char *, Last>& field) {
oss << field.first << " " << SQLiteType<Last>::value << " NOT NULL";
}
template<typename T, typename... Args>
static void generate_sql_var_type(std::ostringstream& oss,
const std::pair<const char *, T>& first,
const std::pair<const char *, Args>& ... rest) {
oss << first.first << " " << SQLiteType<T>::value << " NOT NULL";
if (sizeof...(rest) > 0) {
oss << ", ";
generate_sql_var_type(oss, rest...); // 递归处理剩余参数
}
}
std::string generate_init_table_statement(const std::ostringstream& var_type_statement) {
return "create table if not exists " + table_name + " (" + var_type_statement.str() + ");";
}
用同样的方法生成数据插入语句。
cpp
template<typename Last>
static void generate_sql_insert(std::ostringstream& oss_var, std::ostringstream& oss_val,
const std::pair<const char *, Last>& field) {
oss_var << field.first;
oss_val << "?";
}
template<typename T, typename... Args>
static void generate_sql_insert(std::ostringstream& oss_var,
std::ostringstream& oss_val,
const std::pair<const char *, T>& first,
const std::pair<const char *, Args>& ... rest) {
oss_var << first.first;
oss_val << "?";
if (sizeof...(rest) > 0) {
oss_var << ", ";
oss_val << ", ";
generate_sql_insert(oss_var, oss_val, rest...); // 递归处理剩余参数
}
}
std::string generate_insert_table_statement(const std::ostringstream& var, const std::ostringstream& val) {
return "insert into " + table_name + " (" + var.str() + ") values (" + val.str() + ");";
}
缓存
使用std::vector和std::tuple作为数据缓存,并在达到指定条数时自动触发批量写入。
cpp
std::vector<std::tuple<First, Rest...>> caches{};
绑定参数时展开tuple,实现自动参数绑定。
cpp
template<int Index = 0, typename... Ts>
void bind_values_impl(SQLite::Statement& query, const std::tuple<Ts...>& t) {
if constexpr (Index < sizeof...(Ts)) {
query.bind(Index + 1, std::get<Index>(t));
bind_values_impl<Index + 1>(query, t);
}
}
完整实现
cpp
#ifndef SQLITE_WRITER_HPP
#define SQLITE_WRITER_HPP
#include <iostream>
#include <string>
#include <utility>
#include <sstream>
#include <vector>
#include <tuple>
#include "SQLiteCpp/SQLiteCpp.h"
#define SQLITE_WRITER_SUCCESS 0
#define SQLITE_WRITER_NOT_INIT (-1)
#define SQLITE_WRITER_NOT_ENABLE (-2)
#define SQLITE_WRITER_CACHE_NOT_ENOUGH (-3)
// 类型映射:C++ 类型 → SQLite 类型
template<typename T>
struct SQLiteType {
static const char *value;
};
template<> const char *SQLiteType<int64_t>::value = "INTEGER";
template<> const char *SQLiteType<int32_t>::value = "INTEGER";
template<> const char *SQLiteType<int8_t>::value = "INTEGER";
template<> const char *SQLiteType<float>::value = "REAL";
template<> const char *SQLiteType<double>::value = "REAL";
template<> const char *SQLiteType<const char *>::value = "TEXT";
template<> const char *SQLiteType<std::string>::value = "TEXT";
template<> const char *SQLiteType<bool>::value = "INTEGER";
template<typename First, typename... Rest>
class SqliteWriter {
public:
SqliteWriter() = delete;
explicit SqliteWriter(const bool enable,
const std::string& db_path,
const std::string& table_name,
const uint32_t cache_count,
const std::pair<const char *, First>& first_field,
const std::pair<const char *, Rest>& ... rest_field) :
enable(enable), db_path(db_path), table_name(table_name), cache_count(cache_count) {
std::ostringstream oss0{};
generate_sql_var_type(oss0, first_field, rest_field...);
init_table_statement = generate_init_table_statement(oss0);
std::ostringstream oss1{};
std::ostringstream oss2{};
generate_sql_insert(oss1, oss2, first_field, rest_field...);
insert_table_statement = generate_insert_table_statement(oss1, oss2);
}
~SqliteWriter() {
if (enable) {
write_immediately();
}
}
int32_t init() {
if (!enable) {
return SQLITE_WRITER_NOT_ENABLE;
}
db = std::make_unique<SQLite::Database>(db_path, SQLite::OPEN_READWRITE | SQLite::OPEN_CREATE);
db->exec("PRAGMA journal_mode = WAL");
db->exec("PRAGMA synchronous = OFF");
db->exec(init_table_statement);
caches.reserve(cache_count);
return SQLITE_WRITER_SUCCESS;
}
template<typename... Values>
int32_t write_cache(const Values& ... values) {
if (!enable) {
return SQLITE_WRITER_NOT_ENABLE;
}
caches.emplace_back(values...);
if (caches.size() < cache_count) {
return SQLITE_WRITER_CACHE_NOT_ENOUGH;
}
return write_immediately();;
}
template<typename... Values>
int32_t write_cache(Values&& ... values) {
if (!enable) {
return SQLITE_WRITER_NOT_ENABLE;
}
caches.emplace_back(std::forward<Values>(values)...);
if (caches.size() < cache_count) {
return SQLITE_WRITER_CACHE_NOT_ENOUGH;
}
return write_immediately();
}
const std::string& get_init_table_statement() {
return init_table_statement;
}
const std::string& get_insert_table_statement() {
return insert_table_statement;
}
protected:
template<typename Last>
static void generate_sql_var_type(std::ostringstream& oss, const std::pair<const char *, Last>& field) {
oss << field.first << " " << SQLiteType<Last>::value << " NOT NULL";
}
template<typename T, typename... Args>
static void generate_sql_var_type(std::ostringstream& oss,
const std::pair<const char *, T>& first,
const std::pair<const char *, Args>& ... rest) {
oss << first.first << " " << SQLiteType<T>::value << " NOT NULL";
if (sizeof...(rest) > 0) {
oss << ", ";
generate_sql_var_type(oss, rest...); // 递归处理剩余参数
}
}
template<typename Last>
static void generate_sql_insert(std::ostringstream& oss_var, std::ostringstream& oss_val,
const std::pair<const char *, Last>& field) {
oss_var << field.first;
oss_val << "?";
}
template<typename T, typename... Args>
static void generate_sql_insert(std::ostringstream& oss_var,
std::ostringstream& oss_val,
const std::pair<const char *, T>& first,
const std::pair<const char *, Args>& ... rest) {
oss_var << first.first;
oss_val << "?";
if (sizeof...(rest) > 0) {
oss_var << ", ";
oss_val << ", ";
generate_sql_insert(oss_var, oss_val, rest...); // 递归处理剩余参数
}
}
std::string generate_init_table_statement(const std::ostringstream& var_type_statement) {
return "create table if not exists " + table_name + " (" + var_type_statement.str() + ");";
}
std::string generate_insert_table_statement(const std::ostringstream& var, const std::ostringstream& val) {
return "insert into " + table_name + " (" + var.str() + ") values (" + val.str() + ");";
}
int32_t write_immediately() {
if (db == nullptr) {
return SQLITE_WRITER_NOT_INIT;
}
SQLite::Transaction transaction(*db);
SQLite::Statement query(*db, insert_table_statement);
for (const auto& cache: caches) {
query.reset();
bind_values(query, cache);
query.exec();
}
transaction.commit();
caches.clear();
return SQLITE_WRITER_SUCCESS;
}
void bind_values(SQLite::Statement& query, const std::tuple<First, Rest...>& values) {
bind_values_impl(query, values);
}
template<int Index = 0, typename... Ts>
void bind_values_impl(SQLite::Statement& query, const std::tuple<Ts...>& t) {
if constexpr (Index < sizeof...(Ts)) {
query.bind(Index + 1, std::get<Index>(t));
bind_values_impl<Index + 1>(query, t);
}
}
protected:
std::vector<std::tuple<First, Rest...>> caches{};
std::unique_ptr<SQLite::Database> db{nullptr};
bool enable{true};
std::string db_path{};
std::string table_name{};
uint32_t cache_count{};
std::string init_table_statement{};
std::string insert_table_statement{};
};
#endif //SQLITE_WRITER_HPP
总结
该封装库极大简化了SQLite操作流程,提升了开发效率与代码可靠性,同时保持了C++应用的性能优势。适用于需要高效、可靠数据库操作的各种C++应用场景。