Creating a Global Database Object

Article ID: 1105
Last updated: 01 Feb, 2008
Article ID: 1105
Last updated: 01 Feb, 2008
Revision: 1
Views: 4774
Posted: 30 Aug, 1999
by Dean J.
Updated: 01 Feb, 2008
by Dean J.

The application has runtime errors on program exit when using a global or static RWDBDatabase object. How can I have a global database object without the errors?


This is a known problem and is documented in the DBTools readme file: The creation of global RWDBDatabase instances is problematic on virtually all platforms due to static initialization issues and the order of destruction of static objects. The problem lies in the destruction of the RWDBDatabase object. All other DBTools.h++ objects are created from the RWDBDatabase object, so this must be the last object to be destroyed. The order of destruction of global objects does not occur in a predictable order.


Because having a global or static instance of RWDBDatabase causes problems, the general solution is to create a pointer to a RWDBDatabase object, and allocate an object off the heap at runtime, deleting it before program exit. Say, for example, the following code is defined globally:

RWDBDatabase* dbPtr;

You would then use the pointer in code such as the following, either in main() or some function called at runtime:

dbPtr = new RWDBDatabase (RWDBManager::database(

RWDBConnection myConn = dbPtr->connection();
// ... access the database ...

delete dbPtr;

This results in somewhat "messy" code. If you do not have access to main(), it would be preferable to have a global instance of the database object, connection, tracer and error handler, which can be accessed by any code module in the application. A more elegant solution would encapsulate all of these objects into a single class, which is instantiated as a single global wrapper object, named gdbObj (global database object) in this example.

The constructor for the gdbObj dynamically allocates the RWDBDatabase and other objects off the heap, and the destructor will delete them in a predictable order. If a gdbObj object is defined globally, then the database object is both "global", and self-instantiating and is destroyed cleanly on program exit.

The gdbObj object can be defined and implemented in one of the .cpp files, such as the definition file for gdbObj, then that instance can be declared with extern linkage so that other code modules can access the global gdbObj object, and access the RWDBDatabase, RWDBConnection, and other objects being encapsulated. Then, other code modules can access the "global" RWDBDatabase and RWDBConnection through access functions. For example, if our gdbObj instance is called gdb, and we want to open a table, we could include the gdb.h header file, then write code such as this:

RWDBTable phonebook = gdb.gdbDB().table("phonebook");

The following code is the complete gdbObj declaration and implementation files, gdb.h and gdb.cpp respectively. This example encapsulates an error handler, a RWDBDatabase, a RWDBTracer, and a RWDBConnection.

/* contents of gdb.h, the "global" database object wrapper class */
#include /* ----------------------------------------------------- ** Class declaration for the "global" database object ** ----------------------------------------------------- */ class gdbObj { private: RWDBDatabase* gdbDBPtr; public: gdbObj(); ~gdbObj(); // access function for the database object RWDBDatabase& gdbDB() { return *gdbDBPtr; } }; /* ----------------------------------------------------- ** This declares the "global" database object ** it actually exists (is defined) in gdb.cpp ** the extern makes it available globally to anything ** that includes this header file ** ----------------------------------------------------- */ extern gdbObj gdb;
  /*******************************************************************/ /* contents of gdb.cpp, the "global" database object wrapper class */ /*******************************************************************/ #include  #include "gdb.h"  /* ----------------------------------------------------- ** The error handler function. ** ----------------------------------------------------- */ void errhdlr(const RWDBStatus &status) {     if (status.errorCode() == RWDBStatus::ok) return;     cout << status.message() << endl          << "Message1 "   << status.vendorMessage1 () << endl          << "Message2 "  << status.vendorMessage2 () << endl          << "Error1 "    << status.vendorError1 () << endl          << "Error2 "    <<  status.vendorError2 () << endl; }  /* ----------------------------------------------------- ** Constructor for gdbObj;  ** creates DB objects and connects ** this is called for the one global gdbObj instance  ** before main() starts ** ----------------------------------------------------- */ gdbObj::gdbObj() {     RWDBManager::setErrorHandler (errhdlr); 	gdbDBPtr = new RWDBDatabase (RWDBManager::database( 		"ora7d.dll", 		"server", 		"jdoe", 		"password", 		""));  	if (gdbDBPtr->isValid()) { 		cout << "connected!" << endl; 	} else { 		cout << "could not connect; exiting" << endl; 		 		delete gdbDBPtr; 		exit (-1); 	} }  /* ----------------------------------------------------- ** destructor for gdbObj; deletes DB objects in a  ** controlled way, called when the program exits ** ----------------------------------------------------- */ gdbObj::~gdbObj() { 	delete gdbDBPtr; }  /* ----------------------------------------------------- ** This is our "global database object" ** we must have only one of these -- this one. ** ----------------------------------------------------- */ gdbObj gdb;  An example: --------------- int main() {   RWDBConnection conn = gdb.gdbDB().connection();   RWDBTable phonebook = gdb.gdbDB().table("phonebook");    RWDBSelector sel = gdb.gdbDB().selector();   sel << phonebook;     RWDBReader rdr = sel.reader(conn);   while(rdr())   {    process the results ....   } }   
This article was:   Helpful | Not helpful
Report an issue
Article ID: 1105
Last updated: 01 Feb, 2008
Revision: 1
Views: 4774
Posted: 30 Aug, 1999 by Dean J.
Updated: 01 Feb, 2008 by Dean J.

Others in this category