(原创)发布一个C++版本的ORM库SmartDB(一)
先简单说说ORM的优点:
- 提高开发效率,减少重复劳动,只和业务实体打交道,由业务实体自动生成sql语句,不用手写sql语句。
 - 简单易用, 可维护性好。
 - 隔离数据源,使得我们更换数据源时不用修改代码。
 
SmartDB基础库
java和c#中有很多ORM框架,如c#中的entity framework、Linq to Sql、NHibernate等,java中有Hibernate、Mybatis等等,其它各种开源的ORM库多如牛毛。这应该得益于托管语言在这方面的优势。然而,c++的世界中,却鲜有ORM框架,c++中比较知名的ORM库应该是ODB了,但是ODB的使用比较麻烦,需要三个库,写的ORM代码还需要ODB专门编译一下(不知道我说的是否确切),总之使用起来比较麻烦,让人望而却步。我的项目中本来只需要用sqlite,但是考虑到后期可能会更换数据库,所以想搞一个ORM库,但时间又比较紧,觉得短时间内搞不定ODB,于是放弃,选择最简单稳妥的作法,先只封装sqlite,目标是封装成简洁,统一的接口,即使到后面更换数据库时,我也不需要改应用层的代码,只要替换底层数据库封装模块就行了。在这个目标的指引下我很快完成了基础库SmartDB的封装,如下是SmartDB的操作接口。
bool Open(string dbName);//打开数据库
bool Close();//关闭数据库
bool Excecute(const std::string && query); //直接执行sql语句
bool ExcecuteBulk(const Args && ... args); //批量操作的sql语句
bool Prepare(const std::string && query); //批量操作之前的准备
bool Excecute(const std::string && query, const Args && ... args); //执行带占位符的sql语句
R ExecuteScalar(const std::string &query, const Args &... args); //返回函数执行的一个值, 执行简单的汇聚函数,如select count(*), select max(*)等
bool GetTable(const string &query, T &table, TableRange* tableRange, const Args &... args); //返回一个通用的表结构
int GetRowCount(const string &query, const TableRange* tableRange, const Args &... args);//获取表的总行数
这些接口是直接接受sql语句和参数列表的,所以理论上它可以完成所有的数据库操作。目前内部只支持sqlite,我自测了一下基础库的性能。在我的双核6G的台式机下测试了一下插入速度和读取速度,一个八字段的表(int和doble类型),批量插入速度大概为25W条/s,读取速度大概为11W条/s。这些已经满足了我的需求,但是我觉得可以在这个基础上做得更多,即支持ORM,我期望我的ORM能达到类似于entity framework的调用方式,比ODB更好用(比如智能提示、链式调用等),而性能又不至于降低太多。
EF中查询方式
DemoDBEntities context = new DemoDBEntities();
ObjectQuery<BlogMaster> query = context.CreateObjectSet<BlogMaster>().Where("it.UserId > @UserId",new ObjectParameter("UserId", ))
.OrderBy("it.UserId desc"); List<BlogMaster> list = query.ToList();
SmartDBEX ORM库
c++中ORM库的实现思路: ORM的关键是如何将业务实体转成sql语句以及表的原始数据如何转成业务实体,它们横在数据库和实体之间的两道鸿沟,因此,如何通过一个结构体去自动生成sql脚本是第一个需要解决的问题,如何将原始表转成业务实体是第二个需要解决的问题。我觉得问题的关键在结构体,我需要结构体提供一些信息给我,比如,字段类型,字段名,字段值等信息,这在c#或者java中可以通过反射就可以得到,但c++没有反射,是无法自动就得到这些信息的,不过C++虽然没有托管语言的这些优势,但却有自己的优势--模板元,可以通过模板元来获取这些信息,通过这些信息我就可以自动生成sql脚本了;第二个问题则是通过boost的variant和结构体中的赋值函数来解决。
总之,我通过在结构体上做文章,最终达到了我的目标,而且我会保证结构体不会复杂,尽可能少的让用户去写代码,只需要提供几个简单的函数就可以实现ORM了。我是在基础库SmartDB之上扩展了ORM功能,最终的ORM库为SmartDBEX, 它内部是调用了SmartDB实现具体功能。这样做的目的是即可使用操作sql的接口,又可使用ORM接口,用户根据自己选择即可。
我在台式机上测了一下SmartDBEX的性能:还是之前的表,批量插入速度为25W条/s,读取并转成实体列表的速度为2.3W条/s。比较满意的是批量插入速度没有降低,差强人意的是读取实体列表的速度降低了不少,性能损耗在两个地方:原始表的字段和实体的字段匹配和原始表字段赋值给实体。没办法,ORM这里必须要损耗一些性能。2.3W条/s的读取速度对一般应用来说已经可以满足要求了。
如何使用SmartDB和SmartDBEX
SmartDB的测试程序
/*创建表*/
void TestCreateTable(SmartDB& db)
{
const string sqlcreat = "CREATE TABLE if not exists TestInfoTable(ID INTEGER NOT NULL, KPIID INTEGER, CODE INTEGER, V1 INTEGER, V2 INTEGER, V3
REAL);";
if (!db.Excecute(sqlcreat))
return;
} /*单条插入*/
void TestInsert(SmartDB& db)
{
const string sqlinsert = "INSERT INTO TestInfoTable(ID, KPIID, CODE, V1, V2, V3) VALUES(1, 2, 3, 4, 5, 6.032);";
if (!db.Excecute(sqlinsert))
return; const string sqlinsert1 = "INSERT INTO TestInfoTable(ID, KPIID, CODE, V1, V2, V3) VALUES(?, ?, ?, ?, ?, ?);";
int n = ;
string str = "3.1423";
if (!db.Excecute(sqlinsert1, n, n, n, n, n, str))
return;
} /*批量插入, 启用事务*/
void TestBulkInsert(SmartDB& db)
{
const string sqlinsert = "INSERT INTO TestInfoTable(ID, KPIID, CODE, V1, V2, V3) VALUES(?, ?, ?, ?, ?, ?);";
timer t;
Transaction transaction(&db); //开始事务
db.Prepare(sqlinsert); //准备sql,含占位符
bool ret = false;
for (size_t i = ; i < ; i++)
{
ret = db.ExcecuteBulk(i, i, i, i, i, i);
if (!ret)
break;
} if (ret)
transaction.Commit(); //提交事务
else
transaction.RollBack(); //回滚 cout << t.elapsed() << endl;
}
SmartDBEX的测试程序
void TestORM(SmartDB& db)
{
SmartDBEx dbex(db);
//创建表
auto r = dbex.Create<TestInfo>(); //查询
auto& query = dbex.Query<TestInfo>().Where(Var(TestInfo::CODE < and TestInfo::ID < ));
vector<TestInfo> v;
bool b = dbex.Get<TestInfo>(v); //获取总行数
int count = dbex.Count<TestInfo>().Scalar();
cout << count << endl; //单条插入
r = dbex.Insert(t); //批量插入
TestInfo t = {,,,,,};
Transaction tans(&db);
r = dbex.Prepare<TestInfo>();
for (size_t i = ; i < ; i++)
{
r = dbex.BulkInsert(t);
}
r = tans.Commit(); //删除
r = dbex.Delete<TestInfo>()();
}
这里我们不妨对比一下ODB的调用方式,ODB的查询方式:
typedef odb::query<person> query;
typedef odb::result<person> result; {
transaction t (db->begin ()); result r (db->query<person> (query::age > )); for (result::iterator i (r.begin ()); i != r.end (); ++i)
{
cout << "Hello, " << i->first () << "!" << endl;
} t.commit ();
}
SmartDBEX的查询方式:
auto& query = dbex.Query<TestInfo>().Where(Var(TestInfo::CODE < and TestInfo::ID < ));
vector<TestInfo> v;
bool b = dbex.Get<TestInfo>(v);
呵呵,有点像吧,但哪个更直观简洁呢?
有待完善的地方
牺牲了好几个周末的时间做这个ORM,主要是ORM的关键技术的思路和实现花了不少时间,今天终于做完了第一个版本,算是对前段时间的思考和研究做个总结和交代吧。不过由于是业余时间开发,时间和精力有限,ORM接口目前只有一些基本功能,还不太完善,而且目前内部只支持sqlite,至于其它数据库的支持,等到后面再继续完善了。另外,库内部没有采用缓存和延迟加载等技术,不是不能加,是暂时没有时间和精力去继续完善了,而且性能已经满足我的要求了,优化完善的工作来日方长吧;后期还可以考虑支持结构体通过文件配置的方式生成,进一步增强灵活性。
欢迎使用
欢迎大家下载测试工程和源码并试用,发现问题请报告给我,我好完善。也期待有人愿意在此基础上和我一起完善它,使它成为一个优秀的c++ORM库,让c++开发人员的日子变得更美好,让c++的世界更加绚丽多彩。
c++11 boost技术交流群:296561497,欢迎大家来交流技术。
(原创)发布一个C++版本的ORM库SmartDB(一)的更多相关文章
- 发布一个C++版本的ORM库SmartDB
		
