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++文件,編譯爲動態庫即可,數據庫層模塊化,能夠做到數據庫層,業務層,應用層各個模塊的清晰的劃分.

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