1. 程式人生 > >C++ ORM ODB入門

C++ ORM ODB入門

tab 標識 urn red cxx mys create -s bat

1.ORM



ORM, Object Relational Mapping, 對象關系映射,用來將基於對象的數據結構映射到SQL的數據結構中。即將基於對象的數據映射到關系表中的字段,然後我們可以通過對象提供的接口來操作數據庫,而無需寫sql語句來操作數據庫。
一般一張關系表對應兩個類,一個實體類和一個操作類。
ORM是一種框架,而不是一種實現。


2.C++ 的ORM的實現ODB

1)一個簡單的例子
#include <odb/core.hxx> //(1)


#pragma db object//(2)


class person
{
public:
person (const std::string& first,

const std::string& last,
unsigned short age);
const std::string& first () const;
const std::string& last () const;
unsigned short age () const;
void age (unsigned short);
private:
person(); //(3)

friend class odb::access;//(4)

#pragma db id auto//(5)
unsigned long id_;//(5)
std::string first_;
std::string email_;
std::string last_;
unsigned short age_;
};


Note:
(1) headers to provide class like ‘access‘


(2) 告訴編譯器這是一個persistent class,所以我們要將#pragma db object放在類的定義前。將一個類聲明為persistent class並不意味著這個類的所有對象都會被存儲到數據庫中。


(3)這個構造函數在實例化一個persistent class時會用到。


(4)讓默認的構造函數和其它類成員能夠訪問access類。


(5)每個persistent object通常都要有一個獨一無二的id.如果persistent object沒有定義id,那麽數據庫的操作就會受限。
#pragma db id auto 用來指定這個對象id,auto表明這個id的值是有數據庫自動分配的。
這個id可以使這個類本身就具有的成員,也可以是自己添加僅作為id來標識不同的persistent對象。上面的例子中的id_就是僅僅用來區別persistent object.


上面的例子我們也可以寫成:
#include <odb/core.hxx> //(1)


#pragma db object//(2)
class person
{
public:
person();
person (const std::string& first,
const std::string& last,
unsigned short age);
const std::string& first () const;
const std::string& last () const;
unsigned short age () const;
void age (unsigned short);
private:
#pragma db id auto//(5)
unsigned long id_;//(5)
std::string first_;
std::string email_;
std::string last_;
unsigned short age_;
};
//用來指定persistent 的id
#pragma db object(person)
#pragma db member(person::email_) id


3.用ODB編譯器生成代碼

將上面的代碼保存為person.hxx,然後使用如下的命令來編譯:
odb -d mysql --generate-query person.hxx
-d 用來指定我們使用的數據庫。odb支持很多不同的數據庫,我們以mysql為例。


這個命令會讓odb的編譯器生成三個文件:person-odb.hxx, person-odb.ixx, person-odb.cxx.


--generate-query用來指定編譯器生成數據庫支持的代碼。如果我們使用--generate-shema, 那麽還將生成person-odb.sql文件。


ok, 現在我們已經有persistent class(person.cxx)和database support code(person-odb.hxx, person-odb.ixx, person-odb.cxx)了。


4.編譯和運行

假如我們的main文件為diver.cxx, 我們應該先編譯main文件和上面生成的cxx文件:
c++ -c driver.cxx
c++ -c person-odb.cxx -Iyou_dir_to_odb_headers
c++ -o driver driver.o person-odb.o -Lyou_dir_to_odb_lib


在運行數據庫前,記得先啟動mysql並在庫中使用指定表格。
mysql --user=user_name --database=you_db\


5.一個更加實用的例子

// driver.cxx
//
#include <memory> // std::auto_ptr
#include <iostream>
#include <odb/database.hxx> //定義database類
#include <odb/transaction.hxx> //定義transaction類
#include <odb/mysql/database.hxx> //實現database類的接口,同樣要include
#include "person.hxx"
#include "person-odb.hxx"
using namespace std;
using namespace odb::core;//relative namespace.下面直接使用odb::batabase和odb::transaction就可以了,不用寫成odb::core::database和odb::core::transaction了。


