NexusForce 1.0.0
A rigorously engineered full-stack C++ backend library.
载入中...
搜索中...
未找到
sql_mapper.hpp
浏览该文件的文档.
1#ifndef NEFORCE_DATABASE_SQL_MAPPER_HPP__
2#define NEFORCE_DATABASE_SQL_MAPPER_HPP__
3
25
30NEFORCE_BEGIN_NAMESPACE__
31
37
43inline string cpp_type_to_sql_type(const reflect::type_id tid) {
45 if (tid == type_id_for<short>()) {
46 return "SMALLINT";
47 }
48 if (tid == type_id_for<unsigned short>()) {
49 return "SMALLINT";
50 }
51 if (tid == type_id_for<int>()) {
52 return "INTEGER";
53 }
54 if (tid == type_id_for<unsigned int>()) {
55 return "INTEGER";
56 }
57#ifdef NEFORCE_PLATFORM_WINDOWS
58 if (tid == type_id_for<long>()) {
59 return "INTEGER";
60 }
61 if (tid == type_id_for<unsigned long>()) {
62 return "INTEGER";
63 }
64#else
65 if (tid == type_id_for<long>()) {
66 return "BIGINT";
67 }
68 if (tid == type_id_for<unsigned long>()) {
69 return "BIGINT";
70 }
71#endif
72 if (tid == type_id_for<long long>()) {
73 return "BIGINT";
74 }
75 if (tid == type_id_for<unsigned long long>()) {
76 return "BIGINT";
77 }
78 if (tid == type_id_for<double>()) {
79 return "DOUBLE PRECISION";
80 }
81 if (tid == type_id_for<float>()) {
82 return "REAL";
83 }
84 if (tid == type_id_for<bool>()) {
85 return "BOOLEAN";
86 }
87 if (tid == type_id_for<string>()) {
88 return "TEXT";
89 }
90 return "TEXT";
91}
92
100inline reflect::meta_any read_column_by_type(const idb_tb_result& row, const size_t col, const reflect::type_id tid) {
102 if (tid == type_id_for<int>()) {
103 return reflect::meta_any(static_cast<int>(row.get_int32(col)));
104 } else if (tid == type_id_for<int64_t>()) {
105 return reflect::meta_any(row.get_int64(col));
106 } else if (tid == type_id_for<short>()) {
107 return reflect::meta_any(static_cast<short>(row.get_int32(col)));
108 } else if (tid == type_id_for<double>()) {
109 return reflect::meta_any(row.get_float64(col));
110 } else if (tid == type_id_for<float>()) {
111 return reflect::meta_any(row.get_float32(col));
112 } else if (tid == type_id_for<bool>()) {
113 return reflect::meta_any(row.get_bool(col));
114 } else if (tid == type_id_for<string>()) {
115 return reflect::meta_any(string(row.get(col)));
116 } else {
117 return reflect::meta_any(string(row.get(col)));
118 }
119}
120
127 if (!val.has_value()) {
128 return "NULL";
129 }
130 const auto tid = val.type_id();
132
133 if (tid == type_id_for<int>()) {
134 return integer32(val.get<int>()).to_string();
135 } else if (tid == type_id_for<int64_t>()) {
136 return integer64(val.get<int64_t>()).to_string();
137 } else if (tid == type_id_for<short>()) {
138 return integer32(val.get<short>()).to_string();
139 } else if (tid == type_id_for<double>()) {
140 return float64(val.get<double>()).to_string();
141 } else if (tid == type_id_for<float>()) {
142 return float64(val.get<float>()).to_string();
143 } else if (tid == type_id_for<bool>()) {
144 return val.get<bool>() ? "1" : "0";
145 } else if (tid == type_id_for<string>()) {
146 // simple quoting — caller should use parameterized queries when possible
147 const auto& s = val.get<string>();
148 string escaped;
149 escaped.reserve(s.size() + 2);
150 escaped += '\'';
151 for (const auto ch: s) {
152 if (ch == '\'') {
153 escaped += "''";
154 } else {
155 escaped += ch;
156 }
157 }
158 escaped += '\'';
159 return escaped;
160 }
161 return "NULL";
162}
163
172template <typename T>
174private:
175 static const reflect::meta_type* get_meta() {
177 if (meta == nullptr) {
178 NEFORCE_THROW_EXCEPTION(value_exception(
179 _NEFORCE format("sql_mapper: type '{}' not registered in reflect registry", reflect::type_name_v<T>)
180 .data()));
181 }
182 return meta;
183 }
184
185 static string resolve_table_name() {
186 const auto* meta = get_meta();
187 const auto tn = meta->table_name();
188 if (!tn.empty()) {
189 return tn;
190 }
191 return meta->name();
192 }
193
197 static vector<const reflect::meta_property*> collect_persistent_props(bool include_pk = true) {
198 const auto* meta = get_meta();
200 for (const auto& entry: meta->all_properties()) {
201 const auto* prop = entry.second;
202 if (prop->is_transient()) {
203 continue;
204 }
205 if (!include_pk && prop->is_auto_increment()) {
206 continue;
207 }
208 result.push_back(prop);
209 }
210 return result;
211 }
212
216 static const reflect::meta_property* find_pk_prop() {
217 const auto* meta = get_meta();
218 for (const auto& entry: meta->all_properties()) {
219 if (entry.second->is_primary_key()) {
220 return entry.second;
221 }
222 }
223 return nullptr;
224 }
225
226public:
231 static string table_name() { return resolve_table_name(); }
232
238 static string create_table_sql(string_view table_override = "") {
239 const auto* meta = get_meta();
240 const string tbl = table_override.empty() ? resolve_table_name() : string(table_override);
241
242 sql_builder b;
244
245 for (const auto& entry: meta->all_properties()) {
246 const auto* prop = entry.second;
247 if (prop->is_transient()) {
248 continue;
249 }
250
251 const string sql_type = cpp_type_to_sql_type(prop->type_id());
252
253 if (prop->is_primary_key() && prop->is_auto_increment()) {
254 b.column_auto_increment(prop->name(), sql_type);
255 } else if (prop->is_primary_key()) {
256 b.column_primary_key(prop->name(), sql_type);
257 } else if (prop->is_unique()) {
258 b.column_unique(prop->name(), sql_type);
259 } else if (prop->is_required()) {
260 b.column_not_null(prop->name(), sql_type);
261 } else {
262 b.column(prop->name(), sql_type);
263 }
264 }
265
266 return b.build();
267 }
268
274 static string drop_table_sql(string_view table_override = "") {
275 const string tbl = table_override.empty() ? resolve_table_name() : string(table_override);
276 sql_builder b;
278 return b.build();
279 }
280
286 static string select_sql(string_view table_override = "") {
287 const string tbl = table_override.empty() ? resolve_table_name() : string(table_override);
288
289 sql_builder b;
290 const auto* meta = get_meta();
291
292 for (const auto& entry: meta->all_properties()) {
293 if (entry.second->is_transient()) {
294 continue;
295 }
296 b.select(entry.second->name());
297 }
298 b.from(tbl);
299 return b.build();
300 }
301
308 static string insert_sql(string_view table_override = "", const sql_dialect dialect = sql_dialect::GENERIC) {
309 const string tbl = table_override.empty() ? resolve_table_name() : string(table_override);
310
311 const auto props = collect_persistent_props(false);
312 vector<string> columns;
313 columns.reserve(props.size());
314 for (const auto* p: props) {
315 columns.push_back(p->name());
316 }
317
318 sql_builder b;
319 b.set_dialect(dialect);
320 b.insert_into(tbl, columns);
321 return b.build();
322 }
323
331 static string delete_sql(string_view table_override = "", const sql_dialect dialect = sql_dialect::GENERIC) {
332 const string tbl = table_override.empty() ? resolve_table_name() : string(table_override);
333 const auto* pk = find_pk_prop();
334 if (pk == nullptr) {
335 NEFORCE_THROW_EXCEPTION(value_exception("sql_mapper: no primary key defined for DELETE"));
336 }
337
338 sql_builder b;
339 b.set_dialect(dialect);
340 b.delete_from(tbl);
341 b.where_eq(pk->name(), b.placeholder(1));
342 return b.build();
343 }
344
352 static string update_sql(string_view table_override = "", const sql_dialect dialect = sql_dialect::GENERIC) {
353 const string tbl = table_override.empty() ? resolve_table_name() : string(table_override);
354 const auto* pk = find_pk_prop();
355 if (pk == nullptr) {
356 NEFORCE_THROW_EXCEPTION(value_exception("sql_mapper: no primary key defined for UPDATE"));
357 }
358
359 const auto props = collect_persistent_props(true);
360
361 sql_builder b;
362 b.set_dialect(dialect);
363 b.update(tbl);
364
365 for (const auto* p: props) {
366 if (p->is_primary_key() || p->is_auto_increment()) {
367 continue;
368 }
369 b.set_param(p->name());
370 }
371
372 b.where_eq(pk->name(), b.next_placeholder());
373 return b.build();
374 }
375
382 static vector<string> get_values(const T& entity, bool include_pk = true) {
383 const auto props = collect_persistent_props(include_pk);
384 vector<string> values;
385 values.reserve(props.size());
386 for (const auto* prop: props) {
387 reflect::meta_any val = prop->get(&entity);
388 values.push_back(meta_any_to_sql_literal(val));
389 }
390 return values;
391 }
392
398 static reflect::meta_any get_pk_value(const T& entity) {
399 const auto* pk = find_pk_prop();
400 if (pk == nullptr) {
401 return reflect::meta_any{};
402 }
403 return pk->get(&entity);
404 }
405
412 static T from_row(const idb_tb_result& row, const unordered_map<string, size_t>& col_map = {}) {
413 const auto* meta = get_meta();
414 auto obj_any = meta->create();
415 if (!obj_any.has_value()) {
416 NEFORCE_THROW_EXCEPTION(value_exception("sql_mapper: failed to create entity instance"));
417 }
418 void* raw = obj_any.raw();
419
420 unordered_map<string, size_t> effective_map;
421 if (col_map.empty()) {
422 const auto& names = row.column_names();
423 for (size_t i = 0; i < names.size(); ++i) {
424 effective_map[names[i]] = i;
425 }
426 } else {
427 effective_map = col_map;
428 }
429
430 for (const auto& entry: meta->all_properties()) {
431 const auto* prop = entry.second;
432 if (prop->is_transient() || prop->is_readonly()) {
433 continue;
434 }
435
436 auto it = effective_map.find(prop->name());
437 if (it == effective_map.end()) {
438 continue;
439 }
440
441 reflect::meta_any val = read_column_by_type(row, it->second, prop->type_id());
442 prop->set(raw, val);
443 }
444
445 return *static_cast<T*>(raw);
446 }
447
454 vector<T> entities;
455 if (result == nullptr || result->empty()) {
456 return entities;
457 }
458
460 const auto& names = result->column_names();
461 for (size_t i = 0; i < names.size(); ++i) {
462 col_map[names[i]] = i;
463 }
464
465 while (result->next()) {
466 entities.push_back(from_row(*result, col_map));
467 }
468 return entities;
469 }
470
477 static string select_by_pk_sql(string_view table_override = "", const sql_dialect dialect = sql_dialect::GENERIC) {
478 const string tbl = table_override.empty() ? resolve_table_name() : string(table_override);
479 const auto* pk = find_pk_prop();
480 if (pk == nullptr) {
481 NEFORCE_THROW_EXCEPTION(value_exception("sql_mapper: no primary key defined for SELECT BY PK"));
482 }
483
484 const auto* meta = get_meta();
485 sql_builder b;
486 b.set_dialect(dialect);
487 for (const auto& entry: meta->all_properties()) {
488 if (entry.second->is_transient()) {
489 continue;
490 }
491 b.select(entry.second->name());
492 }
493 b.from(tbl);
494 b.where_eq(pk->name(), b.placeholder(1));
495 return b.build();
496 }
497};
498 // SQLMapper
500
501NEFORCE_END_NAMESPACE__
502#endif // NEFORCE_DATABASE_SQL_MAPPER_HPP__
constexpr bool empty() const noexcept
检查是否为空
属性反射元数据类
类型反射元数据类
static registry & instance()
获取单例实例
sql_builder & where_eq(string field, string value)
添加相等条件
sql_builder & from(string table) noexcept
设置主表
sql_builder & column_not_null(string name, string type)
添加带NOT NULL约束的列定义
sql_builder & set_param(string field)
添加 SET 赋值
sql_builder & column_auto_increment(string name, string type)
添加自增主键列定义(事实标准:AUTO_INCREMENT)
sql_builder & column(string name, string type)
添加基础列定义
sql_builder & set_dialect(const sql_dialect dialect) noexcept
设置数据库方言
sql_builder & column_unique(string name, string type)
添加带UNIQUE约束的列定义
string placeholder(const size_t index) const
生成方言感知的参数占位符
string build() const
构建最终的SQL语句
sql_builder & select(vector< string > fields)
列表设置SELECT字段
sql_builder & column_primary_key(string name, string type)
添加PRIMARY KEY列定义
string next_placeholder()
获取下一个参数占位符
sql_builder & update(string table)
设置UPDATE表名
sql_builder & delete_from(string table)
设置DELETE FROM表名
sql_builder & drop_table_if_exists(string table)
删除表(IF EXISTS)
sql_builder & insert_into(string table, vector< string > fields)
设置INSERT INTO表名和字段
sql_builder & create_table_if_not_exists(string table)
创建表(IF NOT EXISTS)
ORM 对象-关系映射器
static string select_sql(string_view table_override="")
生成 SELECT 查询 SQL
static string create_table_sql(string_view table_override="")
生成 CREATE TABLE SQL
static T from_row(const idb_tb_result &row, const unordered_map< string, size_t > &col_map={})
从结果行构建实体对象
static reflect::meta_any get_pk_value(const T &entity)
获取主键值
static string delete_sql(string_view table_override="", const sql_dialect dialect=sql_dialect::GENERIC)
生成 DELETE SQL(按主键删除)
static string insert_sql(string_view table_override="", const sql_dialect dialect=sql_dialect::GENERIC)
生成 INSERT SQL(占位符由方言自动决定)
static vector< T > from_result(unique_ptr< idb_tb_result > result)
从查询结果集构建实体对象列表
static vector< string > get_values(const T &entity, bool include_pk=true)
从实体提取所有持久化字段的值(以 SQL 字面量表示)
static string drop_table_sql(string_view table_override="")
生成 DROP TABLE SQL
static string table_name()
获取映射的表名
static string update_sql(string_view table_override="", const sql_dialect dialect=sql_dialect::GENERIC)
生成 UPDATE SQL(按主键更新所有非主键字段)
static string select_by_pk_sql(string_view table_override="", const sql_dialect dialect=sql_dialect::GENERIC)
生成按主键查询的 SELECT SQL
独占智能指针
动态大小数组容器
constexpr void reserve(const size_type n)
预留容量
constexpr void push_back(const T &value)
在末尾拷贝插入元素
数据库抽象接口层
long int64_t
64位有符号整数类型
constexpr string format(const string_view fmt, Args &&... args)
格式化字符串
size_t type_id
类型标识符
constexpr type_id type_id_for() noexcept
计算类型 T 的运行时期类型标识
constexpr string_view type_name_v
type_name 的便捷访问变量模板
sql_dialect
数据库方言
string cpp_type_to_sql_type(const reflect::type_id tid)
将 C++ 类型 ID 映射为 SQL 类型名称
reflect::meta_any read_column_by_type(const idb_tb_result &row, const size_t col, const reflect::type_id tid)
从结果行中按类型ID读取值到 meta_any
string meta_any_to_sql_literal(const reflect::meta_any &val)
将 meta_any 值转换为 SQL 字面量字符串
basic_string< char > string
字符字符串
basic_string_view< char > string_view
字符字符串视图
constexpr decltype(auto) data(Container &cont) noexcept(noexcept(cont.data()))
获取容器的底层数据指针
反射系统主入口
SQL语句构建器
字符串类型别名和实用函数
64位浮点数包装类
关系型数据库结果集抽象基类
virtual bool get_bool(size_type n) const =0
布尔值
virtual int64_t get_int64(size_type n) const =0
64位整数
virtual int32_t get_int32(size_type n) const =0
32位整数
virtual string_view get(size_type n) const =0
字符串
virtual float64_t get_float64(size_type n) const =0
64位浮点数
virtual const vector< string_view > & column_names() const =0
获取所有列名
virtual float32_t get_float32(size_type n) const =0
32位浮点数
32位整数包装类
64位整数包装类