BerkeleyDB
多库联合操作
(Secondary Databases)
BerkeleyDB
多库联合操作
(Secondary Databases)
BerkeleyDB
中,二级库(
Secondary Databases
)存在的意义在于:当我们在某一个库中对
key
值进行查询时,往往得不到我们想要的所有信息;又或者,我们想得到更多关于该
key
值对应的信息,而又不想进行过多的操作。比如说,在
database1
中,我们存储了
key
为员工
ID
,
data
为员工籍贯、工资等信息,而
database2
中存储了
key
为员工名字,
data
为员工
ID
,我们如果想要将几张表结合起来操作,就要涉及到对二级库的操作了。
二级库的打开
/
关闭与一般的库没有什么区别,所谓“二级库”“一级库(
primary database
)”
,
只是在用
DB->associate()
函数将他们联系在一起的时候才具有他们字面上的意义。看下面一个代码片段:
DB *dbp, *sdbp;
db_create(&dbp, NULL, 0);
db_create(&sdbp, NULL, 0);
sdbp->set_flags(sdbp, DB_DUP); //
很多情况下,二级库需要支持
duplicate key
dbp->open(
……
); sdbp->open(
……
);
dbp->associate(dbp, //
一级库句柄
NULL, //TXN id
,这个还没整明白。。
sdbp, //
二级库句柄
callback_function, //
回调函数有,后面会详细说明
0); //
标志位
sdbp->close();
dbp->close();
注意:关闭库的时候,应该先关闭二级库,再关闭一级库。
dbp->associate()
的原型为:
DB->associate(DB *primary, DB_TXN *txnid, DB *secondary, int (*callback)(DB *secondary, const DBT *key, const DBT *data, DBT *result), u_int32_t flags);
当一个库被该函数声明为另一个库的二级库时
,
所以对一级库的操作将反映到二级库上
,
而对二级库记录的读操作返回的将是一级库相关记录的信息
.
注意
:
一级库的
key
值对于二级库来说应该是惟一的
,
所以不可以支持
duplicate key.
参数四为用来定义二级
key
值与一级
key
、
data
值关系的回调函数,其四个参数分别为二级库句柄、一级库的
key/data
值、被创造的二级库
key
值。当两级库都是用
DB_READONLY
打开时,该参数可以设为空。
参数五可以为
0
或者
DB_CREATE
、
DB_IMMUTABLE_KEY
之中的一个或两个
(
按位与“
|
”上
)
。
DB_CREATE
是当二级库为空时使用,但开销较大,建议不要使用;
DB_IMMUTABLE_KEY
指定返回的二级
key
为不可变,但注意当两级库以这个标志
associate
在一起的时候,由于二级库不变,改变与该二级
key
值相关的记录会造成两级的库不同步。
将一二级库联系在一起后,用
DB->get()
和
DB->pget()
对二级库进行查找,但返回的
key/value
值为一级库中的记录。下面为普通读取的例子:
sdbp->get(my_secondary_database, NULL, &key, &pdata, 0);
sdbp->pget(my_secondary_database, NULL, &key, &pkey, &pdata, 0);
注意:
key
值的类型应该与
dbp->associate()
中回调函数中对
key
值定义的类型一致。
在很多情况下,二级库一般都是
duplicate key
支持的,所以,如果想要对某
key
值对应的
duplicate
列表全部进行操作的话,应该用游标的方法:
while
(
cursor->c_get(cursorp, &key, &data, DB_SET) == 0) {
cursor->c_del(cursorp, 0);
}
删除所有相关库中所有与当前
key
值相关的记录。删除某二级库的记录会将一级库及其它所有关联起来的二级库的相关记录全部删除。注意:当你对二级库调用
DB->del
(),如果该库支持
duplicate key
则该
key
对应
duplicate
列表将被全部删除。
另外也可以:
sec_dbp->del(sec_dbp, NULL, &key, 0);
这样,如果该
key
值相关记录在某库中为
duplicate
形式的话,就只删除了
duplicate
列表的第一项。
下面我就
F2AS
系统中对于控制单元库(
ctrl_unit db
)与设备库
(device db)
的联合操作来具体说明:
ctrl_unit db
结构:
key
:
long license
data
:
struct unit_db{char unit_id; char unit_addr; char name[20]; char producer[20];char product_date[20]; char install_date[20]; char install_place[20]; int area_num; int place_num; char project_num[20]; unsigned long ctrl_id }
device db
结构:
key
:
unsigned long ctrl_id
data
:
struct device{ char unit_id; char unit_addr; char name[20]; char producer[20]; char product_date[20]; char install_date[20]; char install_place[20]; int area_num; int place_num; char project_num[20]}
我们先分析一下两个库的联系:
device db
中记录的
key
值与
ctrl_unit db
中记录的
data
结构中最后一个成员
ctrl_id
一致。这样,我们需要在对
device db
进行操作的同时对
ctrl_unit db
进行操作,所以我们将
device db
作为二级库,
ctrl_unit db
作为一级库。所有出错处理省略。
DB *ctrl_unit, *device;
DBT key, pvalue, pkey;
DBC *cursor;
/***************
创建库
***************/
db_create(&ctrl_unit, NULL, 0);
db_create(&device, NULL, 0);
/****
让
ctrl_unit
库支持
duplicate key*****/
device->set_flags(device, DB_DUPSORT);
/************
打开库
*************/
ctrl_unit->open(ctrl_unit, NULL,
“
ctrl_unit.db
”
, NULL, DB_BTREE, DB_CREATE, 0);
device->open(device, NULL,
“
device.db
”
, NULL,DB_BTREE,DB_CREATE,0);
device->cursor(device, NULL , &cursor, 0); //
/************
关联库
,
参数四回调函数
get_ctrl_id
在后面定义
*********/
ctrl_unit->associate(ctrl_unit, NULL, device, get_ctrl_id, 0); //ctrl_unit
作为一级库
/*************
通过对二级库
device
游标的操作来操作一级库
************/
memset(key, 0, sizeof(DBT));
memset(pkey, 0, sizeof(DBT));
memset(pvalue, 0, sizeof(DBT));
key.data = 10011; //
编号为
10011
的设备的记录
key.size = sizeof(unsigned long int);
unsigned long id;
struct device dvc;
pkey.data = &id;
pkey.size = sizeof(unsigned long);
pvalue.data = &dvc;
pvalue.size = sizeof(struct device);
cursor->c_get(cursor, &key, &pvalue, DB_SET);//
返回
ctrl_unit
的
value
cursor->c_pget(cursor, &key, &pkey, &pvalue, DB_SET);//
返回
ctrl_unit
的
key,value
对。
/*************
删除记录,这里假设
device
库支持
duplicate key**********/
while(cursor->c_pget(cursor, &key, &pkey, &pvalue, DB_NEXT_DUP) == 0)
{
cursor->c_del(cursor, 0); //
将所有关联起来的库的该记录全部删除。
}
该
while
语句其实等同于:
device->del(device, NULL, &key, 0);
/************
关闭游标,关闭库
(
先二级,再一级
)*****************/
cursor->close(&cursor);
device->close(&device);
ctrl_unit->close(&ctrl_unit);
/*****************
主函数完
**********************/
/*****************
主函数中
associate
所用回调函数
***************/
int get_ctrl_id (DB device, DBT pkey, DBT pvalue, DBT key)
{
struct unit_db *temp;
temp = pvalue->data;
memset(key, 0, sizeof(DBT));
key->data = &(temp->ctrl_id);
key->size = sizeof(unsigned long int);
return 0;
}
这样,就将一级库
ctrl_unit
记录的
value
的
ctrl_id
项与二级库记录的
key
值关联了起来,当对二级库进行检索时,会自动找到一级库中
value.data.ctrl_id
与指定
key.data
相同的值。比如:二级库的
key.data = 1023
,那么对二级库调用
DB->get
(),
DB->pget
()以及二级库游标操作的
DBC->get
(),
DBC-> pget
()将检索到一级库中相关记录。
注意:当要修改二级库的
key
值时,只需要直接修改一级库的关联项即可
,
所有相关二级库记录都将被修改。
在相关的表更多的情况下,我们还要涉及到“库集合”(
Database Joins
)的处理。对其的操作如下:
首先,打开所有相关一、二级库,并得到所有被关联的二级库的游标,再将他们指向各自库中你感兴趣的记录。
然后,将得到的游标存入一个游标数组,该数组最后一个参数为
NULL
。
接着用
DB->join()
将二级库与一级库组合,同时得到组合库的游标。函数原型如下:
int DB->join(DB *primary, DBC **curslist, DBC **dbcp, u_int32_t flags);
参数
1
为一级库句柄,参数
2
为游标数组,参数
3
为组合库的游标,参数
4
为标志(可以为
0
或者
DB_JOIN_NOSORT
)。
再来就可以用组合库的游标进行操作
,
比如:
DBC->c_get()
。
最后关闭所有库,先二级库再一级库。
看一个代码片段:
DBC *color_curs = NULL, *make_curs = NULL, *type_curs = NULL, *join_curs = NULL;
DBC *carray[4];
DBT key, data;
/****
打开库、打开游标、对
key
、
data
赋值代码省略
***/
color_curs->c_get(color_curs, &key, &data, DB_SET)
;
make_curs->c_get(make_curs, &key, &data, DB_SET)
;
type_curs->c_get(type_curs, &key, &data, DB_SET);
carray[0] = color_curs;
carray[1] = make_curs;
carray[2] = type_curs;
carray[3] = NULL;
dbp->join(dbp, carray, &join_curs, 0); //dbp
为一级库句柄。
join_curs->c_get(join_curs, &key, &data, 0);
/******
关闭库代码省略
******/
最后要提的是,几个库的记录的结构应该能够关联起来,对于上面的代码来说:
colorDB key:
颜色
data
:车牌号
makeDB key:
厂家
data
:车牌号
typeDB key:
品牌
data
:车牌号
carDB key:
车牌号
data
:汽车信息
大家注意二级库与一级库之间
key
\
value
的形式的关系