Opening a Transactional Environment and Container

Opening Berkeley DB Databases

To enable transactions for your environment, you must initialize the transactional subsystem. Note that doing this also initializes the logging subsystem. In addition, you must initialize the memory pool (in-memory cache). You must also initialize the locking subsystem. For example:

Notice in the following example that you first create the environment handle, and then you provide the handle to the XmlManager constructor. You do this because you cannot use transactions with the XmlManager instance's default internal environment.

#include "DbXml.hpp"
...

using namespace DbXml;
int main(void)
{
    u_int32_t env_flags = DB_CREATE     |  // If the environment does not
                                           // exist, create it.
                          DB_INIT_LOCK  |  // Initialize locking
                          DB_INIT_LOG   |  // Initialize logging
                          DB_INIT_MPOOL |  // Initialize the cache
                          DB_INIT_TXN;     // Initialize transactions

    DB_ENV *myEnv = NULL;
    XmlManager *myManager = NULL;
    char *envHome = "/export1/testEnv";
    int dberr;

    dberr = db_env_create(&myEnv, 0);
    if (dberr) {
        std::cout << "Unable to create environment: " <<
            db_strerror(dberr) << std::endl;
        if (myEnv)
            myEnv->close(myEnv, 0);
        return (EXIT_FAILURE);
    }

    myEnv->open(myEnv, envHome, env_flags, 0);
    myManager = new XmlManager(myEnv, 0);

    try {
        if (myManager != NULL) {
            delete myManager;
        }
        myEnv->close(myEnv, 0);
    } catch(XmlException &e) {
        std::cerr << "Error closing manager: "
                << envHome << std::endl;
        std::cerr << e.what() << std::endl;
        return (EXIT_FAILURE);
    } catch(std::exception &e) {
        std::cerr << "Error closing environment: "
                << envHome << std::endl;
        std::cerr << e.what() << std::endl;
        return (EXIT_FAILURE);
    }
    return (EXIT_SUCCESS);
} 

You then create and/or open your containers as normal. The only difference is that you must set XmlContainerConfig::setTransactional() to true and pass that object to the openContainer() or createContainer() method. For example:

#include "DbXml.hpp"
...

using namespace DbXml;
int main(void)
{
    u_int32_t env_flags = DB_CREATE     |  // If the environment does not
                                           // exist, create it.
                          DB_INIT_LOCK  |  // Initialize locking
                          DB_INIT_LOG   |  // Initialize logging
                          DB_INIT_MPOOL |  // Initialize the cache
                          DB_INIT_TXN;     // Initialize transactions

    DB_ENV *myEnv = NULL;
    XmlManager *myManager = NULL;
    char *envHome = "/export1/testEnv";
    int dberr;

    dberr = db_env_create(&myEnv, 0);
    if (dberr) {
        std::cout << "Unable to create environment: " <<
            db_strerror(dberr) << std::endl;
        if (myEnv)
            myEnv->close(myEnv, 0);
        return (EXIT_FAILURE);
    }

    myEnv->open(myEnv, envHome, env_flags, 0);
    myManager = new XmlManager(myEnv, 0);

    try {
        XmlContainerConfig cconfig;
        cconfig.setAllowCreate(true);    // If the container does not
                                         // exist, create it.
        cconfig.setTransactional(true);  // Enable transactions.

        std::string containerName = "myContainer.dbxml";
        XmlContainer myContainer =
            myManager->openContainer(containerName, cconfig);

    } catch(XmlException &e) {
        std::cerr << "Error opening environment: "
                  << envHome << std::endl;
        std::cerr << e.what() << std::endl;
        return (EXIT_FAILURE);
    } catch(std::exception &e) {
        std::cerr << "Error opening environment: "
                  << envHome
                  << " or opening XmlManager or XmlContainer." 
                  << std::endl;
        std::cerr << e.what() << std::endl;
        return (EXIT_FAILURE);
    }

    try {
        if (myManager != NULL) {
            delete myManager;
        }
        myEnv.close(0);
    } catch(DbException &e) {
        std::cerr << "Error closing environment: "
                << envHome << std::endl;
        std::cerr << e.what() << std::endl;
        return (EXIT_FAILURE);
    } catch(std::exception &e) {
        std::cerr << "Error closing environment: "
                << envHome << std::endl;
        std::cerr << e.what() << std::endl;
        return (EXIT_FAILURE);
    }
    return (EXIT_SUCCESS);
} 

