1. 打开数据库
首先必须调用db_create()函数初始化DB句柄,然后就可以使用open()方法打开数据库了。默认情况下,如果数据库不存在,DB不会创建。为了覆盖缺省行为,可以在open()调用中指定DB_CREATE标记。
以下代码示范了如何打开数据库:
#include
...
DB *dbp; /* DB structure handle */
u_int32_t flags; /* database open flags */
int ret; /* function return value */
/* Initialize the structure. This
* database is not opened in an environment,
* so the environment pointer is NULL. */
ret = db_create(&dbp, NULL, 0);
if (ret != 0) {
/* Error handling goes here */
}
/* Database open flags */
flags = DB_CREATE; /* If the database does not exist,
* create it.*/
/* open the database */
ret = dbp->open(dbp, /* DB structure pointer */
NULL, /* Transaction pointer */
"my_db.db", /* On-disk file that holds the database. */
NULL, /* Optional logical database name */
DB_BTREE, /* Database access method */
flags, /* Open flags */
0); /* File mode (using defaults) */
if (ret != 0) {
/* Error handling goes here */
}
在open参数中,DB_BTREE是DB的一种访问方法,此外还有DB_HASH、DB_HASH、DB_RECNO、DB_QUEUE等访问方法。
DB存取数据的基本方式是key/value对,DB_BTREE和DB_HASH的key可为任意值,而DB_RECNO和DB_QUEUE的 key只能为逻辑上的数字。DB_BTREE用在记录数据较小的情况,通常能直接保存在内存里,而DB_HASH用在记录数据较大的情况。这里的记录数据指单条记录。
DB_QUEUE和DB_RECNO用数字来标记每条记录(记录ID),前者逻辑数字不会随着记录的删除发生变化,后者会发生变化。
打开标记出了DB_CREATE外,还可有如下标记的或组合。
- DB_EXCL
独占式创建,如果数据库已经存在则打开失败,仅和DB_CREATE组合使用。
- DB_RDONLY
只读方式打开。
- DB_TRUNCATE
截断磁盘上的数据库文件,清空数据。
2. 关闭数据库
在关闭数据库之前必须确保所有的游标(cursors)已经关闭,关闭数据库时自动把缓存数据写入磁盘。如果需要手工写缓存数据,可调用DB->sync()方法。
以下是示范代码:
#include
...
DB *dbp; /* DB struct handle */
...
/*
* Database open and access operations
* happen here.
*/
...
/* When we're done with the database, close it. */
if (dbp != NULL)
dbp->close(dbp, 0);
3. DB管理方法
- DB->get_open_flags() 获取数据库的打开标记
前提是数据库必须已经打开。
#include
...
DB *dbp;
u_int32_t open_flags;
/* Database open and subsequent operations omitted for clarity */
dbp->get_open_flags(dbp, &open_flags);
- DB->remove() 删除数据库
如果database参数为NULL,则删除该方法所引用的整个文件。注意,不要删除已经有打开句柄的数据库。
#include
...
DB *dbp;
/* Database open and subsequent operations omitted for clarity */
dbp->remove(dbp, /* Database pointer */
"mydb.db", /* Database file to remove */
NULL, /* Database to remove. This is
* NULL so the entire file is
* removed. */
0); /* Flags. None used. */
- DB->rename() 数据库重命名
如果database参数是NULL,则该方法所引用的整个文件被重命名。注意,不要重命名已经有打开句柄的数据库。
#include
...
DB *dbp;
/* Database open and subsequent operations omitted for clarity */
dbp->rename(dbp, /* Database pointer */
"mydb.db", /* Database file to rename */
NULL, /* Database to rename. This is
* NULL so the entire file is
* renamed. */
"newdb.db", /* New database file name */
0); /* Flags. None used. */
4. 错误处理函数
- set_errcall()
当DB发生错误的时候,将调用set_errcall()所指定的回调函数,错误前缀和错误消息发送给该回调函数,错误显示由回调函数处理。
- set_errfile()
设置C库的FILE *来显式错误信息。
- set_errpfx()
设置DB的错误消息前缀
- err()
产生一条错误消息。错误消息将被发送给set_errcall所指定的回调函数,如果没有设置回调函数,则发送给set_errfile()所指定的文件,如果还是没有设置,则发送给标准错误输出(stderr)。
错误消息包含前缀串(由set_errprefix()指定),可选的printf风格的格式化串,错误消息,尾部换行。
- errx()
同err(),除了与错误值关联的DB消息不会追加到错误串。
此外,还可以用db_strerror()直接返回与特定的错误号对应的错误串。
例如,为了把错误消息发送给回调函数,可先定义该回调函数。
/*
* Function called to handle any database error messages
* issued by DB.
*/
void
my_error_handler(const char *error_prefix, char *msg)
{
/*
* Put your code to handle the error prefix and error
* message here. Note that one or both of these parameters
* may be NULL depending on how the error message is issued
* and how the DB handle is configured.
*/
}
然后注册它。
#include
#include
...
DB *dbp;
int ret;
/*
* Create a database and initialize it for error
* reporting.
*/
ret = db_create(&dbp, NULL, 0);
if (ret != 0) {
fprintf(stderr, "%s: %s/n", "my_program",
db_strerror(ret));
return(ret);
}
/* Set up error handling for this database */
dbp->set_errcall(dbp, my_error_handler);
dbp->set_errpfx(dbp, "my_example_program");
产生一条错误消息:
ret = dbp->open(dbp,
NULL,
"mydb.db",
NULL,
DB_BTREE,
DB_CREATE,
0);
if (ret != 0) {
dbp->err(dbp, ret,
"Database open failed: %s", "mydb.db");
return(ret);
}
5. DB环境
DB环境是对1个或者多个数据库的封装,一般用法是先打开DB环境,然后在此环境中打开数据库。这样创建或者打开的数据库是相对于环境HOME目录的。DB环境对于一般简单的嵌入式数据库用不上,但是对于多数据库文件、多线程多进程支持、事务处理、高可用性(复制)支持、日志子系统等是必要的。
以下代码示范了如何创建/打开DB环境。
#include
...
DB_ENV *myEnv; /* Env structure handle */
DB *dbp; /* DB structure handle */
u_int32_t db_flags; /* database open flags */
u_int32_t env_flags; /* env open flags */
int ret; /* function return value */
/*
Create an environment object and initialize it for error
reporting.
*/
ret = db_env_create(&myEnv, 0);
if (ret != 0) {
fprintf(stderr, "Error creating env handle: %s/n", db_strerror(ret));
return -1;
}
/* Open the environment. */
env_flags = DB_CREATE; /* If the environment does not exist,
* create it. */
ret = myEnv->open(myEnv, /* DB_ENV ptr */
"/export1/testEnv", /* env home directory */
env_flags, /* Open flags */
0); /* File mode (default) */
if (ret != 0) {
fprintf(stderr, "Environment open failed: %s", db_strerror(ret));
return -1;
}
DB环境打开后,就可以在其中创建/打开数据库了。
/*
* Initialize the DB structure. Pass the pointer
* to the environment in which this DB is opened.
*/
ret = db_create(&dbp, myEnv, 0);
if (ret != 0) {
/* Error handling goes here */
}
/* Database open flags */
db_flags = DB_CREATE; /* If the database does not exist,
* create it.*/
/* open the database */
ret = dbp->open(dbp, /* DB structure pointer */
NULL, /* Transaction pointer */
"my_db.db", /* On-disk file that holds the database. */
NULL, /* Optional logical database name */
DB_BTREE, /* Database access method */
db_flags, /* Open flags */
0); /* File mode (using defaults) */
if (ret != 0) {
/* Error handling goes here */
}
在关闭DB环境之前,必须先关闭打开的数据库。
/*
* Close the database and environment
*/
if (dbp != NULL) {
dbp->close(dbp, 0);
}
if (myEnv != NULL) {
myEnv->close(myEnv, 0);
}
DB记录包含两部分 - 关键字部分(key)和数据部分(data),二者在DB中分别封装为DBT结构。DBT结构有一个void*字段用来存放数据,还有一个字段用来指定数据的长度,因此key和data可以为内存连续的任意长度的数据。
以下代码示范了如何给key和data赋值。
#include
#include
...
DBT key, data;
float money = 122.45;
char *description = "Grocery bill.";
/* Zero out the DBTs before using them. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data = &money;
key.size = sizeof(float);
data.data = description;
data.size = strlen(description) + 1;
要取回key和data的值,只要把DBT中void *部分返回给相应的变量就可以了。但是上例有点例外,因为浮点数在某些系统中要求内存对齐,为安全起见我们自己提供浮点数的内存,而不使用DB返回的内存。通过使用DB_DBT_USEMEM标记就可以实现。
#include
#include
...
float money;
DBT key, data;
char *description;
/* Initialize the DBTs */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data = &money;
key.ulen = sizeof(float);
key.flags = DB_DBT_USERMEM;
/* Database retrieval code goes here */
/*
* Money is set into the memory that we supplied.
*/
description = data.data;
默认情况下,DB不支持重复记录(同一个key对应到多个data)。DBT->put()和DBT->get()适用于这种情况下的记录读写操作。对于重复记录,可使用游标(Cursor)。
写记录通过DB->put()方法实现,它带有一些标记,其中DB_NOOVERWRITE标记指定不能插入重复记录,如果记录有重复,就算数据库支持重复记录,也会返回DB_KEYEXIST错误。
#include
#include
...
char *description = "Grocery bill.";
DBT key, data;
DB *my_database;
int ret;
float money;
/* Database open omitted for clarity */
money = 122.45;
/* Zero out the DBTs before using them. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data = &money;
key.size = sizeof(float);
data.data = description;
data.size = strlen(description) +1;
ret = my_database->put(my_database, NULL, &key, &data, DB_NOOVERWRITE);
if (ret == DB_KEYEXIST) {
my_database->err(my_database, ret,
"Put failed because key %f already exists", money);
}
读记录通过DB->get()方法实现,如果数据库支持重复记录,它只返回第一个与key匹配的记录。当然也可以用一批get调用来获取重复的记录,需要在调用DB->get()时指定DB_MULTIPLE标记。
默认情况DB->get()返回与提供的key匹配的记录,如果需要记录既和key匹配,也和data匹配,可指定DB_GET_BOTH标记。如果没有匹配的记录,本方法返回DB_NOTFOUND。
#include
#include
...
DBT key, data;
DB *my_database;
float money;
char *description;
/* Database open omitted for clarity */
money = 122.45;
/* Zero out the DBTs before using them. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
/*
* Use our own memory to retrieve the float.
* For data alignment purposes.
*/
key.data = &money;
key.ulen = sizeof(float);
key.flags = DB_DBT_USERMEM;
my_database->get(my_database, NULL, &key, &data, 0);
/*
* Money is set into the memory that we supplied.
*/
description = data.data;
删除记录可调用DB->del()方法,如果记录有重复,则删除所有的与key匹配的记录。要删除单条记录,可考虑用游标。要删除数据库的所有记录,可调用DB->truncate()方法。
#include
#include
...
DBT key;
DB *my_database;
float money = 122.45;
/* Database open omitted for clarity */
/* Zero out the DBTs before using them. */
memset(&key, 0, sizeof(DBT));
key.data = &money;
key.size = sizeof(float);
my_database->del(my_database, NULL, &key, 0);
默认情况下数据的修改被缓存起来,另一个打开数据库的进程不会立即看到修改后的数据,在数据库关闭时数据自动保存到磁盘。可以调用Db->sync()强制保存缓存的数据,也可采用事务数据库防止缓存数据的丢失。
如果不希望数据库关闭时保存缓存数据,可在DB->close()中指定DB_NOSYNC标记。
如果系统崩溃而且没有使用事务数据库,可调用DB->verify()验证数据,如果验证失败,可用db_dump命令(使用-R或-r参数控制db_dump的恢复程度)尽可能恢复丢失的数据。
8. DB游标
游标使用DBC结构管理,打开游标可调用DB->cursor()方法。
#include
...
DB *my_database;
DBC *cursorp;
/* Database open omitted for clarity */
/* Get a cursor */
my_database->cursor(my_database, NULL, &cursorp, 0);
关闭有掉调用DBC->c_close()方法,注意在关闭数据库之前必须关闭所有的游标,否则后果不可预料。
#include
...
DB *my_database;
DBC *cursorp;
/* Database and cursor open omitted for clarity */
if (cursorp != NULL)
cursorp->c_close(cursorp);
if (my_database != NULL)
my_database->close(my_database, 0);
为了在数据库中遍历记录,可调用DBC->c_get()方法,指定DB_NEXT标记。
#include
#include
...
DB *my_database;
DBC *cursorp;
DBT key, data;
int ret;
/* Database open omitted for clarity */
/* Get a cursor */
my_database->cursor(my_database, NULL, &cursorp, 0);
/* Initialize our DBTs. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
/* Iterate over the database, retrieving each record in turn. */
while ((ret = cursorp->c_get(cursorp, &key, &data, DB_NEXT)) == 0) {
/* Do interesting things with the DBTs here. */
}
if (ret != DB_NOTFOUND) {
/* Error handling goes here */
}
/* Cursors must be closed */
if (cursorp != NULL)
cursorp->c_close(cursorp);
if (my_database != NULL)
my_database->close(my_database, 0);
要从数据库的尾部到头部遍历记录,可使用DB_PREV代替DB_NEXT标记。
#include
#include
...
DB *my_database;
DBC *cursorp;
DBT key, data;
int ret;
/* Database open omitted for clarity */
/* Get a cursor */
my_database->cursor(my_database, NULL, &cursorp, 0);
/* Initialize our DBTs. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
/* Iterate over the database, retrieving each record in turn. */
while ((ret = cursorp->c_get(cursorp, &key,
&data, DB_PREV)) == 0) {
/* Do interesting things with the DBTs here. */
}
if (ret != DB_NOTFOUND) {
/* Error handling goes here */
}
// Cursors must be closed
if (cursorp != NULL)
cursorp->c_close(cursorp);
if (my_database != NULL)
my_database->close(my_database, 0);
9. 使用游标查找记录
使用DBC->c_get()可查找与key或者key和data匹配的记录,如果数据库支持已序多重记录集,还可执行部分匹配 (partial matches)。参数key和data会被填充找到的DB记录,游标指向找到的记录位置。如果查找失败,游标状态保持不变,返回 DB_NOTFOUND。
DBC->c_get()可带如下的标记:
- DB_SET
移动游标到第一条匹配key的数据库记录。
- DB_SET_RANGE
和DB_SET的Cursor.getSearchKey()类似,除非使用BTree的访问方法,在BTree的访问方法下,游标移到第一条大于等于key的记录位置,这种比较由你提供比较函数,或者缺省按字典顺序。
比如数据库存储了如下key:
Alabama
Alaska
Arizona
如果查找的key为Al,则游标移到Alabama,key为Alas游标移到Alaska,key为Ar移动到最后一条记录。
- DB_GET_BOTH
移动游标到第一条匹配key和data的记录位置。
- DB_GET_BOTH_RANGE
和DB_SET_RANGE类似,先匹配key,然后匹配data。
假设数据库存储了如下的key/data对,
Alabama/Athens
Alabama/Florence
Alaska/Anchorage
Alaska/Fairbanks
Arizona/Avondale
Arizona/Florence
则查找的key/data和查找结果关系如下
待查的key 待查的data 游标位置
---------------------------------------
Al Fl Alabama/Florence
Ar Fl Arizona/Florence
Al Fa Alaska/Fairbanks
Al A Alabama/Athens
假设有一个数据库存储了美国的州(key)和城市(data)的关系,则如下代码片断可定位游标的位置,然后打印key/data对:
#include
#include
...
DBC *cursorp;
DBT key, data;
DB *dbp;
int ret;
char *search_data = "Fa";
char *search_key = "Al";
/* database open omitted for clarity */
/* Get a cursor */
db