C++实现的ORM映射工具

 

1      OrmDao简介

1.1.    对象关系-映射简介

对像和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,数据库中表现为关系数据, 内存中的对象之间存在关联和继承关系, 而在数据库中,关系数据无法直接表达多对多关联和继承关系, 因此,对象-关系映射(ORM)系统一般以中间件的形式存在, 主要实现程序对象到关系数据库数据的映射.

在开发关系数据库的系统时,可以通过SQL 语句读取及操作关系数据库数据..c++领域,可以直接通过数据库厂商提供的内嵌SQL编程来访问数据库,这可以说是访问关系数据库的最原始、最直接的方法,这种方式的优点是运行效率高,缺点是在ESQL程序代码中嵌入大量SQL 语句,冗余是不可避免的,开发人员常常发现自己在一次又一次地编写相同的普通代码, 如获得连接准备语句、循环结果集以及其他一些 ESQL特定元素特别是当涉及到非常多的关系数据表、需要在多个不同类型的关系数据库系统中使用时, 通过在程序中使用ESQL开发实施起来更加困难.

1.2.    如何实现对象-关系映射

     通过引入对象-关系映射系统,实现数据库的快速开发, 可以通过ESQL编程来开发单独的持久化层, 把数据库访问操作封装起来, 提供简洁的API,供业务层统一调用,实现自己的ORM.

1.3为什么要设计OrmDao

要使用c++访问数据库,我们都知道使用ESQL,然而ESQL相对来说毕竟是一个比较底层的数据库访问API, 在实际应用中需要写非常多的SQL 语句, 管理数据源, 处理数据访问异常等诸多工作, 造成开发的极为不便, 而且由于不同数据库之间的sql语句存在一定的差异, 也使得我们写的程序难免捆绑到某一种数据库上, 无法实现程序灵活的在不同的数据库之间进行移植.

近年来,对象关系映射系统得到广泛的应用,java领域,随着以iBatishibernate 为代表的轻量级, 我们基本上可以告别JDBC,告诉烦琐的SQL 语句,

同时也解决了前面提出的问题.因此在羡慕java开发者的同时,有必要设计一个用c++实现的简单的ORM映射的工具,提高c++开发者的开发效率,从而达到统一后台数据库开发的目的.

从本质上说,OrmDao是一个对ESQL的简单封装,通过借鉴当前的主流ORM 系统, 引入了更加简单实用的方式来实现对象及关系数据库的映射, 从而使得我们在开发中可以把精力更多的集成中在域建模及软件业务逻辑上面.

OrmDao在典型三层结构中所处位置:

 

2.OrmDao使用帮助

2.1      OrmDao组成

OrmDao主要包括三部分:

2.1.1 CodeGen

因为c++没有java那样的动态反射能力,不能在运行时得到一些我们所需要的信息,所以在使用OrmDao时我们需要做一些额外的工作,OrmDao知道运行时的信息,这个就需要借助CodeGen来自动生成我们所需要的信息, CodeGen工具借助读取配置的xml文件,来自动生成代码,不需要手工干预.单独编译成为可执行程序.

2.1.2 DBDao

一个小巧的框架,实现OrmDao映射的主要功能,向外提供简洁的API接口.作为一个单独编译的静态库提供.

2.1.3 数据库层模块

按照功能模块划分访问数据库表,该模块以动态库形式提供,在动态库里面包装了若干个实体对象,而实体对象又对应于数据库中的关系表.

2.1.4 OrmDao组成结构图

 

 

2.2 OrmDao快速上手

下面是我们使用OrmDao实现的一个简单例子,以订单主表,订单明细的增删改查为例(表字段信息在test.xml文件进行了配置),Windows平台(Unix平台需要稍作修改)介绍使用OrmDao开发的步骤

2.2.1 OrmDao映射声明

 

 

 

 

 

2.2.1.1配置全局的xml文件:global-config.xml:

 <?xml version="1.0" encoding="utf-8"?>
