odb使用手册

Hello World实例

  1. 声明持久化类
    比如一个person.hxx类的头文件如下所示

    // person.hxx
    //
    #include <string>
    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:
        std::string first_;
        std::string last_;
        unsigned short age_;
    };
    

    如果需要存储在数据库中,则需要将person类的声明部分进行改造,需要修改的地方有(1)(2)(3)(4)(5),修改后的声明如下:

    // person.hxx
    //
    #include <odb/core.hxx> // (1)
    #include <string>
    
    #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 last_;
        unsigned short age_;
    };
    
  2. 产生odb的中间文件

    odb -d mysql --generate-query person.hxx  
    

    如果需要mysql语句,则执行如下指令:

    odb -d mysql --generate-query --generate-schema person.hxx
    
  3. 编译运行
    如果是首次运行,需要在mysql数据库中将mysql语句文件导入其中,以此来生成数据库相关的表

    mysql --user=odb_test --database=odb_test < person.sql
    
  4. 创建持久化对象

    源码如下:

    // driver.cxx
    //
    #include <memory> // std::auto_ptr
    #include <iostream>
    #include <odb/database.hxx>
    #include <odb/transaction.hxx>
    #include <odb/mysql/database.hxx>
    #include "person.hxx"
    #include "person-odb.hxx"
    using namespace std;
    using namespace odb::core;
    int
    main (int argc, char* argv[])
    {
        try
        {
            // mysql server's host name: root, password:123456, ip:198.1.17.252, port:30306
            auto_ptr<database> db(new odb::mysql::database("root", "123456", "odb_test", "198.1.17.252", 30306));
            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 ());
                // Make objects persistent and save their ids for later use.
                // 15 Revision 2.4, February 2015 C++ Object Persistence with ODB 2.4 Making Objects Persistent
                john_id = db->persist (john);
                jane_id = db->persist (jane);
                joe_id = db->persist (joe);
                t.commit ();
            }
        }
        catch (const odb::exception& e)
        {
            cerr << e.what () << endl;
            return 1;
        }
    }
    
  5. 查询数据库对象

    // driver.cxx
    //
    #include <memory> // std::auto_ptr
    #include <iostream>
    #include <odb/database.hxx>
    #include <odb/transaction.hxx>
    #include <odb/mysql/database.hxx>
    #include "person.hxx"
    #include "person-odb.hxx"
    using namespace std;
    using namespace odb::core;
    int
    main (int argc, char* argv[])
    {
        try
        {
            // mysql server's host name: root, password:123456, ip:198.1.17.252, port:30306
            auto_ptr<database> db(new odb::mysql::database("root", "123456", "odb_test", "198.1.17.252", 30306));
            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 ());
                // Make objects persistent and save their ids for later use.
                // 15 Revision 2.4, February 2015 C++ Object Persistence with ODB 2.4 Making Objects Persistent
                john_id = db->persist (john);
                jane_id = db->persist (jane);
                joe_id = db->persist (joe);
                t.commit ();
            }
            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 ();
            }
        }
        catch (const odb::exception& e)
        {
            cerr << e.what () << endl;
            return 1;
        }
    }
    
  6. 数据库更新

    // driver.cxx
    //
    #include <memory> // std::auto_ptr
    #include <iostream>
    #include <odb/database.hxx>
    #include <odb/transaction.hxx>
    #include <odb/mysql/database.hxx>
    #include "person.hxx"
    #include "person-odb.hxx"
    using namespace std;
    using namespace odb::core;
    int
    main (int argc, char* argv[])
    {
        try
        {
            // mysql server's host name: root, password:123456, ip:198.1.17.252, port:30306
            auto_ptr<database> db(new odb::mysql::database("root", "123456", "odb_test", "198.1.17.252", 30306));
            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 ());
                // Make objects persistent and save their ids for later use.
                // 15 Revision 2.4, February 2015 C++ Object Persistence with ODB 2.4 Making Objects Persistent
                john_id = db->persist (john);
                jane_id = db->persist (jane);
                joe_id = db->persist (joe);
                t.commit ();
            }
    
            // 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 ();
            }
            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 ();
            }
        }
        catch (const odb::exception& e)
        {
            cerr << e.what () << endl;
            return 1;
        }
    }
    
  7. 删除数据库

    // driver.cxx
    //
    #include <memory> // std::auto_ptr
    #include <iostream>
    #include <odb/database.hxx>
    #include <odb/transaction.hxx>
    #include <odb/mysql/database.hxx>
    #include "person.hxx"
    #include "person-odb.hxx"
    using namespace std;
    using namespace odb::core;
    int
    main (int argc, char* argv[])
    {
        try
        {
            // mysql server's host name: root, password:123456, ip:198.1.17.252, port:30306
            auto_ptr<database> db(new odb::mysql::database("root", "123456", "odb_test", "198.1.17.252", 30306));
            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 ());
                // Make objects persistent and save their ids for later use.
                // 15 Revision 2.4, February 2015 C++ Object Persistence with ODB 2.4 Making Objects Persistent
                john_id = db->persist (john);
                jane_id = db->persist (jane);
                joe_id = db->persist (joe);
                t.commit ();
            }
    
            // 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 ();
            }
            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 ();
            }
    
            // John Doe is no longer in our database.
            //
            {
                transaction t (db->begin ());
                db->erase<person> (john_id);
                t.commit ();
            }
            // John Doe is no longer in our database. An alternative
            // implementation without using the object id.
            //
            {
                transaction t (db->begin ());
                // Here we know that there can be only one John Doe in our
                // database so we use the query_one() shortcut again.
                //
                auto_ptr<person> john (
                db->query_one<person> (query::first == "John" &&
                query::last == "Doe"));
                if (john.get () != 0)
                db->erase (*john);
                t.commit ();
                }
        }
        catch (const odb::exception& e)
        {
            cerr << e.what () << endl;
            return 1;
        }
    }
    

