如何优雅的写C++代码 Obotcha介绍(Sqlite3 ORM设计——Table设计)

    先show一下Obotcha中声明数据库Table的方法:

DECLARE_SIMPLE_CLASS(Student) IMPLEMENTS(SqlRecord) {
  DECLARE_TABLE_2(Student,age,name);
  DECLARE_INT_DATA_MEMBER(Student,age);
  DECLARE_STRING_DATA_MEMBER(Student,name);
};

PUBLISH_TABLE(Student);

所有的数据库Table对应的class都需要继承SqlRecord,方便以后做package操作。

DECLARE_TABLE_2:这个宏是用来声明Table名称和column列名的。第一个参数是Table名字,第二个参数开始是Table的列名。

DECLARE_INT_DATA_MEMBER:声明了column的类型为INT。第一个是Table名字,第二个是列名字。

DECLARE_STRING_DATA_MEMBER:同上,是申明String类型的。

PUBLISH_TABLE:发布这个table。

通过上述这个定义以后,就完成了和数据库中Student表的映射,前提:数据库中Student表名,列名要和这里声明的一直,否者反射的时候是无法找到对应的赋值函数的。

接下来,我们如果需要做一个查询,就可以使用下面的代码:

ArrayList<SqlRecord> ss = c->query(createString("select * from student;"),RECORD_BUILDER(Student));
    
int size = ss->size();
for(int index = 0;index<size;index++) {
    Student v = ss->get(index);
    printf("name is %s \n",v->getname()->toChars());
    printf("age is %d \n",v->getage());
}

其中RECORD_BUILDER是用来给底层创建Student实例而传入的builder。总体来看基本上就是一个简单的ORM了。哈哈。

说完用法,我们来分析一下具体的实现方法:

一.DECLARE_INT_DATA_MEMBER 宏分析

#define DECLARE_INT_DATA_MEMBER(Table,Member) \
    private:int Member; \
    public: int get##Member() {return Member;} \
    public: void set##Member(int v) {\
        Member = v;\
    } \
    public: static void set##Member(void *t,char *v) { \
        _##Table* _t = (_##Table *)t;\
        _t->Member = atoi(v);\
    }\

这个宏以传入的列名声明了成员变量,此外还扩展了该成员变量的get/set函数。

二.DECLARE_TABLE_2宏分析:

#define DECLARE_TABLE_2(Table,X,Y) \
    public:static std::map<std::string,sql_data_set> funcmap; \
    private:sql_data_set funclist_##Table[2];\
    public:static void preInitialize() { \
        _##Table::funcmap.insert(std::pair<std::string,sql_data_set>(#X,_##Table::set##X));\
        _##Table::funcmap.insert(std::pair<std::string,sql_data_set>(#Y,_##Table::set##Y));\
    }\
    DECLARE_SQL_ASSIGNMENT(Table,2)

funcmap实际上是用来存放<函数名,函数指针>的一个map。底层每次都会调用根据col的列名到这个map中去查找获取DECLARE_INT_DATA_MEMBER中声明的set接口指针。

preInitialize函数就是将set接口指针和col名作为键值对存到map中。

三.DECLARE_SQL_ASSIGNMENT 宏分析

#define DECLARE_SQL_ASSIGNMENT(Table,Size) \
    public: static int isInit;\
    public: sql_data_set getSqlAssignment(int index,std::string col) { \
        printf("get trace1,index is %d \n",index);\
        if(Size <= index) { \
            return nullptr;\
        }\
        if(_##Table::funclist_##Table[index] != nullptr) { \
            printf("get trace2 \n");\
            return _##Table::funclist_##Table[index]; \
        }\
        if(_##Table::isInit == 0) { \
            _##Table::preInitialize();\
            _##Table::isInit = 1;\
        }\
        std::map<std::string,sql_data_set>::iterator ite = _##Table::funcmap.find(col);\
        printf("get trace3 \n");\
        if(ite != _##Table::funcmap.end()) { \
            _##Table::funclist_##Table[index] = ite->second;\
            printf("get trace4 \n");\
            return ite->second;\
        }\
        printf("get trace4 \n");\
        return nullptr;\
    }\

getSqlAsignment的处理就是先根据col名字找到相关的函数指针,funclist是用来做缓存的,后续可以直接根据列id获取,减少hashmap的查找。

四.PUBLISH_TABLE宏分析

#define PUBLISH_TABLE(Table) \
    std::map<std::string,sql_data_set> _##Table::funcmap;\
    int _##Table::isInit = 0;\
    DECLARE_SIMPLE_CLASS(Build##Table) IMPLEMENTS(SqlRecordBuilder){ \
        public :SqlRecord create() { \
            sp<_##Table> data;\
            _##Table *t = new _##Table();\
            data.set_pointer(t);\
            return data;\
        }\
    };\

PUBLISH宏实际上是实现了一个sqlrecordbuilder的实例,sql底层就是调用这个create函数来生产对应实例。

相关代码参看:

https://github.com/wangsun1983/Obotcha/blob/master/util/sql/include/SqlRecord.hpp

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值