对于关系型数据库,sql无疑是让人头疼的地方,个人对写这种重复CRUD的代码简直觉得是恶心透底,要是表再一多,瞬间爆炸。
对于c++,我以往本身还是很排斥ORM的,毕竟那是java和c#喜欢用的东西,感觉没有什么比较顺眼的开源框架,直到这次在巨大压力下尝试matador这个框架,其本身简洁的语法以及极低依赖深深吸引了我,没想到在c++里orm也可以实现得如此优雅。支持采用db first 和 code first两种设计模式,采用code first,直接手码model 然后直接create即可。如果采用db first,还是需要做model生成器的脚本,可以参考以前发的c#的model生成脚本,原理基本差不多,按照想要的格式做即可 。目前主要在内网开发,就懒得搞生成器做生成model的脚本了,有兴趣的可以自己试试,db first 的效率肯定高很多,特别是db有专人设计的情况下,有任何变动直接生成放进去就好。
https://github.com/zussel/matador
我还看到一个ORM框架也还不错,sqlite的,但需要c++14支持,而目前环境不可能因为这个东西就从11改到14。而matador目前只需要修改源码一个地方就可以在c++11下编译。
1、在prototype_node.hpp 做一个c++14中make_unique的方法,用c++11的unique_ptr即可实现
2、在CMakeList.txt中去除add_subdirectory(test),因为他做的单元测试中包含了大量的make_unique调用,屏蔽掉即可,不然需要去解决这个问题
这个框架也带来不小的麻烦,害我基本把源码看了一遍,虽然写这个的人功底不错,但也含有几个小BUG,必须要修改源码。
目前我已经发现并且已经修复的BUG又如下几个(仅sqlite):
1、写long类型,需要在源码中修改为sqlite3_bind_int64,读那地方也同样需要修改。对于unsigned long,其实是不支持的,主要由于sqlite3 api的原因,所以实现是pretend unsigned long,这个地方需注意,存的数据大小不可超过long的边界,否则会overflow
2、identifier 如果用std::string 做主键的话,会出现模板不匹配,他里面用了c++11特性 SFINAE 和 std::enable_if去做模板匹配,还含有继承,这个就造成了不同编译器结果可能不同。最简单解决办法就是在bind那地方,直接加一个std::string的方法并且不使用引用传值。
3、在sqlite_statment.hpp中,对于sqlite execute 错误返回值并未处理,比如SQLITE_CONSTRAINT SQLITE_BUSY,这些是一定需要处理逻辑的,可以在源码中抛出这些异常。。。还有个非常严重的地方是,有错误一定需要reset,sqlite3官方api上写得很明白,这个BUG会导致在persist走出作用域析构的时候出现内存错误,直接abort。。
4、接着3的问题,除了异常,在sqlite3_step之后,如果不是异常,但返回SQLITE_DONE 或者 SQLITE_OK也需要reset,但SQLITE_ROW千万别reset。。
5、在方言那个文件中,不能识别sqlite中小写的类型。。自己加上对小写类型的解析吧,在c++与其他语言混合开发,可能会出现BUG,但严格按SQLITE规范来说,确实是应该全大写的。