<global-config>
   <datasouce>
  <driver>oracle</driver>
  <username>hlb</username>
  <passward>hlb</passward>
  <servername>orcl</servername>
  <initconn>2</initconn>
  <maxconn>4</maxconn>
  <showsql>true</showsql>
 </datasouce>
 <models>
   <model value="E://c++//DBMaping//DBDao//test.xml" />
 </models>
</global-config>说明:
1.<datasouce>数据源标签用于设置数据库连接信息;
2. <models>  模块标签用于指定具体的数据库层的访问模块;
 

2.2.1.2 配置数据库层模块的xml : test.xml:

<?xml version="1.0" encoding="utf-8"?>
<model name="TestBill" path="E://c++//DBMaping//debug//TestBill.dll">
 <tables>
  <table name="bill" class="Bill" catalog="hlb" >
     <column name="bill_id"       type="varchar2" length="10" description=""/>
     <column name="amount_total"  type="integer"  length=""   description=""/>
     <column name="fee_total"     type="double"   length=""   description=""/>
     <column name="create_dt"     type="date"     length=""   description=""/>
     <primaryKey columns="bill_id"/>
  </table>
  <table name="bill_detail" class="BillDetail" catalog="hlb" >
     <column name="bill_id"       type="varchar2" length="10" description=""/>
     <column name="seq_no"        type="integer"  length=""   description=""/>
     <column name="product_id"    type="varchar2" length="10" description=""/>
     <column name="amount"        type="integer"  length=""   description=""/>
     <column name="fee"           type="double"   length=""   description=""/>
     <primaryKey columns="bill_id,seq_no"/>
  </table>
 </tables>
</model>说明:
1. <model>标签的name表示模块名称,path指的是编译之后动态库的路径,
在Windows平台是dll,Unix平台是so库.
2.<tables>下面包含一组<table>子标签,对应于数据库里面表的字段信息;
3.可以配置多个<table>标签,对应多个表,这些表对应的对象统一通过path指定的动态库访问.

2.2.2 OrmDao接口API说明

2.2.2.1 OrmDao接口基类API说明

template <typename T>
class OrmDBBase{
public:
    /*保存单个对象*/
     virtual int save(T t)=0;
    /*更新单个对象:主键作为条件*/
     virtual int update(T t)=0;
    /*删除单个对象*/
     virtual int del(T t)=0;
    /*查询语句*/
     virtual list<T> selList(string selSql)=0;
};
 

2.2.2.2 OrmDao接口API说明

 

 

template <typename T>
class OrmDao{
private:
 Connection *pConnection;
public:
 OrmDao(Connection *pConnection)
 {
  this->pConnection=pConnection;
 }
 ~OrmDao()
 {
  OrmDBManager<T>::Destroy();
 }
 /*保存单个对象*/
 int save(T t)
 {
  try{
   return OrmDBManager<T>::getInstance(pConnection)->save(t);
  }catch(ORMException &e){
   throw;
  }
 }
 /*更新单个对象:主键作为条件*/
 int update(T t)
 {
  try{
   return OrmDBManager<T>::getInstance(pConnection)->update(t);
  }catch(ORMException &e){
   throw;
  }
 }
 /*删除单个对象*/
 int del(T t)
 {
  try{
   return OrmDBManager<T>::getInstance(pConnection)->del(t);
  }catch(ORMException &e){
   throw;
  }
 }
 /*查询语句*/
 list<T> selList(string selSql)
 {
  try{
   return OrmDBManager<T>::getInstance(pConnection)->selList(selSql);
  }catch(ORMException &e){
   throw;
  }
 }
};
 

 

2.2.3使用CodeGen生成代理对象

2.2.2.1生成Proxy对象

   使用CodeGen读取配置的xml模块文件,自动生成相关的Proxy文件,命令如下:

CodeGen test.xml

    执行后自动生成如下文件:

文件名

描述

Bill.hpp

Bill对象头文件

Bill.cpp

