Berkeley DB for C
在开打数据库前必须通过db_create()方法来初始化一个db句柄, 然后你可以通过他的open方法来打开一个数据库
示例:
#include <db.h> //必要的包含文件
...
DB *dbp; /* 数据库结构句柄 */
u_int32_t flags; /* 数据库打开标志 */
int ret; /* 用来接收通过db_create来创建的数据库是否成功 */
/* 初始化结构体
* 因为数据库没有在环境中打开
* 所以环境指针为null. */
ret = db_create(&dbp, NULL, 0);
if (ret != 0) {
/* 错误处理 */
}
/* 数据库打开标志 */
flags = DB_CREATE; /* 如果数据库不存在的化则创建一个新的*/
/* 打开数据库 */
ret = dbp->open(dbp, /* 数据库结构指针 */
NULL, /* 事务处理指针 */
"my_db.db", /* 数据库名 */
NULL, /* Optional logical database name */
DB_BTREE, /* 数据库访问方式 */
flags, /* 打开标识 */
0); /* 文件模式 (使用默认的) */
if (ret != 0) {
/* 错误处理 */
}
一旦你使用完毕某个数据库,你必须通过DB->close()来关闭他。除非你再次打开他否则关闭后的数据库是不能在使用的。在关闭数据库之前请确保与之相关的游标先关闭,否则会有很多想不到的后果。关闭数据库后很多缓存在内存中的数据会被同步到你的硬盘中去,而且你也可以随时通过DB->sync()方法来随时同步数据库。
使用示例:
#include <db.h>
...
DB *dbp; /* 数据库句柄 */
...
/*
* 在这里进行一些数据库有关的操作
*/
...
/* 当你不在使用他的时候关闭他. */
if (dbp != NULL)
dbp->close(dbp, 0);
如果在打开数据库的时候使用多个标识则可以通过按位或来添加多个标示
示例:
u_int32_t open_flags = DB_CREATE | DB_EXCL;
l 标示说明:
DB_CREATE:当数据不存在的时候创建一个新的
DB_EXCL:创建专用数据库,如果数据库已经存在则将会打开失败,这个标识只能与DB_CREATE联合使用
DB_RDONLY:以只读方式打开数据库,所有的写操作都被禁止。
DB_TRUNCATE:打开数据库的同时清空数据库中原有的记录。
下面这些方法可能会对你管理数据库有用
l DB->get_open_flags():返回当前的数据库打开标识,如果在打开数据库前调用此方法会出错。
使用示例:
#include <db.h>
...
DB *dbp;
u_int32_t open_flags;
/* Database open and subsequent operations omitted for clarity */
dbp->get_open_flags(dbp, &open_flags);
l DB->remove()
如果一个数据库已经不再需要,则可以使用这个方法来删除。
示例:
#include <db.h>
...
DB *dbp;
/* Database open and subsequent operations omitted for clarity */
dbp->remove(dbp, /* 数据库句柄 */
"mydb.db", /* 要移除的数据库 */
NULL, /* Database to remove. This is
* NULL so the entire file is
* removed. */
0); /* Flags. None used. */
l DB->rename()
给目标数据库改名,不要试图给一个已经打开了的数据库改名,那样会出错的。
使用示例:
#include <db.h>
...
DB *dbp;
/* Database open and subsequent operations omitted for clarity */
dbp->rename(dbp, /* Database pointer */
"mydb.db", /* 要被改名的数据库 */
NULL, /* Database to rename. This is
* NULL so the entire file is
* renamed. */
"newdb.db", /* 新的数据库名 */
0);
l 与错误报告有关的几个函数
set_errcall():设置出错的时候调用的函数
set_errfile():设置出错的时候 把错误信息写进去的文件指针(file *)
set_errpfx():设置有数据库产生的错误类型
err():产生一个错误,如果指定了错误的回调函数或文件指针则把错误发送给相应的处理方法或文件,否则发送到标准出错。
errx():与err()雷同
除此之外你可以通过db_strerror()来直接的返回错误代号。
使用示例:
/*
* Function called to handle any database error messages
* issued by DB.
*/
void
my_error_handler(const char *error_prefix, char *msg)
{
/*
* 相关的错误处理
*/
}
注册错误回调函数的方法如下
#include <db.h>
#include <stdio.h>
...
DB *dbp;
int ret;
/*
* 为错误创建一个初始化的环境
*/
ret = db_create(&dbp, NULL, 0);
if (ret != 0) {
fprintf(stderr, "%s: %s/n", "my_program",
db_strerror(ret));//直接输出错误信息
return(ret);
}
/* 设置回调函数 */
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);
}
要想设置数据库环境,首先你需要创建一个环境句柄并且打开他,在打开的时候你必须确保存在指定的目录,同样你页可以设置环境所使用的内存cache.
使用示例:
#include <db.h>
...
DB_ENV *myEnv; /* db环境结构句柄 */
DB *dbp; /* db结构句柄 */
u_int32_t db_flags; /* 数据库打开标识 */
u_int32_t env_flags; /* 环境打开标识 */
int ret; /* 用来存储打开结果(成功还是失败) */
/*
创建一个数据库环境
*/
ret = db_env_create(&myEnv, 0);
if (ret != 0) {
//错误处理
return -1;
}
/* 打开环境. */
env_flags = DB_CREATE | /* 如果环境不存在则创建一个新的*/
DB_INIT_MPOOL; /* 初始化内存大小. */
ret = myEnv->open(myEnv, /* 数据库环境指针 */
"/export1/testEnv", /* 环境目录 */
env_flags, /* 打开标识 */
0); /* File mode (default) */
if (ret != 0) {
fprintf(stderr, "Environment open failed: %s", db_strerror(ret));
//错误处理
return -1;
}
一但打开一个数据库环境后,你就可以通过他来打开数据库。通常情况下数据库默认存储在环境的根目录或其相对路径
使用示例:
/*
* 创建数据库
*/
ret = db_create(&dbp, myEnv, 0);
if (ret != 0) {
/* Error handling goes here */
}
/* 数据库打开标志 */
db_flags = DB_CREATE; /* If the database does not exist,
* create it.*/
/* 打开数据库 */
ret = dbp->open(dbp, /* db结构指针 */
NULL, /* 事务处理指针 */
"my_db.db", /* 数据库名 */
NULL, /* Optional logical database name */
DB_BTREE, /* 数据库访问方式 */
db_flags, /* 打开标识符 */
0); /* File mode (using defaults) */
if (ret != 0) {
/* 错误处理 */
}
当你使用完一个环境后必须关闭他,请确保在关闭一个数据库环境前先关闭所有数据库。
使用示例:
/*
* 关闭数据库和环境
*/
if (dbp != NULL) {
dbp->close(dbp, 0);
}
if (myEnv != NULL) {
myEnv->close(myEnv, 0);
}
每条数据库记录是由两个DBT结构体所组成,一个代表键名(key),一个代表键值(value);
为了存储数据我们只需要拿一个指针指向这段数据的首地址,并且标识出这段数据的长度就行了。具体使用示例如下:
DBT key, data; //分别定义key和value
float money = 122.45;
char *description = "Grocery bill.";
/* 开始使用DBT的时候给DBT清0 */
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;
为了能够得到这条记录,可以通过void指针指向DBT.
某些系统的float和struct类型的内存对齐方式比较特殊,为了兼容性,应该使用Dbt的set_ulen函数设置内存长度,并且用set_flag(DB_DBT_USERMEM)函数注明使用BDB的内存处理方式.
只有在获取数据的时候,才需要ulen的值。get()操作后,DBT的sizefield会设置为data的实际大小。可以通过设置flag为0,获得data的实际大小。如果设置了flag为DB_DBT_USERMEM,那么必须使用try-catch模板包围get()操作,如果返回的data长度超过了ulen的大小,会issues DBException,打印出来即为 DB_BUFFER_SMALL 错误。
1. 读和些记录
在读和写数据库记录的时候需要了解掉你的数据库是否支持多重记录,如果有两条或多条数据对应同一个key值则表示数据库支持多条记录。默认情况下数据库是不支持多从记录的,在游标那节我们将会介绍怎样通过游标来操作多条记录。
DB提供了两个基本的方法来存储和检索数据。
a) DBT->put()和DBT->get()这两个方法提供了检索和存储非多重记录的简单方法。
b) 当然也可以通过游标的方式来获取,游标将在后面的章节做介绍。
通过DBT->put向数据库中写记录的时候,你也许能够用到DB_NOOVERWRITE这个标识,他表示如果记录已经在数据库中存在,则返回DB_KEYEXIST给你,即使数据库支持多重记录他也是会返回的。使用示例:
#include <db.h>
#include <string.h>
...
char *description = "Grocery bill.";
DBT key, data;
DB *my_database;
int ret;
float money;
/* Database open omitted for clarity */
money = 122.45;
/* DBT数据清0. */
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()方法来从数据库中读取记录。如果你的数据库支持多重记录则会返回第一条记录。
#include <db.h>
#include <string.h>
...
DBT key, data;
DB *my_database;
float money;
char *description[DESCRIPTION_SIZE + 1];
/* Database open omitted for clarity */
money = 122.45;
/* 清0. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data = &money;
key.ulen = sizeof(float);
data.set_data(&description);//设置数据
data.set_ulen(DESCRIPTION_SIZE + 1);
data.set_flags(DB_DBT_USERMEM);
my_database->get(my_database, NULL, &key, &data, 0);
/*
* Description is set into the memory that we supplied.
*/
同样的你也可以通过DB->del()方法来删除一条记录,如果key对应多重记录则所有的记录都会被删除,同样的你可以通过DB->truncate()方法来清空所有的记录。
使用示例:
#include <db.h>
#include <string.h>
...
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);
BDB与硬盘IO之间的同步方式采用的是异步的方式,也就是说对数据进行写操作的时候不是立即dump到本地硬盘中去的,当关闭数据库的时候所有的数据会立即dump到本地硬盘中去的,当然你也可以通过调用DB->sync().来随时同步,但频繁的IO肯定会对效率大打则扣。如果在向硬盘同步数据完毕前发生了错误,数据就有可能丢失。可以通过DB->verify()这个方法来检查数据库,必要的时候也可以通过db_dump方法来挽留尽可能多的数据。
今天先到这里,明天继续,有点困了。