
Rogue Wave Software Inc.'s DBtools.h++ offers standard, C++ call-level interface object-oriented access to relational DBMSs.
DBtools.h++ by Rogue Wave Software Inc. is a C++ class library for relational database applications, and it builds upon (and includes) Rogue Wave's popular tools.h++ library. It abstracts relational database entities and operations to give developers a common, object-oriented interface to Informix, CA-OpenIngres, Oracle, Sybase, or ODBC databases. It even supports simultaneous connections to different vendors' databases, and it transparently converts data types between databases. Optionally distributed with source code, it supports many Unix platforms in addition to Windows, Windows NT, and OS/2.
Although DBtools.h++ is available for several platforms and databases, I reviewed the source-code version on an SGI Unix workstation with an Informix database. Installation on other Unix systems will be similar. There are three sets of files you need to install: the tools.h++ class libraries, the DBtools.h++ class libraries, and the database-access libraries for your database(s). Once all of the files have been copied to disk (using Unix "tar" and "uncompress"), you need only run several shell scripts to customize header files and makefiles for your compiler to build the libraries. You then have the option of building default versions of the libraries or modifying the makefiles to your needs -- adding debugging flags or creating static instead of shared libraries, for example -- before compiling the libraries.
There isn't enough room here to describe each of the DBtools.h++ classes in detail, but in broad strokes, most of the classes fall into three categories: database entities (such as databases and tables), database data types (such as datetime, duration, and BLOB), and database operations (such as select, insert, update, and delete).
The database (RWDBDatabase) class manages database connections. It is possible to let the DBtools.h++ manage your connections implicitly, opening and closing connections whenever it needs to do so. For greater efficiency, however, and especially if you must have multiple connections open simultaneously, you should connect to the database explicitly through RWDBDatabase.
Once your database connection is open, most of your interaction with your data will be through the table (RWDBTable) class. A table can be a database table, the results of a database operation (a select, for example), or a table stored entirely in memory (allowing fast, random access to table data). The table class is used in conjunction with the data type and operations classes to move data between your application and the database. The data type classes present a uniform interface to your data no matter which database you're using.
The simple example shown in Listing 1 (page 38) gives you an idea of how the DBtools.h++ classes work together. This code fragment connects to the RaptorDB database, selects rows from the "dinosaurs" and "incage" tables, and then prints the results one row at a time. As you can see, the DBtools.h++ classes seamlessly merge decidedly non-object-oriented SQL queries with C++ constructs. If you're familiar with either SQL or C++, you'll have no trouble understanding DBtools.h++ code. Also, if you're relatively new to object-oriented programming or if you still need to be convinced of its usefulness, DBtools.h++ classes (and Rogue Wave's other class libraries) offer excellent examples of the power of object-oriented design.
In addition to the simple query shown in Listing 1, DBtools.h++ supports arbitrarily complex queries, inserts, updates, deletes, and stored procedures. It also provides an interface to vendor-specific database operations via the RWDBSystemHandle class. Of course, the more you use RWDBSystemHandle functions, the less portable your code becomes.
In combination, C++ and DBtools.h++ allow you to create a simple interface for other programmers in your group while you (and DBtools.h++) handle the intricacies of your database access behind the scenes. You can create your own database types in your implementation, or you can specify a simple "Save" method and, when it's called, write different records to your Informix order processing, Sybase customer support, and Oracle financials databases.
One area in which C++ has only recently stabilized (in commercial compilers) is in its treatment of errors, or exceptions. Because some C++ compilers still do not support standard C++ exception processing, DBtools.h++ gives you a choice. You can check the status of database operations, write error handlers, or use exceptions. Checking status is the traditional, procedural method that we're all familiar with to check for errors.
Error handlers are a DBtools.h++ hybrid that takes advantage of C++ but doesn't rely on exceptions. An error handler is invoked for a DBtools.h++ object whenever its status changes to a state other than "ok." The default error handler does nothing, but you can replace it with your own -- either a single one for all objects or more specialized handlers for specific objects. To take full advantage of C++ exceptions, simply write the code in your error handler to throw an exception, and then use "try" blocks and catch the exceptions you need to catch. In all cases, to help you track down your errors you still have access to database-specific error messages through DBtools.h++'s object-oriented framework.
Another important feature of a class library is its documentation. DBtools.h++ includes a user's guide with tutorials, a reference manual, and separate database access library documentation for database-specific guidelines. The user's guide gets you up and running quickly, and the reference manual provides the depth you need with comprehensive details on each class.
My only concern with DBtools.h++ is strategic and has little to do with the product specifically, but more to do with its entire class of development tools. You must carefully consider the risks and rewards of using third-party libraries for your projects. On the one hand, they can instantly provide a tested base of code that might take years of effort to develop. On the other hand, you depend on your library vendor for bug fixes, updates, and new releases. Furthermore, you must have confidence that the vendor and its product will be around through the life of your product. And finally, with most major database vendors adding object-oriented functionality to their flagship products (such as Oracle 8 and Informix-Universal Server), and with progress being made in OQL standards, new object-oriented database interfaces may soon appear.
That being said, Rogue Wave tools are better than most. The company's tools.h++ product has become a de facto standard for C++ development, and DBtools.h++ has the potential to become the same for C++ database development. DBtools.h++ is well-designed and complete enough for any database development project, and it is indispensable for multidatabase, cross-platform development.
RWDBDatabase RaptorDB = RWDBManager::database
("INFORMIX",
"raptor_server",
"raptor_user",
"vel0ci", // password
"raptor_db" // database name);
if ( ! RaptorDB.isValid())
{cout << RaptorDB.status().message() << "\n"; return(0);}
RWDBConnection connection = RaptorDB.connection();
RWDBTable dinosaurs = RaptorDB.table("dinosaurs");
RWDBTable incage = RaptorDB.table("incage");
RWDBSelector selector = RaptorDB.selector();
select
<< dinosaurs["id"]
<< dinosaurs["name"]
<< dinosaurs["cage_id"]
<< incage["status"]
<< incage["status_time"];
select.where(dinosaurs["id"] == incage["id"]
&& incage["status"] == "OUT");
select.orderBy(dinosaurs["id"]);
select.orderBy(incage["status_time"]);
RWDBReader reader = select.reader(connection);
while (reader())
{int id;
RWCString name;
int cage_id;
RWCString status;
RWDBDateTime status_time;
reader
>> id >> name >> cage_id >> status >> status_time;
cout
<< id << "\t"
<< name << "\t"
<< cage_id << "\t"
<< status << "\t"
<< status_time << "\n";
} // end while
An example of how the DBtools.h++ classes work together.