C++ ORM ODB 入門介紹(一)

:::歡迎廣大oscer拍磚和交流. mysql

C++的語言特性決定了在C++中的ORM框架不可能像Java,C#那沒有那麼靈活。 sql

C++的ORM框架通常都是基於模板,編譯時,所以其效率比起Java中的ORM框架更高。 數據庫

ODB是一個比較獨立,成熟的基於C++Template的ORM框架。使用#pragma編譯指令和ODB.exe編譯器生成SQL的特化版本。#pragma指令,對於熟悉Java的ORM映射的oscer,能夠認爲和Java的註解相似。只不過Java的註解是運行時,而C++#pragma的指令是編譯時。 框架

ODB中的類的#pragma(註解)包含2種。 socket

#pragma db value 和 #pragma db object post

value所註解的類的對象,不能夠成爲一個獨立的數據庫對象。通常映射爲數據庫的1或者多列。若是有位value類茲自定義trait類,則能夠映射爲一列,不然默認狀況下value類的每一個字段映射一個列。固然value必須被某個object類所包含,並在object類所對應的表中線性化的展開列。 this

object所註解的類,會映射一個或者多個表格(當有集合成員時)。每一個持久化的object對象表明數據庫表格中的一行。默認狀況下object類的每一個原始數據成員(包含std::string)都會映射爲數據庫表格的一列,除非使用編譯指令#pragma db transient 把字段聲明爲臨時的,也就是該字段不保存與數據庫中。 spa

關於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
相關文章
相關標籤/搜索