berkeley db 游标使用

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);

 

 

使用重复记录

如果两个记录共享相同的键值。对于重复的记录,只记录的数据独一无二的部分是。

重复的记录仅支持BTreehash方法。

如果你的数据库支持重复的记录,那么它可能会包含多个记录共享相同的键值。默认情况下,正常的数据库操作将只返回第一个这样的记录,在一组重复记录。通常情况下,随后的重复记录的访问使用游标。数据库支持重复记录,以下Dbc::get()标志是有趣的:

DB_NEXTDB_PREV显示下一首/上一记录在数据库中,无论它是一个重复的当前记录。使用这些方法的一个例子,请参阅使用游标获取记录。

DB_GET_BOTH_RANGE寻求光标移动到一个特定的记录,无论它是否支持重复的记录都非常有用。

DB_NEXT_NODUPDB_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()。在下一章中,我们将利用的二级索引库扩展该应用程序,这样我们就可以查询特定清单项目清单数据库中。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值