Berkeley DB——Records
本文主要讲述如何操作Berkeley DB的记录——Records的Create、Retrieve、Update和Delete。
Berkeley DB中保存的是key/data对,它们都是Dbt对象:
#include <db_cxx.h>
class Dbt { public: Dbt(void *data, size_t size); Dbt(); Dbt(const Dbt &); Dbt &operator = (const Dbt &); ~Dbt();
void *get_data() const; void set_data(void *);
u_int32_t get_size() const; void set_size(u_int32_t);
u_int32_t get_ulen() const; void set_ulen(u_int32_t);
u_int32_t get_dlen() const; void set_dlen(u_int32_t);
u_int32_t get_doff() const; void set_doff(u_int32_t);
u_int32_t get_flags() const; void set_flags(u_int32_t);
DBT *Dbt::get_DBT(); const DBT *Dbt::get_const_DBT() const; static Dbt *Dbt::get_Dbt(DBT *dbt); static const Dbt *Dbt::get_const_Dbt(const DBT *dbt); };
|
它是DBT对象的c++封装,而DBT是一个结构体。如果我们设置DB的访问方法为B树或者Hash的话,Key可以为任何值。而如果我们定义一个有n-1(相对于有n个字段的关系数据库表)个字段的struct,将类型为这个struct的对象存入数据库的话,就相当于我们存入了n个字段的值了,只是我们不能以data中的字段作为查询条件,而只能以key为查询条件。
通常情况下,我们需要设置Dbt对象的data和len,以指明需要保存的数据和数据所占用空间的大小——不是指针的大小而是实际数据所占空间的大小。当写数据到DB中的时候,我们需要设置key/data对的size和data。存入的数据最好是那些c/c++的基本数据类型——比如我们想存入字符串,那应该使用char*或者wchar_t*类型,而不是string或者wstring。根据我的实践,如果存入string和wstring会发生一些奇怪的问题,具体看这里。
如果数据库不允许重复记录的话(默认情况,如果想存入重复记录需要对Db设置相应的flag),put方法就相当于关系数据库中的insert or update:
int Db::put(DbTxn *txnid, Dbt *key, Dbt *data, u_int32_t flags);
我们可以给put方法传入不同的flag来控制put的具体行为,这些flag有:
Flag(Db.put) |
Description |
0 |
添加数据到数据库中。如果DB不允许重复记录,当已经有相同的key的记录时,将修改原记录,否则存入新记录;如果DB允许重复,则添加新记录。 |
DB_APPEND |
只针对Queue和Recno的DB,将记录添加到数据库的末尾 |
DB_NODUPDATA |
只针对B树和Hash的DB,且数据库需要设置为允许重复记录,如果不存在相同key的记录,则存入新记录,否则返回DB_KEYEXIST |
DB_NOOVERWRITE |
当不存在key相同的记录时,存入新的记录,而不管DB是否允许有重复记录;否则返回DB_KEYEXIST |
代码示例:
#include <db_cxx.h> #include <string.h>
...
char *description = "Grocery bill."; float money = 122.45;
Db my_database(NULL, 0); // Database open omitted for clarity
Dbt key(&money, sizeof(float)); Dbt data(description, strlen(description) + 1);
int ret = my_database.put(NULL, &key, &data, DB_NOOVERWRITE); if (ret == DB_KEYEXIST) { my_database.err(ret, "Put failed because key %f already exists", money); } |
可以使用get方法从DB中根据key来获取数据:
int Db::get(DbTxn *txnid, Dbt *key, Dbt *data, u_int32_t flags);
必须先设定key的值和size,把data当作out参数传入,如果搜索到记录,则数据会写入data参数中,并且get方法返回0,否则返回非零值,这些返回值的含义如下:
Result |
Description |
0 |
成功 |
-30989 |
没有找到此key的记录 |
get方法也有一个flag参数,常用的flag有:
Flag |
Description |
0 |
取出key匹配的一条记录,填充data参数 |
DB_GET_BOTH |
当key和data都匹配时,填充data参数 |
DB_SET_RECNO |
根据行号来搜索记录,key和data参数都将被填充(仅针对B+树的DB)。 |
DB_MULTIPLE |
当key匹配时,返回一批数据到data参数所指的buffer中(针对设置了允许重复记录的DB) |
一个例子:
#include <db_cxx.h> #include <string.h> ...
float money; char *description;
Db my_database(NULL, 0); // Database open omitted for clarity
money = 122.45;
Dbt key, data; // Use our own memory to retrieve the float. // For data alignment purposes. key.set_data(&money); key.set_ulen(sizeof(float)); key.set_flags(DB_DBT_USERMEM);
my_database.get(NULL, &key, &data, 0);
// Money is set into the memory that we supplied. description = (char *)data.get_data(); |
删除记录使用del方法:
int Db::del(DbTxn *txnid, Dbt *key, u_int32_t flags);
如果DB设置为允许重复记录的话,那key匹配的所有记录都被删除。想一条条记录删除的话,需要使用游标。
一个例子:
#include <db_cxx.h>
...
Db my_database(NULL, 0); // Database open omitted for clarity
float money = 122.45; Dbt key(&money, sizeof(float));
my_database.del(NULL, &key, 0);
|
del方法并不是真正的物理删除,db文件的大小也不会变小——这有点象Access。
#include "stdafx.h" #include <iostream> #include <db_cxx.h> #include <string.h>
using namespace std; int _tmain(int argc, _TCHAR* argv[]) { Db db(NULL,0);
u_int32_t oFlags = DB_CREATE; // Open flags; try { db.open(NULL, // Transaction pointer "my_db.db", // Database file name NULL, // Optional logical database name DB_BTREE, // Database access method oFlags, // Open flags 0); // File mode (using defaults) db.truncate(NULL,0,0); float money = 122.45; char *description = "Grocery bill.";
Dbt key(&money, sizeof(float)); Dbt data(description, strlen(description)+1); int ret = db.put(NULL, &key, &data, DB_NOOVERWRITE); cout<<"put data--"<<description<<endl;
ret = db.get(NULL, &key, &data, DB_GET_BOTH); cout<<"get key--"<<*((float*)key.get_data())<<endl; cout<<"get data--"<<(char *)data.get_data()<<endl;
money = 111; description = "James--------------------";
data.set_data(description); data.set_size(strlen(description)+1); db.put(NULL,&key,&data,DB_NOOVERWRITE); ret = db.get(NULL, &key, &data, DB_GET_BOTH); cout<<"get key--"<<*((float*)key.get_data())<<endl; cout<<"get data--"<<(char *)data.get_data()<<endl;
money = 191; description = "Mike";
data.set_data(description); data.set_size(strlen(description)+1); db.put(NULL,&key,&data,DB_NOOVERWRITE); ret = db.get(NULL, &key, &data, DB_GET_BOTH); cout<<"get key--"<<*((float*)key.get_data())<<endl; cout<<"get data--"<<(char *)data.get_data()<<endl;
} catch(DbException &e) { cerr<<"DBException:"<<e.what(); } catch(std::exception &e) { cerr<<"DBException:"<<e.what(); } system("pause"); return 0; } |
所有Berkeley DB相关的随笔