Bill对象实现文件

Bill_Proxy.hpp

Bill代理对象头文件

Bill_Proxy.cpp

Bill代理对象实现文件

BillDetail.hpp

BillDetail对象实现文件

BillDetail.cpp

BillDetail对象实现文件

BillDetail_Proxy.hpp

BillDetail代理对象头文件

BillDetail_Proxy.cpp

BillDetail代理对象实现文件

在加上我们预先定义好的接口文件:

 

class BeanProxy{
public:
     virtual int execVoidMethod(const std::string& method, void* Prams)=0;

     virtual void setBean(void* p)=0;

     virtual void* execGetMethod(const std::string& method)=0;

     virtual void delPtr()=0;
};

 

2.2.3 编写BS(业务)层的代码

2.2.3.1新建BS业务层工程

2.2.3.2引入DBDao

BS层的工层里面,引入DBDao框架所编译的静态库lib(.a)

2.2.3.3编译数据库层为动态库

    用前面CodeGen代码自动生成的c++文件,将数据库层编译为动态库的形式,以供业务层调用.

   Windows平台为例,VC环境下新建一个dll的工层,将上述9个相关的c++文件加入到工程里面,编译为test.xml配置的TestBill.dll动态库,Unix平台下也是如此,编译为libbilldb.so. 

 

2.2.3.4编写实际测试代码

增加业务操作:

 

void BillOperAdd()
{
 Connection conn;
 OrmDao<Bill> billDao(&conn);
 OrmDao<BillDetail> billDetailDao(&conn);
 Bill bill;
 bill.setBillId("100001");
 BillDetail oneDetail,twoDetail;
 oneDetail.setBillId(bill.getBillId());
 oneDetail.setSeqNo(1);
 oneDetail.setProductId("0001");
 oneDetail.setAmount(3);
 oneDetail.setFee(15.72);
 twoDetail.setBillId(bill.getBillId());
 twoDetail.setSeqNo(2);
 twoDetail.setProductId("0002");
 twoDetail.setAmount(5);
 twoDetail.setFee(12.32);
 CDateTime dt;
 dt=CDateTime::getCurrentTime();
 bill.setCreateDt(dt);
 bill.setAmountTotal(oneDetail.getAmount()+twoDetail.getAmount());
 bill.setFeeTotal(oneDetail.getAmount()*oneDetail.getFee()+twoDetail.getAmount()*twoDetail.getFee());
 try{
  billDao.save(bill);
  billDetailDao.save(oneDetail);
  billDetailDao.save(twoDetail);
  conn.commit();
 }catch(ORMException &e){
  cout<<e.getMessage()<<endl;
  conn.rollback();
 }
}


查找删除操作:

void BillOper()
{
 Connection conn;
 OrmDao<Bill> billDao(&conn);
 OrmDao<BillDetail> billDetailDao(&conn);
 try{
  //查询数据
  list<Bill> billList=billDao.selList("select * from bill");
  list<Bill>::iterator itBill=billList.begin();
  while(itBill!=billList.end()){
   Bill tmpBill=*itBill;
   list<BillDetail> billDetailList=billDetailDao.selList("select bill_id,seq_no from bill_detail where bill_id='"+tmpBill.getBillId()+"'");
   list<BillDetail>::iterator itBillDetail=billDetailList.begin();
   while(itBillDetail!=billDetailList.end()){
    BillDetail tmpBillDetail=*itBillDetail;
    billDetailDao.del(tmpBillDetail);
    itBillDetail++;
   }
   billDao.del(tmpBill);
   itBill++;
  }
  conn.commit();
 }catch(ORMException &e){
  cout<<e.getMessage()<<endl;
  conn.rollback();
 }
}


3.OrmDao优点

OrmDao的优点是开发简单,快速,只需要配置好xml文件,利用CodeGen自动生成相关的c++文件,编译为动态库即可,数据库层模块化,能够做到数据库层,业务层,应用层各个模块的清晰的划分.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章