Opening Berkeley DB Databases

It is possible to use Berkeley DB databases along side of BDB XML containers. When you do this, you will typically use both the databases and the containers from within the same environment so that you can combine operations to both using transactions.

There is no difference between opening a Berkeley DB database in an environment that uses containers and opening a database in an environment that does not use containers (see the Berkeley DB Getting Started with Transaction Processing guide for details on how to do this). You simply share the same environment handle between the two when you open the database(s) and container(s). For example:

#include "DbXml.hpp"
...

using namespace DbXml;
int main(void)
{
    u_int32_t env_flags = DB_CREATE     |  // If the environment does not
                                           // exist, create it.
                          DB_INIT_LOCK  |  // Initialize locking
                          DB_INIT_LOG   |  // Initialize logging
                          DB_INIT_MPOOL |  // Initialize the cache
                          DB_INIT_TXN;     // Initialize transactions

    u_int32_t db_flags = DB_CREATE | DB_AUTO_COMMIT;
    Db *dbp = NULL;
    const char *file_name = "mydb.db";

    DB_ENV *myEnv = NULL;
    XmlManager *myManager = NULL;
    char *envHome = "/export1/testEnv";
    int dberr;

    dberr = db_env_create(&myEnv, 0);
    if (dberr) {
        std::cout << "Unable to create environment: " <<
            db_strerror(dberr) << std::endl;
        if (myEnv)
            myEnv->close(myEnv, 0);
        return (EXIT_FAILURE);
    }

    myEnv->open(myEnv, envHome, env_flags, 0);
    myManager = new XmlManager(myEnv, 0);
    dbp = db_create(&dbp, myEnv, 0);
    dbp->open(NULL,       // Txn pointer
                 file_name,  // File name
                 NULL,       // Logical db name
                 DB_BTREE,   // Database type (using btree)
                 db_flags,   // Open flags
                 0);         // File mode. Using defaults 
    try {
        XmlContainerConfig cconfig;
        cconfig.setAllowCreate(true);    // If the container does not
                                         // exist, create it.
        cconfig.setTransactional(true);  // Enable transactions.

        std::string containerName = "myContainer.dbxml";
        XmlContainer myContainer =
            myManager->openContainer(containerName, cconfig);

    } catch(XmlException &e) {
        std::cerr << "Error opening container: "
                  << e.what() << std::endl;
        return (EXIT_FAILURE);
    } catch(std::exception &e) {
        std::cerr << "Error opening database environment: "
                  << envHome
                  << " or opening XmlManager or XmlContainer." 
                  << std::endl;
        std::cerr << e.what() << std::endl;
        return (EXIT_FAILURE);
    }

    try {
        if (dbp != NULL) {
            dbp->close(dbp, 0);
        }

        if (myManager != NULL) {
            delete myManager;
        }
        myEnv.close(0);
    } catch(DbException &e) {
        std::cerr << "Error closing database and environment: "
                << envHome << std::endl;
        std::cerr << e.what() << std::endl;
        return (EXIT_FAILURE);
    } catch(std::exception &e) {
        std::cerr << "Error closing database environment: "
                << envHome << std::endl;
        std::cerr << e.what() << std::endl;
        return (EXIT_FAILURE);
    }
    return (EXIT_SUCCESS);
} 

Note

Never close a database that has active transactions. Make sure all transactions are resolved (either committed or aborted) before closing the database.