odb 使用指南(三)持久化对象的处理
背景
最近一直在看odb官网上odb-manual,由于是全英文文档,仔细看过一遍之后虽然感觉基本了解,但是隔几天之后再翻开时又要逐一回忆当初理解的一些细节,毕竟不是母语,没有那种一看就条件反射式的想起来,所以把一些key word记录下来,避免下次重复花时间来消化知识。对于一个coder,能够用代码来阐述的,就尽量不用文字,所以在记录过程中尽量通过代码来展示一些api的用法。
使用说明
本文基于odb官方手册为指导,通过自己的理解之后用尽可能通俗易懂的文字来阐述相关用法。站在应用编程的角度,将文档中常用知识点、易错点进行翻译整理。同时本文也弱化掉了一些不常用的知识点,基于这种考虑主要是由于本文的立足点是为了使编程人员尽快上手开发,如果将一些不常用的知识点也罗列其中会显得繁杂且冗余,所以如果在开发过程中遇到本文未提及的点可以回到odb官方手册进行查询。
持久化对象的处理
Objects and Values: Objects属于具有全局唯一标识的实体entity的对象,相当于数据库中的某一条记录,具有id标识符。而Values被称为值对象,这些对象不会单独存在于数据库中,而是存在于实体entity对象的某一个成员变量中。
事务(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;} }
持久化对象存库(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;
加载持久化对象(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的数据
持久化对象的更新(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 (); }
删除持久化对象(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本身
执行本地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 ();
上一篇 odb 使用指南(二)Hello World
下一篇 odb 使用指南(四)数据库查询
odb 使用指南(三)持久化对象的处理相关推荐
- hibernate中的PO持久化对象及PO三种状态
一,认识持久化对象PO: 在hibernate的应用程序中,每一个数据库中的表都对应一个持久化对象PO.PO可以看成是与数据库表相映射的java对象.最简单的PO对应数据库中某个表中的一条记录,多个记 ...
- Hibernate持久化对象三种状态
持久化类类三种状态介绍 瞬时态:也叫做临时态或自由态,它一般指我们new出来的对象,它不存在OID,与hibernate session无关联,在数据库中也无记录.它使用完成后,会被jvm直接回收掉, ...
- RVC使用指南(三)-对象管理
RVC使用指南(三)-对象管理 https://mp.weixin.qq.com/s/B3wcxUP-QJRnb_kVIlbZqQ 看了就要关注我,哈哈~ 本文介绍了vSAN中与对象管理相关的RVC命 ...
- Hibernate持久化对象的三种状态深入理解
关于OID hibernate缓存是一个map,他会根据OID作为缓存对象的key,我们的映射文件中<id>标签指定的属性值会作为OID 持久化对象的三种状态 为了方便理解,Hiberna ...
- odb 使用指南(四)数据库查询
背景 最近一直在看odb官网上odb-manual,由于是全英文文档,仔细看过一遍之后虽然感觉基本了解,但是隔几天之后再翻开时又要逐一回忆当初理解的一些细节,毕竟不是母语,没有那种一看就条件反射式的想 ...
- (9) hibernate加载持久化对象的两种方式——get、load
一.get与load对比 在hibernate中get和load方法是根据id取得持久化对象的两种方法,但在实际使用的过程中总会把两者混淆,不知道什么情况下使用get好,什么时候使用load方法效率更 ...
- C++11 并发指南三(Lock 详解)
在 <C++11 并发指南三(std::mutex 详解)>一文中我们主要介绍了 C++11 标准中的互斥量(Mutex),并简单介绍了一下两种锁类型.本节将详细介绍一下 C++11 标准 ...
- C++11 并发指南三(Lock 详解)(转载)
multithreading 多线程 C++11 C++11多线程基本使用 C++11 并发指南三(Lock 详解) 在 <C++11 并发指南三(std::mutex 详解)>一文中我们 ...
- Hibernate持久化对象状态
在Hibernate中,持久化对象再被操作过程中分为三个时期.这三个时期和session周期相关. 各自是瞬时(Transient),持久太(persistent)和游离态(Detached) 瞬时状 ...
最新文章
- PHP MySQL Update
- java多线程操作同一资源
- Vue+Openlayers加载Geoserver发布的TileWMS后更换shp数据源的流程
- 《Nature》上给青年科研工作者的几条忠告 (转载)
- Vue+blockly 制作与自定义美化
- 大话ConcurrentHashMap的put,get过程
- python工程师面试宝典_2019年,Python工程师必考的6个面试题,Python面试题No5
- django-模型类字段类型
- LINUX SHELL删除文件中的回车(WINDOWS转LINUX)
- 电力系统微型计算机继电保护2018,2018年4月高等教育自学考试电力系统微型计算机继电保护试题及答案.docx...
- 在线识别图片中的字体的网站
- WordPress文章/页面浏览量计数器插件Post Views Counter
- nosqlbooster 延长试用日期
- 4.0寸86盒显示屏调试(一)
- android版本兼容API24,Android 7.0 (API 24) 适配
- 金蝶云苍穹集成,苍穹到eas审核反审核
- 使用Foxmail定制自己的邮件模板
- PHP——人人都会编程
- Java---登录页面及其接口的实现
- 【每日新闻】诺基亚展示未来工厂:5G自动化机器人与人类和谐共处