持久化对象的相关处理

  1. Objects and Values: Objects属于具有全局唯一标识的实体entity的对象,相当于数据库中的某一条记录,具有id标识符。而Values被称为值对象,这些对象不会单独存在于数据库中,而是存在于实体entity对象的某一个成员变量中。

  2. 事务(Transaction): 事务是原子(Atomic),一致(consistent),隔离(isolated)和持久(durable)(ACID)的工作单元。事务的代码结构如下:

    #include <odb/transaction.hxx>
    transaction t (db.begin ())
    // Perform database operations.
    t.commit ();
    

    odb::transaction 类有如下接口:

    namespace odb
    {
        class transaction
        {
        public:
            typedef odb::database database_type;
            typedef odb::connection connection_type;
            explicit transaction (transaction_impl*, bool make_current = true);
            transaction ();
            void reset (transaction_impl*, bool make_current = true);
            void commit ();
            void rollback ();
            database_type& database ();
            connection_type& connection ();
            bool finilized () const;
        public:
            static bool has_current ();
            static transaction& current ();
            static void current (transaction&);
            static bool reset_current ();
            // Callback API.
            //
        public:
        ...
        };
    }
    

    解释说明:

    • commit():事务提交
    • rollback(): 事务回滚,当然如果没有显示支持需要commit或rollback的话,当transaction类析构时会自动回滚。
    • database(): 返回当前事务的database
    • connection(): 返回当前事务的connection
    • current(): 返回线程的active态的transaction,如果没有active transaction的话,则会抛出odb::not_in_transaction的异常,如果我们想check一下是否有一个有效的transaction在这个线程中,我们可以使用has_current()接口来实现。
    • transaction的构造函数输入参数make_current如果为false,则不会自动创建active transaction,之后在使用时通过current()接口人为创建。
    • reset_current(): 清除当前的active transaction

    以下是针对同一个线程多个transaction的实例代码

    transaction t1 (db1.begin ()); // Active transaction.
    transaction t2 (db2.begin (), false); // Not active.
    // Perform database operations on db1.
    transaction::current (t2); // Deactivate t1, activate t2.
    // Perform database operations on db2.
    transaction::current (t1); // Switch back to t1.
    // Perform some more database operations on db1.
    t1.commit ();
    transaction::current (t2); // Switch to t2.
    // Perform some more database operations on db2.
    t2.commit ();
    
    • reset(): 允许我们重新使用相同的transaction实例来实现多个数据库的事务操作。通常在我们commit()之后后需要发起事务的场景,示例代码如下:
    transaction t (db.begin ());
    for (size_t i (0); i < n; ++i)
    {
        // Perform a database operation, such as persist an object.
        // Commit the current transaction and start a new one after
        // every 100 operations.
        //
        if (i % 100 == 0)
        {
            t.commit ();
            t.reset (db.begin ());
        }
    }
    t.commit ();
    

    当然我们还需要注意事务会带来另一个潜在的风险,示例代码如下:

    void update_age (database& db, person& p)
    {
        transaction t (db.begin ());
        p.age (p.age () + 1);
        db.update (p);
        t.commit ();
    }
    

    这段代码如果事务提交失败时,就会回滚,相当于数据库什么都没改,但是内存数据p.age却已经被修改了,而编程人员还未发现此问题,这种问题应该尽量在编码过程中避免。我们可以使用下面的代码来解决:

    void update_age (database& db, unsigned long id)
    {
        transaction t (db.begin ());
        auto_ptr<person> p (db.load<person> (id));
        p.age (p.age () + 1);
        db.update (p);
        t.commit ();
    }
    

    当然,还有一种解决方案是当出现异常时,在catch的地方重新从数据库中load数据到内存,实例代码如下:

    void update_age (database& db, person& p)
    {
        try
        {
            transaction t (db.begin ());
            p.age (p.age () + 1);
            db.update (p);
            t.commit ();
        }
        catch (...)
        {
            transaction t (db.begin ());
            db.load (p.id (), p);
            t.commit ();
            throw;
        }
    }
    
  3. 持久化对象存库(persist)

    database::persist()函数的模板如下:

    template <typename T> typename object_traits<T>::id_type persist (const T& object);
    template <typename T> typename object_traits<T>::id_type persist (const object_traits<T>::const_pointer_type& object);
    template <typename T> typename object_traits<T>::id_type persist (T& object);
    template <typename T> typename object_traits<T>::id_type persist (const object_traits<T>::pointer_type& object);
    

    实例代码如下:

    person john ("John", "Doe", 33);
    shared_ptr<person> jane (new person ("Jane", "Doe", 32));
    transaction t (db.begin ());
    db.persist (john);
    unsigned long jane_id (db.persist (jane));
    t.commit ();
    cerr << "Jane’s id: " << jane_id << endl;
    
  4. 加载持久化对象(load)

    database::load()函数模板如下:

    template <typename T> typename object_traits<T>::pointer_type load (const typename object_traits<T>::id_type& id);
    template <typename T> void load (const typename object_traits<T>::id_type& id, T& object);
    

    如果load失败(比如你传的id数据库不存在)则会抛出odb::object_not_persistent异常。
    示例代码如下:

    transaction t (db.begin ());
    auto_ptr<person> jane (db.load<person> (jane_id));
    db.load (jane_id, *jane);
    t.commit ();
    

    当前如果我们已经load过持久化对象到内存中时,我们希望重新load一下,我们可以使用reload函数,reload函数不会与缓存中的数据进行交互,即直接从数据库中取数据,而不会从缓存中取。函数模板如下:

    template <typename T> void reload (T& object);
    template <typename T> void reload (const object_traits<T>::pointer_type& object);
    

    当我们不确定我们的id在数据库中是否存在时,我们可以通过find()函数来检查,函数模板如下:

    template <typename T> typename object_traits<T>::pointer_type find (const typename object_traits<T>::id_type& id);
    template <typename T> bool find (const typename object_traits<T>::id_type& id, T& object);
    

    第一个find()输入id,返回指针类型,如果id没有找到则返回null,第二个find()输入id,返回bool值,如果bool值为true,则object为数据库的对象实例,如果bool值为false,则表明没有找到对应id的数据

  5. 持久化对象的更新(update)

    先看下database::update()函数的模板

    template <typename T> void update (const T& object);
    template <typename T> void update (const object_traits<T>::const_pointer_type& object);
    template <typename T> void update (const object_traits<T>::pointer_type& object);
    

    下面通过一个银行账户转账的例子来讲述update()函数的使用,代码如下

    void transfer (database& db, unsigned long from_acc, unsigned long to_acc, unsigned int amount)
    {
        bank_account from, to;
        transaction t (db.begin ());
    
        db.load (from_acc, from);
    
        if (from.balance () < amount)
            throw insufficient_funds ();
        
        db.load (to_acc, to);
    
        to.balance (to.balance () + amount);
        from.balance (from.balance () - amount);
    
        db.update (to);
        db.update (from);
    
        t.commit ();
    }
    

    上面的例子是将from, to从数据库load到指定对象,我们也可以动态的分配一块内存,然后将内存的数据update到数据库中,实例如下:

    void transfer (database& db, unsigned long from_acc, unsigned long to_acc, unsigned int amount)
    {
        transaction t (db.begin ());
        shared_ptr<bank_account> from (db.load<bank_account> (from_acc));
    
        if (from->balance () < amount)
            throw insufficient_funds ();
    
        shared_ptr<bank_account> to (db.load<bank_account> (to_acc));
    
        to->balance (to->balance () + amount);
        from->balance (from->balance () - amount);
    
        db.update (to);
        db.update (from);
    
        t.commit ();
    }
    
  6. 删除持久化对象(delete)

    database::erase_query()函数的模板如下:

    template <typename T> void erase (const T& object);
    template <typename T> void erase (const object_traits<T>::const_pointer_type& object);
    template <typename T> void erase (const object_traits<T>::pointer_type& object);
    template <typename T> void erase (const typename object_traits<T>::id_type& id);
    

    使用举例:

    person& john = ...
    shared_ptr<jane> jane = ...
    unsigned long joe_id = ...
    transaction t (db.begin ());
    db.erase (john);
    db.erase (jane);
    db.erase<person> (joe_id);
    t.commit ();
    

    我们也可以通过erase_query()函数来删除多个匹配的数据库对象,当然使用它的前提是在odb的编译选项中得有–generate-query,erase_query()函数的模板如下:

    template <typename T> unsigned long long erase_query ();
    template <typename T> unsigned long long erase_query (const odb::query<T>&);
    

    第一个函数会删除所有类型为T的数据,第二个函数根据odb::query提供的查询表达式来匹配,样例程序如下:

    typedef odb::query<person> query;
    transaction t (db.begin ());
    db.erase_query<person> (query::last == "Doe" && query::age < 30);
    t.commit ();
    

    与query()函数不同,在调用delete_query()时,我们不能在查询表达式中使用指向对象的成员。但是,我们仍然可以将与指针对应的成员用作具有指向对象的id类型的普通对象成员。这使我们可以比较对象ID并测试指针是否为NULL.接下来我们会通过例子来详细说明

    typedef odb::query<employee> query;
    transaction t (db.begin ());
    employer& e = ... // Employer object to be deleted.
    db.erase_query<employee> (query::employer == e.id ());
    db.erase (e);
    t.commit ();
    

    假设employee对象的成员包含有指向employer对象e的指针,现在我们需要将要删除employer对象,为了避免出现employee中指向employer的指针找不到对象的情况,我们在删除employer对象之前,先删除那些把employer作为成员变量的对象,这里我们通过query来查询一下所有employee对象中,指定e的全部删除。之后再删除e本身

  7. 执行本地sql语句(execute)

    某些场景中,我们可能需要直接使用sql语句来操作数据库,odb提供了相关的api接口。database::execute()函数的重载版本如下:

    unsigned long long execute (const char* statement);
    unsigned long long execute (const std::string& statement);
    unsigned long long execute (const char* statement, std::size_t length)
    

    使用举例:

    transaction t (db.begin ());
    db.execute ("DROP TABLE test");
    db.execute ("CREATE TABLE test (n INT PRIMARY KEY)");
    t.commit ();
    
发布了7 篇原创文章 · 获赞 5 · 访问量 826

猜你喜欢

转载自blog.csdn.net/dean_zhang5757/article/details/104791165