1. 游标使用
游标提供了一种机制,通过它可以遍历数据库中的记录。使用游标,你可以读、写、删除数据库中的记录。如果数据库允许重复的记录,然后光标是最简单的方法,你可以访问任何给定的键以外的第一条记录。
本章介绍了游标。它解释了如何打开和关闭它们,如何使用它们来修改数据库,以及如何使用这些重复记录。
1.1. 打开和关闭游标
游标管理使用DBC类。要使用游标,你必须打开它使用Db::cursor()方法。
例如:
#include <db_cxx.h>
...
Dbc *cursorp;
Db my_database(NULL, 0);
// Database open omitted for clarity
// Get a cursor
my_database.cursor(NULL, &cursorp, 0);
当你使用完游标,你应该关闭它。要关闭游标,请致电DBC:: close()方法。请注意,关闭你的数据库游标时,仍然打开DB手柄的范围内,特别是如果这些游标写入数据库,可以产生不可预知的结果。关闭你的数据库之前,请务必关闭游标。
#include <db_cxx.h>
...
Dbc *cursorp;
Db my_database(NULL, 0);
// Database and cursor open omitted for clarity
if (cursorp != NULL)
cursorp->close();
my_database.close(0);
1.2. 使用游标获取记录
要遍历数据库中的记录,从第一条记录的最后,只需打开游标,然后,使用DBC:: get()方法。请注意,您需要提供这种方法的DB_NEXT标志。例如:
#include <db_cxx.h>
...
Db my_database(NULL, 0);
Dbc *cursorp;
try {
// Databaseopen omitted for clarity
// Get a cursor
my_database.cursor(NULL, &cursorp, 0);
Dbt key, data;
int ret;
// Iterate overthe database, retrieving each record in turn.
while ((ret =cursorp->get(&key, &data, DB_NEXT)) == 0) {
// Dointeresting things with the Dbts here.
}
if (ret !=DB_NOTFOUND) {
// retshould be DB_NOTFOUND upon exiting the loop.
// Dbc::get()will by default throw an exception if any
//significant errors occur, so by default this if block
// cannever be reached.
}
} catch(DbException &e) {
my_database.err(e.get_errno(), "Error!");
} catch(std::exception &e) {
my_database.errx("Error! %s", e.what());
}
// Cursors must be closed
if (cursorp != NULL)
cursorp->close();
my_database.close(0);
要遍历数据库的最后一条记录的第一个,使用DB_PREV而不是DB_NEXT的:
#include <db_cxx.h>
...
Db my_database(NULL, 0);
Dbc *cursorp;
try {
// Databaseopen omitted for clarity
// Get a cursor
my_database.cursor(NULL, &cursorp, 0);
Dbt key, data;
int ret;
// Iterate overthe database, retrieving each record in turn.
while ((ret =cursorp->get(&key, &data, DB_PREV)) == 0) {
// Dointeresting things with the Dbts here.
}
if (ret !=DB_NOTFOUND) {
// retshould be DB_NOTFOUND upon exiting the loop.
//Dbc::get() will by default throw an exception if any
//significant errors occur, so by default this if block
// cannever be reached.
}
} catch(DbException &e) {
my_database.err(e.get_errno(), "Error!");
} catch(std::exception &e) {
my_database.errx("Error! %s", e.what());
}
// Cursors must be closed
if (cursorp != NULL)
cursorp->close();
my_database.close(0);
1.2.1. 搜索记录
您可以使用游标来搜索数据库记录。可以根据键值搜索,也可以通过键值与数据进行搜索键。如果你的数据库支持重复集排序,您也可以执行部分匹配。
此外,如果搜索失败,则游标的状态保持不变,并返回DB_NOTFOUND的。
使用游标来搜索一个记录,使用,Dbt::get().。当你使用这种方法,你可以提供以下标志:
(请注意,在下面的列表中,游标标志使用关键字SET(),来检查记录的关键字(在这种情况下,光标被设置到该记录的值相匹配的所提供的光标键)。此外,当游标使用包含GET,则游标被定位成提供的游标键和数据值。)
•DB_SET将游标移动到数据库中,指定的键值的第一条记录。
•DB_SET_RANGE相同DB_SETCursor.getSearchKey(),除非您使用的是btree访问。在这种情况下,将游标移动到数据库中的第一条记录,其键值是大于或等于指定的键值。这种比较函数由应用自己确定。如果没有提供的比较函数,那么默认字序排序。
例如,假设你有数据库使用以下字符串作为键的记录:
· Alabama/Athens
· Alabama/Florence
· Alaska/Anchorage
· Alaska/Fairbanks
· Arizona/Avondale
· Arizona/Florence
然后提供:
|
a search key of ... |
and a search data of ... |
moves the cursor to ... |
|
Alaska |
Fa |
Alaska/Fairbanks |
|
Arizona |
Fl |
Arizona/Florence |
|
Alaska |
An |
Alaska/Anchorage |
例如,假设一个数据库,其中包含美国各州/美国城市的键/数据对排序的重复记录,那么下面的代码片段可以用于将光标定位到任何数据库中的记录,并打印键/数据的值:
#include <db_cxx.h>
#include <string.h>
...
Db my_database(NULL, 0);
Dbc *cursorp;
try {
// databaseopen omitted for clarity
// Get a cursor
my_database.cursor(NULL, &cursorp, 0);
// Searchcriteria
char *search_key= "Alaska";
char*search_data = "Fa";
// Set up ourDBTs
Dbtkey(search_key, strlen(search_key) + 1);
Dbtdata(search_data, strlen(search_data) + 1);
// Position thecursor to the first record in the database whose
// key matchesthe search key and whose data begins with the search
// data.
int ret =cursorp->get(&key, &data, DB_GET_BOTH_RANGE);
if (!ret) {
// Dosomething with the data
}
} catch(DbException &e) {
my_database.err(e.get_errno(), "Error!");
} catch(std::exception &e) {
my_database.errx("Error! %s", e.what());
}
// Close the cursor
if (cursorp != NULL)
cursorp->close();
// Close the database
my_database.close(0);
使用重复记录
如果两个记录共享相同的键值。对于重复的记录,只记录的数据独一无二的部分是。
重复的记录仅支持BTree或hash方法。
如果你的数据库支持重复的记录,那么它可能会包含多个记录共享相同的键值。默认情况下,正常的数据库操作将只返回第一个这样的记录,在一组重复记录。通常情况下,随后的重复记录的访问使用游标。数据库支持重复记录,以下Dbc::get()标志是有趣的:
•DB_NEXT,DB_PREV显示下一首/上一记录在数据库中,无论它是一个重复的当前记录。使用这些方法的一个例子,请参阅使用游标获取记录。
•DB_GET_BOTH_RANGE寻求光标移动到一个特定的记录,无论它是否支持重复的记录都非常有用。
•DB_NEXT_NODUP,DB_PREV_NODUP
获取下一个/上一个不重复的记录。这可以让你跳过所有重复的一组重复记录。如果你调用DBC :: get()方法DB_PREV_NODUP,然后将光标定位到最后一个记录在数据库中的前一个关键。例如,如果你在你的数据库中有以下记录:
Alabama/Athens
Alabama/Florence
Alaska/Anchorage
Alaska/Fairbanks
Arizona/Avondale
Arizona/Florence
将光标定位至Alaska/Fairbanks,然后调用DBC :: get()方法与DB_PREV_NODUP,然后将光标定位到Alabama/Florence。同样,如果您调用DBC :: get()方法DB_NEXT_NODUP,然后将光标定位到第一条记录在数据库中对应的下一个键值。
如果在数据库中不存在下一个/前一个键,然后返回DB_NOTFOUND,并且游标保持不变。
•DB_NEXT_DUP获取下一条记录,共用目前的键值。如果将游标定位重复集的最后一条记录,你调用DBC :: get()方法DB_NEXT_DUP,然后返回DB_NOTFOUND,并且游标保持不变。
例如,下面的代码。
#include <db_cxx.h>
#include <string.h>
...
char *search_key = "Al";
Db my_database(NULL, 0);
Dbc *cursorp;
try {
// databaseopen omitted for clarity
// Get a cursor
my_database.cursor(NULL, &cursorp, 0);
// Set up ourDBTs
Dbtkey(search_key, strlen(search_key) + 1);
Dbt data;
// Position thecursor to the first record in the database whose
// key and databegin with the correct strings.
int ret =cursorp->get(&key, &data, DB_SET);
while (ret !=DB_NOTFOUND) {
std::cout<< "key: " << (char *)key.get_data()
<< "data: " << (char *)data.get_data()<<std::endl;
ret =cursorp->get(&key, &data, DB_NEXT_DUP);
}
} catch(DbException &e) {
my_database.err(e.get_errno(),"Error!");
} catch(std::exception &e) {
my_database.errx("Error! %s", e.what());
}
// Close the cursor
if (cursorp != NULL)
cursorp->close();
// Close the database
my_database.close(0);
1.3. 使用游标写记录
您可以使用游标将记录写到数据库中。游标的行为取决于你的数据库是否支持重复数据。
请注意,当使用游标写记录到数据库中,游标定位处于你插入的位置。
您可以使用DBC :: put()写记录到数据库中。用这种方法,您可以使用以下标志:
•DB_NODUPDATA如果所提供的键值已经存在于数据库中,则此方法返回DB_KEYEXIST。如果该键不存在,则写入数据,记录顺序由数据使用的排序算法确定。如果排序算法由应用提供,记录被插入在其排序位置。否则(假设B树),字典排序时,用较短的项目整理之前较长的项目。这个标志只能用于B树和哈希方法,只有当数据库已配置为支持排序的重复数据项(DB_DUPSORT在创建数据库时指定)。
这个标志不能使用的队列和Recno接入的方法。对于重复记录的更多信息,请参阅允许重复记录。
•DB_KEYFIRST对于不支持重复的数据库,这个方法的行为完全一样的,如果默认插入。如果数据库支持重复的记录,并已经指定了一个重复的排序功能,其排序位置插入的数据项中添加。如果键已经存在于数据库中,并没有重复的排序功能已被指定,作为该键的数据项的第一个插入的数据项添加。
•DB_KEYLAST行为完全一样的,如果使用DB_KEYFIRST,惟如果该键已经存在于数据库中,并没有重复的排序功能已指定,增加一条,作为插入的数据项,关键的最后一个数据项。
例如:
#include <db_cxx.h>
#include <string.h>
...
char *key1str = "My first string";
char *data1str = "My first data";
char *key2str = "A second string";
char *data2str = "My second data";
char *data3str = "My third data";
Db my_database(NULL, 0);
Dbc *cursorp;
try {
// Set up ourDBTs
Dbtkey1(key1str, strlen(key1str) + 1);
Dbtdata1(data1str, strlen(data1str) + 1);
Dbtkey2(key2str, strlen(key2str) + 1);
Dbtdata2(data2str, strlen(data2str) + 1);
Dbtdata3(data3str, strlen(data3str) + 1);
// Databaseopen omitted
// Get thecursor
my_database.cursor(NULL, &cursorp, 0);
// Assuming anempty database, this first put places
// "Myfirst string"/"My first data" in the first
// position inthe database
int ret =cursorp->put(&key1, &data1, DB_KEYFIRST);
// This putplaces "A second string"/"My second data" in the
// the databaseaccording to its key sorts against the key
// used for thecurrently existing database record. Most likely
// this recordwould appear first in the database.
ret =cursorp->put(&key2, &data2,
DB_KEYFIRST); /* Added according to sort order */
// Ifduplicates are not allowed, the currently existing record that
// uses"key2" is overwritten with the data provided on this put.
// That is, therecord "A second string"/"My second data" becomes
// "Asecond string"/"My third data"
//
// Ifduplicates are allowed, then "My third data" is placed in the
// duplicateslist according to how it sorts against "My second data".
ret =cursorp->put(&key2, &data3,
DB_KEYFIRST); // If duplicates are not allowed, record
// is overwritten with new data. Otherwise,
// the record is added to the beginning of
// the duplicates list.
} catch(DbException &e) {
my_database.err(e.get_errno(), "Error!");
} catch(std::exception &e) {
my_database.errx("Error! %s", e.what());
}
// Cursors must be closed
if (cursorp != NULL)
cursorp->close();
my_database.close(0);
1.4. 使用游标删除记录
使用游标删除记录,只需将光标定位到要删除的记录,然后调用Dbc::del()。
#include <db_cxx.h>
#include <string.h>
...
char *key1str = "My first string";
Db my_database(NULL, 0);
Dbc *cursorp;
try {
// Databaseopen omitted
// Get thecursor
my_database.cursor(NULL, &cursorp, 0);
// Set up ourDBTs
Dbtkey(key1str, strlen(key1str) + 1);
Dbt data;
// Iterate overthe database, deleting each record in turn.
int ret;
while ((ret =cursorp->get(&key, &data,
DB_SET)) ==0) {
cursorp->del(0);
}
} catch(DbException &e) {
my_database.err(e.get_errno(), "Error!");
} catch(std::exception &e) {
my_database.errx("Error! %s", e.what());
}
// Cursors must be closed
if (cursorp != NULL)
cursorp->close();
my_database.close(0);
1.5. 使用游标更新记录
您更新数据库记录的数据通过使用DBC:: put()方法与DB_CURRENT标志。
#include <db_cxx.h>
#include <string.h>
...
Db my_database(NULL, 0);
Dbc *cursorp;
int ret;
char *key1str = "My first string";
char *replacement_data = "replace me";
try {
// Databaseopen omitted
// Get thecursor
my_database.cursor(NULL, &cursorp, 0);
// Set up ourDBTs
Dbtkey(key1str, strlen(key1str) + 1);
Dbt data;
// Position thecursor */
ret =cursorp->get(&key, &data, DB_SET);
if (ret == 0) {
data.set_data(replacement_data);
data.set_size(strlen(replacement_data) + 1);
cursorp->put(&key, &data, DB_CURRENT);
}
} catch(DbException &e) {
my_database.err(e.get_errno(), "Error!");
} catch(std::exception &e) {
my_database.errx("Error! %s", e.what());
}
// Cursors must be closed
if (cursorp != NULL)
cursorp->close();
my_database.close(0);
请注意,你不能使用此方法更改记录的键,更新操作时,此键值总被忽略。
当更换一个记录的数据部分,如果您要更换的记录,这是一个成员的排序重复集,只有当新纪录的排序与旧纪录相同,更换才能成功。这意味着,如果你要替换的记录,是一个有序的重复集的一员,如果你使用的是默认的字典排序,那么将无法更换,由于违反排序顺序。不过,如果你提供了一个自定义的排序例程,例如,排序的基础上只是几个字节的数据项,然后可能可以进行直接替换,这里所述的限制仍然没有违反。
在这种情况下,如果你想替换的数据所包含的重复记录,您不使用自定义排序程序,然后删除该记录所需的密钥和数据,并创建一个新的记录。
1.6. 游标示例
在数据库使用情况的例子中,我们写了一个应用程序加载与供应商的两个数据库和库存信息。在这个例子中,我们将编写一个应用程序来显示的库存数据库中的所有项目。作为任何给定的存货项目的一部分,我们将看看供应商谁能够提供的资料,显示供应商的联系信息。
具体来说,example_database_read的应用程序执行以下:
1. 打开的库存和供应商所建立的我们的example_database_load应用程序的数据库。请参阅example_database_load的信息,应用程序如何创建数据库和写入数据。
2. 从库存数据库中获取游标。
3. 通过库存数据库的步骤,显示每一个记录。
4. 获取供应商的名称,库存项目的库存记录。
5. 根据供应商的名称,查看供应商在供应商数据库中的记录。
6. 显示供应商记录。
请记住,你可以找到这个应用程序的完整实现:DB_INSTALL/ examples_cxx/ getting_started其中DB_INSTALL DB为安装的位置。
例4.1 example_database_read
首先,我们声明有必要的头文件。我们还写我们的usage()函数。
// File: example_database_read.cpp
#include <iostream>
#include <fstream>
#include <cstdlib>
#include "MyDb.hpp"
#include "gettingStartedCommon.hpp"
// Forward declarations
int show_all_records(MyDb &inventoryDB, MyDb&vendorDB);
int show_vendor(MyDb &vendorDB, const char *vendor);
接下来我们写的main()函数。请注意,它是这里做了些不必要的复杂,因为我们将它扩展,为下一章来执行库存项目查找。
// Displays all inventory items and the associated vendorrecord.
int
main (int argc, char *argv[])
{
// Initializethe path to the database files
std::stringdatabaseHome("./");
// Databasenames
std::stringvDbName("vendordb.db");
std::stringiDbName("inventorydb.db");
// Parse thecommand line arguments
// Omitted forbrevity
try
{
// Open alldatabases.
MyDbinventoryDB(databaseHome, iDbName);
MyDbvendorDB(databaseHome, vDbName);
show_all_records(inventoryDB, vendorDB);
}catch(DbException &e) {
std::cerr<< "Error reading databases. " << std::endl;
std::cerr<< e.what() << std::endl;
return(e.get_errno());
}catch(std::exception &e) {
std::cerr<< "Error reading databases. " << std::endl;
std::cerr<< e.what() << std::endl;
return(-1);
}
return(0);
} // End main
接下来,我们需要写show_all_records()函数。此功能显示所有在数据库中找到库存记录。一旦有库存记录,从该记录中检索供应商的名称,并使用它来查找并显示相应的供应商记录:
// Shows all the records in the inventory database.
// For each inventory record shown, the appropriate
// vendor record is also displayed.
int
show_all_records(MyDb &inventoryDB, MyDb&vendorDB)
{
// Get a cursorto the inventory db
Dbc *cursorp;
try {
inventoryDB.getDb().cursor(NULL, &cursorp, 0);
// Iterateover the inventory database, from the first record
// to thelast, displaying each in turn
Dbt key,data;
int ret;
while ((ret= cursorp->get(&key, &data, DB_NEXT)) == 0 )
{
InventoryData inventoryItem(data.get_data());
inventoryItem.show();
show_vendor(vendorDB, inventoryItem.getVendor().c_str());
}
}catch(DbException &e) {
inventoryDB.getDb().err(e.get_errno(), "Error inshow_all_records");
cursorp->close();
throw e;
}catch(std::exception &e) {
cursorp->close();
throw e;
}
cursorp->close();
return (0);
}
注意,我们这里使用的InventoryData类在InventoryData类中描述。
根据库存记录,我们要显示对应的供应商记录。在这种情况下,我们不需要使用游标来显示供应商记录。这里使用游标,我们的代码稍微复杂,没有很好的效率。相反,我们只需执行get()方法直接对供应商数据库。
// Shows a vendor record. Each vendor record is aninstance of
// a vendor structure. See loadVendorDB() in
// example_database_load for how this structure wasoriginally
// put into the database.
int
show_vendor(MyDb &vendorDB, const char *vendor)
{
Dbt data;
VENDORmy_vendor;
try {
// Set thesearch key to the vendor's name
// vendoris explicitly cast to char * to stop a compiler
//complaint.
Dbtkey((char *)vendor, strlen(vendor) + 1);
// Makesure we use the memory we set aside for the VENDOR
//structure rather than the memory that DB allocates.
// Somesystems may require structures to be aligned in memory
// in aspecific way, and DB may not get it right.
data.set_data(&my_vendor);
data.set_ulen(sizeof(VENDOR));
data.set_flags(DB_DBT_USERMEM);
// Get therecord
vendorDB.getDb().get(NULL, &key, &data, 0);
std::cout<< " " <<my_vendor.street << "\n"
<< " "<< my_vendor.city << ", "
<< my_vendor.state << "\n"
<< " " << my_vendor.zipcode <<"\n"
<< " "<< my_vendor.phone_number << "\n"
<< " Contact:" << my_vendor.sales_rep << "\n"
<< " " << my_vendor.sales_rep_phone
<< std::endl;
}catch(DbException &e) {
vendorDB.getDb().err(e.get_errno(), "Error in show_vendor");
throw e;
}catch(std::exception &e) {
throw e;
}
return (0);
}
完整的example_database_read()。在下一章中,我们将利用的二级索引库扩展该应用程序,这样我们就可以查询特定清单项目清单数据库中。

1463

被折叠的 条评论
为什么被折叠?