先简单说说ORM的优点: 提高开发效率,减少重复劳动,只和业务实体打交道,由业务实体自动生成sql语句,不用手写sql语句. 简单易用, 可维护性好. 隔离数据源,使得我们更换数据源时不用修改代码. ...
 - (原创)发布一个c++11开发的轻量级的并行Task库TaskCpp
		
TaskCpp简介 TaskCpp是c++11开发的一个跨平台的并行task库,它的设计思路来源于微软的并行计算库ppl和intel的并行计算库tbb,关于ppl和tbb我在前面有介绍.既然已经有了这 ...
 - 发布一个简单的knockout-easyui绑定库
		
最近做事情总是南辕北辙,拖延症越发严重了起来.原先计划早就要完成的这个项目也拖延了近两个月后总算勉勉强强发布了(最开始设想的部分功能就这么砍了,好吧纯粹个人太懒) knockout作为老牌的mvvm框 ...
 - 一个C++版本的Sqlite3封装--SmartDb
		
Sqlite是一个非常轻量级的开源数据库,在嵌入式系统中使用的比较多,存储管理数据非常方便,Sqlite库提供的基于C语言的API,用起来也挺简单,但是有一点不太好的就是API使用起来有些繁琐,另外就 ...
 - Node的关系型数据库ORM库:bookshelf
		
