C++ ORM ODB 入门介绍(一)

:::欢迎广大oscer拍砖和交流.

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会抛出异常。如果涉及到对象关系,再次可以解引用。

实例:一下是本人某工程的一个简单实例:

1#ifndef __DATA_MODEL_H__ 2#define __DATA_MODEL_H__ 3 4#include <memory> 5#include <string> 6#include <set> 7#include <list> 8#include <vector> 9 10// ODB相关头文件. 11#include <odb/database.hxx> 12#include <odb/tr1/memory.hxx> 13#include <odb/callback.hxx> 14 15#include <ace/Thread_Mutex.h> 16#include <ace/Time_Value.h> 17 18using namespace std::tr1; 19 20 21 22 23class Entity; 24class Zone; 25class Terminal; 26class User; 27 28 29enum EntityType { 30 TERMINAL = 0, 31 USER = 1, 32 ZONE = 3 33}; 34/* 35object 告诉ODB编译器,Entity为一个持久类。 36table 告诉 ODB编译器,Entity类对应的表名称为tblEntity,如果没有改指令则表名与类名相同 37polymorphic 告诉编译器,Entity是一个虚拟基类,数据库中并不存在tblEntity的直接对象。有点类似与C++的虚拟基类(当在此处,Entity不能有任何未实行的虚拟成员) 38callback 指定语言级别的触发器。需要包含一个const版本和一个非const版本,其它签名完全相同. 39*/ 40 41#pragma db object table("tblEntity") polymorphic callback(dbevent) 42class Entity : public enable_shared_from_this<Entity> 43{ 44 //使得odb.exe生成的trait代码可以直接读取字段成员,否则需要告诉odb编译器对某个字段的get.set方法分别是啥. 45 friend class odb::access; 46public: 47 Entity(); 48 Entity(const Entity & other); 49 Entity & operator=( const Entity &other); 50public: 51 52 virtual void dbevent( odb::callback_event e, odb::database& db ); 53 virtual void dbevent( odb::callback_event e, odb::database& db ) const; 54 virtual EntityType GetType() const; 55 void SetType( EntityType t); 56public: 57 //get set 58 unsigned int GetId() const; 59 60 const std::string & GetName() const; 61 //std::string & GetName(); 62 63 shared_ptr<Zone> GetParent() const ; 64 65 void SetId(unsigned int value); 66 void SetName(const std::string & value); 67 void SetParent( const shared_ptr<Zone> & value) ; 68 69 70 71private: 72 //指定id和自动增长。 73#pragma db id auto 74 unsigned int id_; 75 std::string name_; 76 shared_ptr<Zone> parent_; 77 //临时自动,不保存与数据库. 78#pragma db transient 79 EntityType type_; 80}; 81 82#pragma db object table("tblZone") callback(dbevent) 83class Zone : public Entity 84{ 85 friend class odb::access; 86public: 87 Zone(); 88 Zone(const Zone & other); 89 Zone & operator=( const Zone & other); 90 91 virtual EntityType GetType() const; 92public: 93 void Add( const shared_ptr<Entity> & en); 94 void Remove( const shared_ptr<Entity> & en); 95 shared_ptr<Entity> Find( unsigned int id, bool recursive = true); 96 shared_ptr<Entity> Find( const std::string & name , bool recursive = true); 97 shared_ptr<Terminal> FindTerminalByNtid( const std::string & macAddress); 98 99public: 100 //get set 101 const std::set < shared_ptr < Entity > > & GetMembers() const; 102/* 103 std::set < shared_ptr < Entity > > & GetMembers(); 104*/ 105 106 /*for Zone CPlusPlusSet*/ 107 void SetMembers( const std::set < shared_ptr < Entity > > &value) ; 108private: 109 //与Entity类是一对多关系,使用inverse告诉odb编译器反向字段(必须,因为Entity可能包含多个Zone字段). 110 //ODB编译器不会为members_创建一个对象关联表格,在加载的时候通过SQL语句在tblEntity表格中查询members_。 111#pragma db inverse(parent_) 112 std::set< shared_ptr<Entity> > members_; 113#pragma db transient 114 mutable ACE_Thread_Mutex mutex_; 115}; 116//设备端口类型..fxs ( 0 ) , ivr ( 1 ) , group ( 2 ) , fxo ( 3 ) , gsm ( 4 ) 117enum PortType 118{ 119 FXS = 0, 120 IVR = 1, 121 Group = 2, 122 FXO = 3 , 123 GSM = 4 , 124 UNKNOWN = 5 125}; 126//呼叫协议 127enum ProtocolType 128{ 129 SIP = 0, 130 MGCP = 1, 131 H248 = 2, 132 H323 = 3 133}; 134// 设备操作系统类型 135enum SystemType 136{ 137 Linux = 0, 138 Vxworks = 1 139}; 140enum TerminalStatus 141{ 142 OffLine = 0, 143 OnLine = 1, 144 DropLine = 2 145}; 146#pragma db object table("tblTerminal") callback(dbevent) 147class Terminal : public Entity 148{ 149 friend class odb::access; 150public: 151 Terminal(); 152 Terminal( const Terminal & other); 153 Terminal & operator=( const Terminal & other); 154 155 virtual EntityType GetType() const; 156 157 void dbevent( odb::callback_event e, odb::database& db ); 158 void dbevent( odb::callback_event e, odb::database& db ) const; 159public: 160 //get set 161 const std::string & GetMacAddress() const; 162 //std::string & GetMacAddress(); 163 164 bool GetStaticAddress() const; 165 166 const std::string & GetIpAddress() const; 167 //std::string & GetIpAddress(); 168 169 unsigned int GetSnmpPort() const; 170 bool GetDynamicPorts() const; 171 172 const std::vector < PortType > & GetPorts() const; 173 std::vector < PortType > & GetPorts(); 174 175 ProtocolType GetProtocol() const; 176 unsigned int GetTimeToLive() const; 177 TerminalStatus GetOnline() const; 178 bool GetNat() const; 179 const std::string & GetAgentAddress() const; 180 181 void SetMacAddress(const std::string & value); 182 void SetStaticAddress( bool value); 183 void SetIpAddress(const std::string & value); 184 void SetSnmpPort(unsigned int value); 185 void SetDynamicPorts( bool value); 186 void SetPorts( const std::vector < PortType > &value) ; 187 void SetProtocol( ProtocolType value); 188 void SetTimeToLive(unsigned int value); 189 void SetOnline( TerminalStatus value); 190 void SetOnlineTime(const std::string & value); 191 void SetOfflineTime(const std::string & value); 192 void SetDroplineTime(const std::string & value); 193 void SetDeviceType(const std::string & value); 194 void SetSoftwareVersion(const std::string & value); 195 void SetHardwareVersion(const std::string & value); 196 void SetRunningDuration( const std::string & value); 197 void SetNat( bool value); 198 void SetAgentAddress(const std::string & value); 199 200 201 unsigned int GetTotalPorts() const ; 202 void SetTotalPorts(unsigned int val) ; 203 204 SystemType GetSystemType() const ; 205 void SetSystemType(SystemType val) ; 206 207 void SetRecvKeepalive() const; 208 bool IsTimeout() const; 209 int GetTTLMinUnit() const; 210 211 bool HasTimer() const; 212 void SetTimer( bool val); 213 214 215 const std::string & GetOnlineTime() const; 216 std::string & GetOnlineTime(); 217 218 219 const std::string & GetOfflineTime() const; 220 std::string & GetOfflineTime(); 221 222 223 const std::string & GetDroplineTime() const; 224 std::string & GetDroplineTime(); 225 226 227 const std::string & GetDeviceType() const; 228 std::string & GetDeviceType(); 229 230 231 const std::string & GetSoftwareVersion() const; 232 std::string & GetSoftwareVersion(); 233 234 235 const std::string & GetHardwareVersion() const; 236 std::string & GetHardwareVersion(); 237 238 const std::string & GetRunningDuration() const; 239private: 240 //使用MAC地址作为区分设备的唯一标识. 241 std::string mac_address_; 242 // true ip_address_ & port_字段的内容由用户提供,否则通过socket地址获取. 243 bool static_address_; 244 std::string ip_address_; 245 unsigned int snmp_port_; 246 247 248 //设备端口集合. 249 // true 表示 ports_的内容通过SNMP Trap消息获得,否则为用户新建设备时提供。 250 bool dynamic_ports_; 251 //设备端口个数..4,8,16,32 252 unsigned int total_ports_; 253 std::vector< PortType > ports_; 254 SystemType system_type_; 255 256 //VOIP呼叫协议 257 ProtocolType protocol_; 258 259 // 保活时间,接收到2个注册包或者保活包之间的最大间隔时间。超过此时间,系统会把online_设置为false. 260 unsigned int time_to_live_ ; 261 //设备是否在线 262#pragma db transient 263 TerminalStatus online_; 264#pragma db transient 265 mutable int last_keepalive_; 266#pragma db transient 267 mutable bool tiemr_id_; 268#pragma db transient 269 std::string online_time_; 270#pragma db transient 271 std::string offline_time_; 272#pragma db transient 273 std::string dropline_time_; 274#pragma db transient 275 std::string device_type_; 276#pragma db transient 277 std::string software_version_ ; 278#pragma db transient 279 std::string hardware_version_; 280#pragma db transient 281 std::string running_duration_; 282#pragma db transient 283 bool nat_; 284#pragma db transient 285 std::string agent_address_; 286 287}; 288 289 290 291#pragma db object table("tblUser") callback(dbevent) 292class User : public Entity 293{ 294 friend class odb::access; 295public: 296 User(); 297 User( const User & other); 298 User & operator=( const User & other); 299 300 virtual EntityType GetType() const; 301public: 302 //get set 303 304 const std::string & GetPassword() const; 305 //std::string & GetPassword(); 306 307 bool GetAdministrator() const; 308 309 void SetPassword(const std::string & value); 310 void SetAdministrator( bool value); 311 312 bool GetOnline() const ; 313 void SetOnline(bool val) ; 314private: 315 // 登陆密码 316 std::string password_; 317 // 是否为管理员,true 管理员,false 操作员 318 bool administrator_; 319#pragma db transient 320 bool online_; 321 322}; 323#endif
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )