如何用Boost C++从SQL获取泛型对象?自定义ORM开发遇阻
如何基于Boost C++实现SQL到泛型对象的ORM映射?
我目前在基于SQLite和C实现数据库操作,已经完成了数据表的创建、删除以及对象的增删功能,但在查询时无法将数据库记录映射为内存中的C对象,只能返回打印摘要。我想实现类ORM的映射功能,远期目标是支持代码生成,当前需要解决未知结构表的查询与对象编辑问题,同时不确定现有设计是否合理。
已知前提:所有数据库对象至少包含整数类型的id、字符串类型的name,以及任意数量的其他类型字段,希望直接返回对象本身而非字符串表示。
现有代码如下:
头文件:SelectOperation.h
#pragma once #include "../SqlOperation.h" #include "../../Exceptions/DbException.h" #include "ITableRecord.h" #include <sqlite3.h> #include <vector> #include <string> class SelectOperation : public SqlOperation { public: SelectOperation(const std::string& tableName); void prepareStatement(sqlite3_stmt** stmt, sqlite3* db) override; };
实现文件:selectOperation.cpp
#include "SelectOperation.h" SelectOperation::SelectOperation(const std::string& tableName) { sql_statement = "SELECT * FROM " + tableName; } void SelectOperation::prepareStatement(sqlite3_stmt** stmt, sqlite3* db) { int rc = sqlite3_prepare_v2(db, sql_statement.c_str(), -1, stmt, nullptr); if (rc != SQLITE_OK) { throw DbException("Failed to prepare select statement: " + std::string(sqlite3_errmsg(db))); } }
当前prepareStatement仅能预处理SQL语句,无法将查询结果映射为泛型对象,核心问题是不知道如何处理结构未知的表记录。
解决方案
1. 定义泛型记录基类,支持动态字段存储
扩展ITableRecord,强制实现核心字段id/name的访问接口,同时用boost::any实现任意类型字段的动态存储:
#include <boost/any.hpp> #include <unordered_map> class ITableRecord { public: virtual ~ITableRecord() = default; // 核心字段强制接口 virtual int getId() const = 0; virtual void setId(int id) = 0; virtual const std::string& getName() const = 0; virtual void setName(const std::string& name) = 0; // 动态字段读写模板 template<typename T> T getField(const std::string& fieldName) const { auto it = m_fields.find(fieldName); if (it == m_fields.end()) { throw DbException("Field not found: " + fieldName); } return boost::any_cast<T>(it->second); } template<typename T> void setField(const std::string& fieldName, const T& value) { m_fields[fieldName] = value; } protected: std::unordered_map<std::string, boost::any> m_fields; };
2. 实现通用记录类
基于基类实现通用的记录实例,用于存储任意表的查询结果:
class GenericTableRecord : public ITableRecord { private: int m_id; std::string m_name; public: int getId() const override { return m_id; } void setId(int id) override { m_id = id; } const std::string& getName() const override { return m_name; } void setName(const std::string& name) override { m_name = name; } };
3. 修改SelectOperation,添加结果映射逻辑
拆分预处理与执行逻辑,新增execute方法负责查询并返回泛型对象列表:
// 在SelectOperation.h中新增方法声明 std::vector<std::unique_ptr<ITableRecord>> execute(sqlite3* db); // 在selectOperation.cpp中实现execute方法 std::vector<std::unique_ptr<ITableRecord>> SelectOperation::execute(sqlite3* db) { sqlite3_stmt* stmt = nullptr; prepareStatement(&stmt, db); std::vector<std::unique_ptr<ITableRecord>> records; int rc; while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { auto record = std::make_unique<GenericTableRecord>(); // 读取核心字段 int idColIdx = sqlite3_column_index(stmt, "id"); record->setId(sqlite3_column_int(stmt, idColIdx)); int nameColIdx = sqlite3_column_index(stmt, "name"); const char* namePtr = reinterpret_cast<const char*>(sqlite3_column_text(stmt, nameColIdx)); record->setName(namePtr ? std::string(namePtr) : ""); // 读取动态字段 int colCount = sqlite3_column_count(stmt); for (int i = 0; i < colCount; ++i) { const char* colName = sqlite3_column_name(stmt, i); std::string fieldName(colName); if (fieldName == "id" || fieldName == "name") continue; switch (sqlite3_column_type(stmt, i)) { case SQLITE_INTEGER: record->setField<int>(fieldName, sqlite3_column_int(stmt, i)); break; case SQLITE_TEXT: { const char* textPtr = reinterpret_cast<const char*>(sqlite3_column_text(stmt, i)); record->setField<std::string>(fieldName, textPtr ? std::string(textPtr) : ""); break; } case SQLITE_FLOAT: record->setField<double>(fieldName, sqlite3_column_double(stmt, i)); break; case SQLITE_BLOB: { int blobSize = sqlite3_column_bytes(stmt, i); const uint8_t* blobData = static_cast<const uint8_t*>(sqlite3_column_blob(stmt, i)); record->setField<std::vector<uint8_t>>(fieldName, std::vector<uint8_t>(blobData, blobData + blobSize)); break; } case SQLITE_NULL: // 可根据需求处理空值,比如存储默认值或留空 break; default: throw DbException("Unsupported column type: " + std::to_string(sqlite3_column_type(stmt, i))); } } records.push_back(std::move(record)); } if (rc != SQLITE_DONE) { throw DbException("Select execution failed: " + std::string(sqlite3_errmsg(db))); } sqlite3_finalize(stmt); return records; }
4. 现有设计优化建议
- 单一职责拆分:将SQL语句构建、预处理、执行逻辑拆分为独立方法,避免一个方法承担过多职责。
- 引入工厂模式:为远期代码生成做铺垫,可实现
TableRecordFactory,根据表名创建对应实体类(而非仅通用类)。 - 封装类型映射:将SQLite类型与C++类型的映射逻辑封装为工具类,方便后续扩展和维护。
- 事务支持:在
SqlOperation基类中添加事务管理接口,保证增删改操作的原子性。
内容的提问来源于stack exchange,提问作者user284985
相关产品推荐
相关产品推荐

