如何设计一个更好的C++ ORM
2016/11/26
“用C++的方式读写数据库,简直太棒了!”
上一篇相关文章:如何设计一个简单的C++ ORM
(旧版代码)
��
关于这个设计的代码和样例 ��:
https://github.com/BOT-Man-JL/ORM-Lite
0. 上一版本的问题
上一个版本较为简单,仅仅支持了最基本的
CRUD
,存在以下不足:
- 不支持
Nullable
数据类型; - 自动判断C++对象成员的字段名所用的方法耦合度高;
- 表达式系统不够完善;
- 不支持多表操作和选择部分字段的操作;
利用了课余时间,在最新版本中已经改进了以上内容;��
如果你只是对模板部分感兴趣,可以直接看 4. 推导查询结果
;
1. Nullable
字段
1.1 为什么要支持 Nullable
字段
- 尽管C++原生数据类型并没有
Nullable
的支持,但是SQL里有null
; - 当两个表合并时,会产生可能为空的字段;
(例如,和一个空表LEFT JOIN
后,每一行都会带有null
字段)
1.2 基本语义和实现
所以,ORMLite中实现了一个类似 C# 的 Nullable<T>
:
- 默认 构造/赋值:对象为空;
- 值 构造/赋值:对象为值;
- 复制/移动:目标和源同值;
- GetValueOrDefault:返回 值 或 默认非空值;
- 比较:两个对象相等,当且仅当
- 两个值都为
空
; - 两个值都
非空
且 具有相同的值
;
- 两个值都为
具体参考:
http://stackoverflow.com/questions/2537942/nullable-values-in-c/28811646#28811646
2. 提取对象成员字段名
这里的提取对象成员字段名,指的是:
如果想表示 UserModel
的 user_id
字段,可以通过
UserModel user
的 user.user_id
推导出来;
(之前的文章讲的不是很明白��)
UserModel user;
auto field = FieldExtractor { user };
// Get Field of 'user'
auto field_user_id = field (user.user_id);
// Get string of Field Name
auto fieldName_user_id = field_user_id.fieldName;
另外,在跨表查询时,除了字段名,我们还需要保存表名;
2.1 之前的实现
在上一篇文章里,我曾经使用这种方法实现自动判断C++对象的成员字段名:
由于没有想到很好的办法,所以目前使用了指针进行运行时判断:
queryHelper.__Accept (FnVisitor (), [&property, &isFound, &index] (auto &val) { if (!isFound && property == &val) isFound = true; else if (!isFound) index++; }); fieldName = FieldNames[index];
相当于使用
Visitor
遍历这个对象,找到对应成员的序号;
总结起来有两点:
- 保存一个被注入对象的引用
queryHelper
; - 每次遍历这个引用,判断各个字段指针是否相同;
首先,这么做将导致 queryHelper
和 Queryable
对象严重耦合:
- 每一个
Queryable
对象里都需要保存对应的queryHelper
; - 当一个
Queryable
对象可以判断多个不同的表的字段名时,
需要将所有queryHelper
保存为一个tuple
;
(因为不同的queryHelper
是不同的数据类型,不能直接用list
)
另外,这将会导致(巨大的)运行时开销&#