int
main (int argc, char* argv[])
{
try
{
auto_ptr<database> db (new odb::mysql::database (argc, argv));//(1)
unsigned long john_id, jane_id, joe_id;
// Create a few persistent person objects.
//
{
person john ("John", "Doe", 33);
person jane ("Jane", "Doe", 32);
person joe ("Joe", "Dirt", 30);
transaction t (db->begin ());//(1)
// Make objects persistent and save their ids for later use.
//
john_id = db->persist (john);//(2)
jane_id = db->persist (jane);//(2)
joe_id = db->persist (joe);//(2)
t.commit ();//(3)
}
}
catch (const odb::exception& e)
{
cerr << e.what () << endl;
return 1;
}
}


Note:
(1) 首先,我們要先創建一個database對象和transaction對象。
odb的實現中,所有database的操作都必須在一個transaction內進行。transaction是我們操作數據庫的工作量的一個原子單位。一個transaction內包含有很過個database操作。如果一次transaction成功,則這個transaction內的數據庫操作都會成功。如果一次transaction失敗,這個transaction內的所有操作都會失敗。


(2)我們在new了很多person對象後,使用persist選擇獎哪些對象存儲到數據庫。


(3)提交一次transaction。如果在commit之前程序崩潰,所有的將要進行保存的數據庫都不會被存儲。


編譯程序,然後開啟數據庫,然後執行程序。查看SQL的記錄,我們可看到如下記錄:
INSERT INTO ‘person‘ (‘id‘,‘first‘,‘last‘,‘age‘) VALUES (?,?,?,?)
INSERT INTO ‘person‘ (‘id‘,‘first‘,‘last‘,‘age‘) VALUES (?,?,?,?)
INSERT INTO ‘person‘ (‘id‘,‘first‘,‘last‘,‘age‘) VALUES (?,?,?,?)


6.查詢數據庫

typedef odb::query<person> query;
typedef odb::result<person> result;
// Say hello to those over 30.
//
{
transaction t (db->begin ());
result r (db->query<person> (query::age > 30));
for (result::iterator i (r.begin ()); i != r.end (); ++i)
{
cout << "Hello, " << i->first () << "!" << endl;
}
t.commit ();
}
}

7.更新數據庫

(1)//使用object id
unsigned long john_id, jane_id, joe_id;
// Create a few persistent person objects.
//
{
...
// Save object ids for later use.
//
john_id = john.id ();
jane_id = jane.id ();
joe_id = joe.id ();
}
// Joe Dirt just had a birthday, so update his age.
//
{
transaction t (db->begin ());
auto_ptr<person> joe (db->load<person> (joe_id));
joe->age (joe->age () + 1);
db->update (*joe);
t.commit ();
}


(2)不使用object id
// Joe Dirt just had a birthday, so update his age. An
// alternative implementation without using the object id.
//
{
transaction t (db->begin ());
result r (db->query<person> (query::first == "Joe" &&
query::last == "Dirt"));
result::iterator i (r.begin ());
if (i != r.end ())
{
auto_ptr<person> joe (i.load ());
joe->age (joe->age () + 1);
db->update (*joe);
}
t.commit ();
}

8.刪除persistent object

刪除一個object要使用到object 的id
// John Doe is no longer in our database.
//
{
transaction t (db->begin ());
db->erase<person> (john_id);
t.commit ();
}


當然,也可以不用object id
// John Doe is no longer in our database. An alternative
// implementation without using the object id.
//
{
transaction t (db->begin ());
result r (db->query<person> (query::first == "John" &&
query::last == "Doe"));
result::iterator i (r.begin ());
if (i != r.end ())
{
auto_ptr<person> john (i.load ());
db->erase (*john);
}
t.commit ();
}

C++ ORM ODB入門