Berkeley db使用方法简介(c接口)

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);
}
6. DB记录
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;
7. 读写DB记录和数据的持久化
默认情况下,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

你可能感兴趣的:(Berkeley db使用方法简介(c接口))