先简单说说ORM的优点:

  1. 提高开发效率,减少重复劳动,只和业务实体打交道,由业务实体自动生成sql语句,不用手写sql语句。
  2. 简单易用, 可维护性好。
  3. 隔离数据源,使得我们更换数据源时不用修改代码。

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(一)的更多相关文章

  1. 发布一个C++版本的ORM库SmartDB

    先简单说说ORM的优点: 提高开发效率,减少重复劳动,只和业务实体打交道,由业务实体自动生成sql语句,不用手写sql语句. 简单易用, 可维护性好. 隔离数据源,使得我们更换数据源时不用修改代码. ...

  2. (原创)发布一个c++11开发的轻量级的并行Task库TaskCpp

    TaskCpp简介 TaskCpp是c++11开发的一个跨平台的并行task库,它的设计思路来源于微软的并行计算库ppl和intel的并行计算库tbb,关于ppl和tbb我在前面有介绍.既然已经有了这 ...

  3. 发布一个简单的knockout-easyui绑定库

    最近做事情总是南辕北辙,拖延症越发严重了起来.原先计划早就要完成的这个项目也拖延了近两个月后总算勉勉强强发布了(最开始设想的部分功能就这么砍了,好吧纯粹个人太懒) knockout作为老牌的mvvm框 ...

  4. 一个C++版本的Sqlite3封装--SmartDb

    Sqlite是一个非常轻量级的开源数据库,在嵌入式系统中使用的比较多,存储管理数据非常方便,Sqlite库提供的基于C语言的API,用起来也挺简单,但是有一点不太好的就是API使用起来有些繁琐,另外就 ...

  5. Node的关系型数据库ORM库:bookshelf

    NodeJs 关系数据库ORM库:Bookshelf.js bookshelf.js是基于knex的一个关系型数据库的ORM库.简单易用,内置了Promise的支持.这里主要罗列一些使用的例子,例子就 ...

  6. 教你一步步发布一个开源库到 JCenter

    今天想来分享下,如何一步步自己发布一个开源库到 JCenter 这方面的博客网上已经特别多了,所以本篇并不打算仅仅只是记录流程步骤而已,而是尽可能讲清楚,为什么需要有这个步骤,让大伙知其然的同时还知其 ...

  7. 深度学习库 SynapseML for .NET 发布0.1 版本

    2021年11月 微软开源一款简单的.多语言的.大规模并行的机器学习库 SynapseML(以前称为 MMLSpark),以帮助开发人员简化机器学习管道的创建.具体参见[1]微软深度学习库 Synap ...

  8. 发布 Vant - 高效的 Vue 组件库,再造一个有赞移动商城也不在话下

    发布 Vant - 高效的 Vue 组件库,再造一个有赞移动商城也不在话下:https://segmentfault.com/a/1190000011377961 vantUI框架在vue项目中的应用 ...

  9. Spring 5的最后一个特性版本5.3发布,4.3将于12月终止维护

    10月27日,Spring Framework团队宣布了5.3版本正式GA,Spring用户可以在repo.spring.io和Maven Central上获取到最新版本的依赖包. JDK的版本支持 ...

随机推荐

  1. 第一个go的web程序;调用七牛云存储的音频api问题解决;条件搜寻文件中的内容,字符串拼接+在上一行

    package main import ( "html/template" "io" "io/ioutil" "log" ...

  2. vmware与virtualbox之对比分析

    2012-04-01 16:01:17        vmware与virtualbox之对比分析   测试过程:本机安装双系统xp.ubuntu:在每个系统中安装两种虚拟机:每个虚拟机虚拟一个Win ...

  3. Java Exceptions

    invalid end header( bad central directory size) 异常描述 java.util.zip.ZipException: invalid END header ...

  4. numpy文件读写的三对函数

    在Python很多库中,使用文件名的地方都可以使用文件对象来替代. 在下述三种方法中,都是如此. 一.a.tofile()和np.fromfile() numpy中的ndarray对象有一个函数tof ...

  5. java+win7+eclipse+Maven+sikuli 配置总结---图形脚本语言

    简介:Sikuli 是一种新颖的图形脚本语言,或者说是一种另类的自动化测试技术.它与我们常用的自动化测试技术(工具)有很大的区别. 关于配置,一直是一个问题,下面做个总体介绍,用sikuli也有几个月 ...

  6. iOS 相册相机应用2

    在iOS中要拍照和录制视频最简单的方式就是调用UIImagePickerController,UIImagePickerController继承与UINavigationController,需要使用 ...

  7. RSA加密异常

    在利用RSA进行数据加密时,出现如下异常: Exception bytes at com.sun.crypto.provider.RSACipher.a(DashoA13*..) at com.sun ...

  8. 一步一步掌握java的线程机制(一)----创建线程

    现在将1年前写的有关线程的文章再重新看了一遍,发现过去的自己还是照本宣科,毕竟是刚学java的人,就想将java的精髓之一---线程进制掌握到手,还是有点难度.等到自己已经是编程一年级生了,还是无法将 ...

  9. SharePoint 2013 Designer工作流——Parallel Block的应用

    参考目录 安装和配置SharePoint 2013 Workflow SharePoint 2013 实现多级审批工作流 在自定义Workflow时,往往会遇到这样场景,某个审批需要被多人查阅,每个查 ...

  10. SQL 给字符串补0

    第一种方法: right('00000'+cast(@count as varchar),5) 其中'00000'的个数为right函数的最后参数,例如这里是5,所以有5个0 @count就是被格式化 ...