NodeJs 关系数据库ORM库:Bookshelf.js bookshelf.js是基于knex的一个关系型数据库的ORM库.简单易用,内置了Promise的支持.这里主要罗列一些使用的例子,例子就 ...
 - 教你一步步发布一个开源库到 JCenter
		
今天想来分享下,如何一步步自己发布一个开源库到 JCenter 这方面的博客网上已经特别多了,所以本篇并不打算仅仅只是记录流程步骤而已,而是尽可能讲清楚,为什么需要有这个步骤,让大伙知其然的同时还知其 ...
 - 深度学习库 SynapseML for .NET 发布0.1 版本
		
2021年11月 微软开源一款简单的.多语言的.大规模并行的机器学习库 SynapseML(以前称为 MMLSpark),以帮助开发人员简化机器学习管道的创建.具体参见[1]微软深度学习库 Synap ...
 - 发布 Vant - 高效的 Vue 组件库,再造一个有赞移动商城也不在话下
		
发布 Vant - 高效的 Vue 组件库,再造一个有赞移动商城也不在话下:https://segmentfault.com/a/1190000011377961 vantUI框架在vue项目中的应用 ...
 - Spring 5的最后一个特性版本5.3发布,4.3将于12月终止维护
		
10月27日,Spring Framework团队宣布了5.3版本正式GA,Spring用户可以在repo.spring.io和Maven Central上获取到最新版本的依赖包. JDK的版本支持 ...
 
随机推荐
- Oracle常用方法
			
oracle常用函数整理 时间转换 to_char to_date select to_char( sysdate, 'yyyy-mm') FROM dual; -- 2014-05 select t ...
 - StatefulSet和Deployment的区别
			
“Deployment用于部署无状态服务,StatefulSet用来部署有状态服务”. 具体的,什么场景需要使用StatefulSet呢?官方给出的建议是,如果你部署的应用满足以下一个或多个部署需求, ...
 - kubernetes-pod分配CPU和RAM资源以及计算方法
			
例子: Containers: cpu-ram-demo-container: ... image: busybox:latest resources: Limits: cpu: 1 memory: ...
 - iOS中coreData的用法
			
// // ViewController.m // coredatademo002 // // Created by ganchaobo on 13-6-29. // Copyright (c) 20 ...
 - [POST] What Is the Linux fstab File, and How Does It Work?
			
If you’re running Linux, then it’s likely that you’ve needed to change some options for your file sy ...
 - hql语句的case when then else end问题
			
http://www.iteye.com/problems/4499 hibernate count不支持case when?
 - numpy二分查找
			
a = np.array([1, 2, 2, 3]) print(np.searchsorted(a, 0)) # 0 print(np.searchsorted(a, 1)) # 0 print(n ...
 - 基于Android的小巫新闻客户端开发系列教程
			
<ignore_js_op> 141224c6n6x7wmu1aacap7.jpg (27.51 KB, 下载次数: 0) 下载附件 保存到相册 23 秒前 上传 <ignor ...
 - 进阶之路(基础篇) - 009 通过底层AVR方法实现SPI数据传输
			
主机端: /********************************* 代码功能:通过底层AVR方法实现SPI数据传输(主机端) 创作时间:2016*10*17 使用资源: 更低阶的 aTme ...
 - 【Android】Android实现Handler异步详解
			
方式不止一种,这里使用的是Timer类,创建一个定时器.我们经常需要获得移动设备端口的显示屏信息,但是onCreate()方法执行的时候,OnShow()方法不一定执行了,也就是说,在执行Oncrea ...