C++的語言特性決定了在C++中的ORM框架不可能像Java,C#那沒有那么靈活。
C++的ORM框架一般都是基於模板,編譯時,因此其效率比起Java中的ORM框架更高。
ODB是一個比較獨立,成熟的基於C++Template的ORM框架。使用#pragma編譯指令和ODB.exe編譯器生成SQL的特化版本。#pragma指令,對於熟悉Java的ORM映射的oscer,可以認為和Java的注解類似。只不過Java的注解是運行時,而C++#pragma的指令是編譯時。
ODB中的類的#pragma(注解)包含2種。
#pragma db value 和 #pragma db object
value所注解的類的對象,不可以成為一個獨立的數據庫對象。一般映射為數據庫的1或者多列。如果有位value類茲自定義trait類,則可以映射為一列,否則默認情況下value類的每個字段映射一個列。當然value必須被某個object類所包含,並在object類所對應的表中線性化的展開列。
object所注解的類,會映射一個或者多個表格(當有集合成員時)。每個持久化的object對象代表數據庫表格中的一行。默認情況下object類的每個原始數據成員(包含std::string)都會映射為數據庫表格的一列,除非使用編譯指令#pragma db transient 把字段聲明為臨時的,也就是該字段不保存與數據庫中。
關於object的id,一般情況下數據庫的每個對象都應該是唯一的,那么就需要有某個列或者多列來唯一標識,是的在整個表格中是唯一的。object類中只能指定一個字段作為對象的id,正常情況下是對應數據庫的一列。如果想要指定多列組合為對象的id,需要使用value類對象作為object類的一個成員。不過是普通數據成員,還是value數據成員作為id,都需要使用#pragma db id來注解。數據庫的id中(mysql,sqlite都有自動增長字段)的auto指令,可以讓數據庫自己管理id,在持久化的時候,自動分配給object對象一個id,但是要求id的數據類型必須是整形。ODB也支持沒有id的object類,但是那樣的話,就無法使用find或者load等方法加載持久對象了。只能通過查詢語句加載對象。
關於集合:OBD支持vector、list、set、multiset等有序和無序的集合,還支持map、mulitimap
對於集合的嵌套目前只支持一層(如果是對象集合則不受此限制,此處是值集合)。除非使用trait把集合映射為一列,因為默認情況下集合映射為一個表格,並通過外鍵和自己所在的object類的表格關聯起來。集合最好不要放在value類中,除非你能保證該value類不會被當做其它集合的成員,否則就出現了多層嵌套集合,這個目前ODB是無法處理的。
ODB對象間關系:一個object可以包含另外一個object(只能是指針),就形成了object與object之間的關系了。包含一對一單向,一對一雙向,一對多單向,一對多雙向,多對多雙向等5種關系。
一對一單向最簡單,支持RAW ptr,shared_ptr,weak_ptr等。建議使用shared_ptre或者weak_ptr,可以自動管理對象的生命周期。一對一單向可能是唯一所有權,也可能是共享所有權。例如有object A,B兩個類,其中object A包含一個object B的對象。如果任何一個object B的對象只被一個object A對象所包含的話,那么最好使用shared_ptr,如果同時有可能多個object A包含 object B對象的話,最好使用weak_ptr。
對於雙向對象,建議一邊使用shared_ptr,一邊使用weak_ptr,否則在釋放對象的時候還需要手動解引。默認情況下數據庫表格會保存雙方的id,也就是tblA有一個colBid,tblB有一個colAid,但是一般情況下沒有必須要,可以在一邊使用#pragma db reverse(雙向引用對方的字段名稱),來告訴數據庫,從對方表格獲得次對象。
一對多或者多對多,也就是集合中的數據是object對象. ODB編譯器會創建一個表格用於關聯2個對象之間的關系。同樣建議使用shared_ptr\weak_ptr組合。對於多對多無法使用#pramga db reverse,但是對於一對多,則建議在集合的一方使用#pragma db reverse
關於繼承體系。一般情況下我們的業務數據都有一定的體系結構的。例如學校的老師和學生,都是從person類對象繼承而來的。ODB2.0及其以后的版本對此支持已經比較完善,而且也支持動態加載。在1.5版本的時候,即使teacher和student都繼承自person,都不能使用load(name)或者find(name)來加載一個person,必須知道次name究竟是teacher還是student。2.0版本就可以直接使用了,因為在數據庫內部已經保存了某條對象的類類型。使用起來和普通的C++類繼承完全一樣。想要讓某個object成為支持動態聯編的基類,在#pragma db object 的時候,必須在附加上polymorphic指令,也就是 #pragma db object polymorphic,只有基類才需要這樣哦。如果是派生類則和普通的object一樣的編譯指令即可。
關於dbevent:數據庫有觸發器。dbevent是OBD提供的基於框架級別的觸發器。供有6種類型的觸發器。
pre_persist : 對象持久化之前被調用,再次可以拋出某些異常阻止對象被持久化。例如檢查某個字段是否合法,檢查某些對象的指針是否必須不能為null等。
post_persist:對象持久化之后被調用,再次可以做某些通知,關聯之類的操作。
pre_load:對象從數據庫第一次或者重新加載時,拋出異常,阻止加載。
post_load: 對象已經從數據庫加載完成。
pre_update : 更新對象的內存信息到數據庫之前。拋出異常阻止加載,此時拋出異常,會導致內存的數據和數據庫數據不匹配,可以通過load方法從數據庫加載最新的內容。
post_update : 數據更新完成。
pre_erase : 從數據庫刪除之前。可以拋出異常,阻止刪除。
post_erase: 已經從數據庫刪除。但是內存對象並沒有刪除,需要自己刪除,此時不能在此對象上調用load,update,erase操作。否則ODB會拋出異常。如果涉及到對象關系,再次可以解引用。
實例:一下是本人某工程的一個簡單實例:
#ifndef __DATA_MODEL_H__ #define __DATA_MODEL_H__ #include <memory> #include <string> #include <set> #include <list> #include <vector> // ODB相關頭文件. #include <odb/database.hxx> #include <odb/tr1/memory.hxx> #include <odb/callback.hxx> #include <ace/Thread_Mutex.h> #include <ace/Time_Value.h> using namespace std::tr1; class Entity; class Zone; class Terminal; class User; enum EntityType { TERMINAL = 0, USER = 1, ZONE = 3 }; /* object 告訴ODB編譯器,Entity為一個持久類。 table 告訴 ODB編譯器,Entity類對應的表名稱為tblEntity,如果沒有改指令則表名與類名相同 polymorphic 告訴編譯器,Entity是一個虛擬基類,數據庫中並不存在tblEntity的直接對象。有點類似與C++的虛擬基類(當在此處,Entity不能有任何未實行的虛擬成員) callback 指定語言級別的觸發器。需要包含一個const版本和一個非const版本,其它簽名完全相同. */ #pragma db object table("tblEntity") polymorphic callback(dbevent) class Entity : public enable_shared_from_this<Entity> { //使得odb.exe生成的trait代碼可以直接讀取字段成員,否則需要告訴odb編譯器對某個字段的get.set方法分別是啥. friend class odb::access; public: Entity(); Entity(const Entity & other); Entity & operator=( const Entity &other); public: virtual void dbevent( odb::callback_event e, odb::database& db ); virtual void dbevent( odb::callback_event e, odb::database& db ) const; virtual EntityType GetType() const; void SetType( EntityType t); public: //get set unsigned int GetId() const; const std::string & GetName() const; //std::string & GetName(); shared_ptr<Zone> GetParent() const ; void SetId(unsigned int value); void SetName(const std::string & value); void SetParent( const shared_ptr<Zone> & value) ; private: //指定id和自動增長。 #pragma db id auto unsigned int id_; std::string name_; shared_ptr<Zone> parent_; //臨時自動,不保存與數據庫. #pragma db transient EntityType type_; }; #pragma db object table("tblZone") callback(dbevent) class Zone : public Entity { friend class odb::access; public: Zone(); Zone(const Zone & other); Zone & operator=( const Zone & other); virtual EntityType GetType() const; public: void Add( const shared_ptr<Entity> & en); void Remove( const shared_ptr<Entity> & en); shared_ptr<Entity> Find( unsigned int id, bool recursive = true); shared_ptr<Entity> Find( const std::string & name , bool recursive = true); shared_ptr<Terminal> FindTerminalByNtid( const std::string & macAddress); public: //get set const std::set < shared_ptr < Entity > > & GetMembers() const; /* std::set < shared_ptr < Entity > > & GetMembers(); */ /*for Zone CPlusPlusSet*/ void SetMembers( const std::set < shared_ptr < Entity > > &value) ; private: //與Entity類是一對多關系,使用inverse告訴odb編譯器反向字段(必須,因為Entity可能包含多個Zone字段). //ODB編譯器不會為members_創建一個對象關聯表格,在加載的時候通過SQL語句在tblEntity表格中查詢members_。 #pragma db inverse(parent_) std::set< shared_ptr<Entity> > members_; #pragma db transient mutable ACE_Thread_Mutex mutex_; }; //設備端口類型..fxs ( 0 ) , ivr ( 1 ) , group ( 2 ) , fxo ( 3 ) , gsm ( 4 ) enum PortType { FXS = 0, IVR = 1, Group = 2, FXO = 3 , GSM = 4 , UNKNOWN = 5 }; //呼叫協議 enum ProtocolType { SIP = 0, MGCP = 1, H248 = 2, H323 = 3 }; // 設備操作系統類型 enum SystemType { Linux = 0, Vxworks = 1 }; enum TerminalStatus { OffLine = 0, OnLine = 1, DropLine = 2 }; #pragma db object table("tblTerminal") callback(dbevent) class Terminal : public Entity { friend class odb::access; public: Terminal(); Terminal( const Terminal & other); Terminal & operator=( const Terminal & other); virtual EntityType GetType() const; void dbevent( odb::callback_event e, odb::database& db ); void dbevent( odb::callback_event e, odb::database& db ) const; public: //get set const std::string & GetMacAddress() const; //std::string & GetMacAddress(); bool GetStaticAddress() const; const std::string & GetIpAddress() const; //std::string & GetIpAddress(); unsigned int GetSnmpPort() const; bool GetDynamicPorts() const; const std::vector < PortType > & GetPorts() const; std::vector < PortType > & GetPorts(); ProtocolType GetProtocol() const; unsigned int GetTimeToLive() const; TerminalStatus GetOnline() const; bool GetNat() const; const std::string & GetAgentAddress() const; void SetMacAddress(const std::string & value); void SetStaticAddress( bool value); void SetIpAddress(const std::string & value); void SetSnmpPort(unsigned int value); void SetDynamicPorts( bool value); void SetPorts( const std::vector < PortType > &value) ; void SetProtocol( ProtocolType value); void SetTimeToLive(unsigned int value); void SetOnline( TerminalStatus value); void SetOnlineTime(const std::string & value); void SetOfflineTime(const std::string & value); void SetDroplineTime(const std::string & value); void SetDeviceType(const std::string & value); void SetSoftwareVersion(const std::string & value); void SetHardwareVersion(const std::string & value); void SetRunningDuration( const std::string & value); void SetNat( bool value); void SetAgentAddress(const std::string & value); unsigned int GetTotalPorts() const ; void SetTotalPorts(unsigned int val) ; SystemType GetSystemType() const ; void SetSystemType(SystemType val) ; void SetRecvKeepalive() const; bool IsTimeout() const; int GetTTLMinUnit() const; bool HasTimer() const; void SetTimer( bool val); const std::string & GetOnlineTime() const; std::string & GetOnlineTime(); const std::string & GetOfflineTime() const; std::string & GetOfflineTime(); const std::string & GetDroplineTime() const; std::string & GetDroplineTime(); const std::string & GetDeviceType() const; std::string & GetDeviceType(); const std::string & GetSoftwareVersion() const; std::string & GetSoftwareVersion(); const std::string & GetHardwareVersion() const; std::string & GetHardwareVersion(); const std::string & GetRunningDuration() const; private: //使用MAC地址作為區分設備的唯一標識. std::string mac_address_; // true ip_address_ & port_字段的內容由用戶提供,否則通過socket地址獲取. bool static_address_; std::string ip_address_; unsigned int snmp_port_; //設備端口集合. // true 表示 ports_的內容通過SNMP Trap消息獲得,否則為用戶新建設備時提供。 bool dynamic_ports_; //設備端口個數..4,8,16,32 unsigned int total_ports_; std::vector< PortType > ports_; SystemType system_type_; //VOIP呼叫協議 ProtocolType protocol_; // 保活時間,接收到2個注冊包或者保活包之間的最大間隔時間。超過此時間,系統會把online_設置為false. unsigned int time_to_live_ ; //設備是否在線 #pragma db transient TerminalStatus online_; #pragma db transient mutable int last_keepalive_; #pragma db transient mutable bool tiemr_id_; #pragma db transient std::string online_time_; #pragma db transient std::string offline_time_; #pragma db transient std::string dropline_time_; #pragma db transient std::string device_type_; #pragma db transient std::string software_version_ ; #pragma db transient std::string hardware_version_; #pragma db transient std::string running_duration_; #pragma db transient bool nat_; #pragma db transient std::string agent_address_; }; #pragma db object table("tblUser") callback(dbevent) class User : public Entity { friend class odb::access; public: User(); User( const User & other); User & operator=( const User & other); virtual EntityType GetType() const; public: //get set const std::string & GetPassword() const; //std::string & GetPassword(); bool GetAdministrator() const; void SetPassword(const std::string & value); void SetAdministrator( bool value); bool GetOnline() const ; void SetOnline(bool val) ; private: // 登陸密碼 std::string password_; // 是否為管理員,true 管理員,false 操作員 bool administrator_; #pragma db transient bool online_; }; #endif
聲明:OSCHINA 博客文章版權屬於作者,受法律保護。未經作者同意不得轉載。
No tags for this post.