The application layer is the code you write using the DBTools.h++ library. Applications using the library can move data back and forth safely and efficiently between the internal storage layer and the variables in the application. You typically accomplish this by using the insertion << and extraction >> operators of the various database classes. For example, if you want to insert some integers into a table, you would place them into an inserter:
int i = 255; RWDBInserter insert = myTable.inserter(); insert << 1234 << i;
To read a string and a float from a table, you would extract them from a reader:
RWDBReader reader = anotherTable.reader(); while (reader()) { RWCString s; float f; reader >> s >> f; // process s and f }
Code in the application layer of the DBTools.h++ data model is responsible for converting the literal 1234 and the int i into RWDBValue instances for internal storage, and for converting other RWDBValue instances into application variables. As simple as this example is, it illustrates several features provided by the application layer:
Use of general purpose datatypes. The example code above uses general purpose datatypes, ints, floats, and RWCStrings. The database classes RWDBInserter and RWDBReader are used exclusively for data transport; no special types or structures are needed to store the data itself.
Type safety. Database programming is notorious for its lack of type safety. This is mainly because it is impossible to know at compile time which datatypes will belong in which database objects at runtime. By using DBTools.h++, you can attain runtime type safety: the data model ensures that only type-safe conversions will be attempted.
Memory management. Notice that there is no explicit binding of program variables in the example above. In fact, there are no pointers at all. This eliminates a very common cause of error.
Robustness. There isn't any error checking in the example above. For now, simply note that an application can freely continue in the face of errors, without worrying about crashing because of a lack of error checking. (Chapter 5 discusses the DBTools.h++ Error Model.)
The remainder of this chapter briefly describes each of the datatypes supplied by the DBTools.h++ core library. For a detailed listing of each class, please see the DBTools.h++ Class Reference or your Tools.h++ User's Guide, as appropriate.
Class RWCString, from the Tools.h++ class library, is an industry standard for string manipulation. RWCString provides string processing features that are just as efficient as those in C, but far less prone to errors. Its features include memory management, collation, substrings, pattern matching, regular expressions, I/O, tokenizing, and support for multibyte strings.
Class RWCString has member functions to read, compare, store, restore, concatenate, prefix, and append RWCStrings and char*'s. Operators allow access to individual characters, with or without bounds checking.
A full discussion of the many powerful features of this class can be found in the Tools.h++ User's Guide.
Class RWDBDateTime represents a date and time, stored as a Julian day number, plus a time, stored as the number of milliseconds since midnight. The member function isValid() can be used to determine whether an RWDBDateTime represents a valid date and time.
Class RWDBDateTime serves as a compact representation for calendar calculations, shields you from details such as leap years, and performs conversions to and from conventional calendar formats.
RWDBDateTime instances can be converted to and from the Tools.h++ classes RWDate and RWTime, and to and from the Standard C library struct tm defined in <time.h>.
Output formatting is done using the current default Tools.h++ RWLocale object. The current default locale is set via the RWLocale::global() method. See the Tools.h++ User's Guide for more information.
The algorithm RWDBDateTime is used to convert a Gregorian calendar date, for example January 10, 1990, to a Julian day number, as described in Algorithm 199 from Communications of the ACM, Volume 6, No. 8, Aug. 1963, p. 444.
This example prints out the date January 6, 1990, and then calculates and prints the date of the previous Sunday, using the global locale:
#include <rw/db/db.h> #include <rw/rstream.h> int main() { RWDBDateTime dd(1990U, 1, 6); cout << dd.asString() << ", a " << dd.weekDayName() << endl; RWDBDateTime prev = dd.previous("Sunday"); cout << "The previous Sunday is: " << prev.asString() << endl; return 0; }
Program output:
01/06/90 00:00:00.000, a Saturday The previous Sunday is: 12/31/89 00:00:00.000
An RWDBDateTime may be constructed in many ways. For example, you can:
Construct an RWDBDateTime with the current date:
RWDBDateTime d;
Construct an RWDBDateTime for a given year, month, and day:
RWDBDateTime d1(1924U, 1, 24); // 1/24/1924
Construct an RWDBDateTime from an RWDate, supplying hours, minutes, seconds, and milliseconds:
RWDate d(10, 3, 90); // 3/10/1990
RWDBDateTime dt(d, 1, 30, 30, 100) // 3/10/30 1:30:30:100
In the first example, we use the default constructor to create an RWDBDateTime. Be aware that because the default fills in today's date, constructing an array of RWDBDateTime may be slow. If this is an issue, you can derive a class from RWDBDateTime that provides a faster constructor, and use that to declare your arrays.
There are many other constructors, including those that use RWDate or RWTime in various ways. There are accessors for each component of an RWDBDateTime (years, months, days, hours, etc.), as well as for its RWDate component and RWTime component. Both RWDate and RWTime provide powerful localization features which may be accessed and changed via the RWDBDateTime interface. Member operators and functions allow a complete range of arithmetic manipulations on date and time values.
Complete listings of the capabilities of RWDate and RWTime can be found in your Tools.h++ User's Guide; a complete listing of the capabilities of RWDBDateTime is in the DBTools.h++ Class Reference.
Class RWDBDuration represents a time span, stored in a double as a number of seconds. All reasonable arithmetic operations involving time spans are supported, including addition and subtraction, multiplication by a constant, incrementation by seconds, minutes, hours, and so forth.
RWDBDuration also supports arithmetic operations involving the imprecise quantities months and years. These quantities are imprecise because it's not clear how to interpret them. For example, how many days is one day plus one month? DBTools.h++ supports these operations using the following conversions:
#define RWDB_MILLISECONDS_PER_SEC |
((double)1000.0) |
#define RWDB_SECONDS_PER_MIN |
((double)60.0) |
#define RWDB_SECONDS_PER_HR |
(RWDB_SECONDS_PER_MIN*60.0) |
#define RWDB_SECONDS_PER_DAY |
(RWDB_SECONDS_PER_HR*24.0) |
#define RWDB_SECONDS_PER_WEEK |
(RWDB_SECONDS_PER_DAY*7.0) |
#define RWDB_SECONDS_PER_RWMTH |
(RWDB_SECONDS_PER_WEEK*4.0) |
#define RWDB_SECONDS_PER_RWYR |
(RWDB_SECONDS_PER_RWMTH*12.0) |
For example, adding one month to an RWDBDuration adds four weeks' worth of seconds to the duration, regardless of the number of weeks in any particular month.
As a reminder of this interpretation, the methods for accessing years and months are named asRWMonth() and asRWYear(). Applications can apply specialized arithmetic for durations that span more than one month or year by using the arithmetic operators for doubles.
Class RWDBBlob provides memory management services and access to a binary large object (blob) with arbitrary data and length. Class RWDBBlob is derived from RWCollectable, so all features of a collectable are supported, including persistance. It does not provide any semantic interpretation of its data; it is designed as a base class that applications can specialize to provide application-specific semantics.
In the following example, we illustrate how a hypothetical structure, struct SOUND, might be transferred between a database and program variables.
#include <fstream.h> #include <rw/db/db.h> struct SOUND { char *data; size_t length; }; void playSound(const SOUND& s) { // Hypothetical function which plays the sound s. } istream& operator >> (istream& iStream, SOUND& sound) { // Hypothetical function that reads the next sound from // the stream into sound. } main() { SOUND sound; RWDBBlob aBlob; RWDBDatabase myDatabase = RWDBManager::database("odb7d.dll", "simba_driver", "myUserName", "myPassword", "\\sounddata"); // Play all the sounds stored in the table. RWDBTable soundTable = myDatabase.table("sounds"); RWDBReader aReader = soundTable.reader(); while (aReader()) { //1 aReader >> aBlob; //2 sound.length = aBlob.length(); //3 sound.data = aBlob.data(); //4 playSound(sound); } // Add all the sounds in input file soundfile.dat to // the database table sounds. ifstream soundStream("soundfile.dat"); while (soundStream) { //5 soundStream >> sound; RWDBBlob newBlob(sound.data, sound.length); //6 RWDBInserter anInserter = soundTable.inserter(); //7 anInserter << newBlob; //8 anInserter.execute(); //9 } }
The example transfers data back and forth between SOUND, sound, and the RWDBBlobs, aBlob and newBlob.
The loop at //1 uses a reader to iterate through a table holding SOUNDs. The data is transferred to aBlob from the reader on //2. The RWDBBlob member functions length() and data() are used on //3 and //4 to set the related components of sound.
The loop at //5 calls a hypothetical function readSounds(), which presumably transfers data from a stream to the SOUND. If the call is successful, the body of the loop is entered. On //6 an RWDBBlob, newBlob, is constructed with the components of sound used as arguments. On //7 an RWDBInserter is produced from the soundTable. On //8 the newBlob is shifted into the inserter. On //9 the execute() method is invoked and the new sound is added to the database table.
Class RWDBBlob can be used effectively with the bulk interface. Please see Section 7.3 for examples.
Class RWDecimalPortable represents an arbitrary precision decimal fraction as a character string. This class exists mainly to provide a technique for retrieving exact precision numeric datatypes, such as monetary values. Some databases have methods for representing numeric values exactly. If DBTools.h++ were to fetch these values into float or double precision variables, there would be a risk of inaccuracy. Native C++ floating datatypes represent numbers using base 2; however, many noninteger decimal numbers, such as 0.1 and 19.7, cannot be exactly represented in base 2. For accounting or financial computations, the roundoff error inherent in floating point arithmetic is unacceptable.
There are several reasons for providing RWDecimalPortable. First, RWDecimalPortable provides exact representation and computation with decimal numbers. Using RWDecimalPortable objects is the same as using the built-in floating types, except that since RWDecimalPortable uses base 10, there is no round-off error when working with decimal fractions. Second, RWDecimalPortable is also part of the Rogue Wave Money.h++ class library, and is the common representation used by all classes within Money.h++. This provides applications with extensibility into the high-speed Money.h++ classes, if desired.
The following example demonstrates the difference in accuracy between RWDecimalPortable and the built-in type double. In each case below, we add one cent to a zero-initialized variable one hundred times. Then we remove one dollar, which should return the variable to zero. Using RWDecimalPortable, the test succeeds; the account balances. Using double, the value does not return to zero.
void usingDecimal() { cout << "using RWDecimalPortable ... "; RWDecimalPortable penny = "0.01"; RWDecimalPortable bank = 0; for(int i = 100; i--;) bank += penny; // deposit 100 pennies bank -= 1; // withdraw a dollar cout << (bank == 0 ? "balances" : "doesn't balance") << endl; } void usingDouble() { cout << "using double ... "; double penny = 0.01; double bank = 0; for(int i = 100; i--;) bank += penny; // deposit 100 pennies bank -= 1; // withdraw a dollar cout << (bank == 0 ? "balances" : "doesn't balance") << endl; } int main() { usingDecimal(); usingDouble(); return 0; }
©Copyright 2000, Rogue Wave Software, Inc.
Contact Rogue Wave about documentation or support issues.