dtl
Category: utilities | Component type: type |
A BoundIO is an object used to hold an association between a field in a query and a field in a user defined data object. The BoundIO class is held in a private member of the DBView template called the BoundIOs object which is an STL map<string, BoundIO> that holds a set of BoundIO objects that are mapped by SQL field name. As an end user, the four places you will encouter a BoundIO are when writing BCA , BPA, InsVal or SelVal functions.
Defined in the BoundIO.h header file.
None.
NullField - a tag struct which indicates we want to set a field to null.
// Functor to bind SQL columns to a data object
class Example
{
public: // tablename.columnname:
int exampleInt; // DB_EXAMPLE.INT_VALUE
string exampleStr; // DB_EXAMPLE.STRING_VALUE
double exampleDouble; // DB_EXAMPLE.DOUBLE_VALUE
long exampleLong; // DB_EXAMPLE.EXAMPLE_LONG
TIMESTAMP_STRUCT exampleDate; // DB_EXAMPLE.EXAMPLE_DATE
};
class BCAExampleObj { public: void operator()(BoundIOs &boundIOs, Example &rowbuf) { boundIOs["INT_VALUE"] >> rowbuf.exampleInt; boundIOs["STRING_VALUE"] >> rowbuf.exampleStr; boundIOs["DOUBLE_VALUE"] >> rowbuf.exampleDouble; boundIOs["EXAMPLE_LONG"] >> rowbuf.exampleLong; boundIOs["EXAMPLE_DATE"] >> rowbuf.exampleDate; } };
//BPA Functor to bind SQL parameters to a data object
// "Example" class to hold rows from our database table
class Example
{
public: // tablename.columnname:
int exampleInt; // DB_EXAMPLE.INT_VALUE
string exampleStr; // DB_EXAMPLE.STRING_VALUE
double exampleDouble; // DB_EXAMPLE.DOUBLE_VALUE
long exampleLong; // DB_EXAMPLE.EXAMPLE_LONG
TIMESTAMP_STRUCT exampleDate; // DB_EXAMPLE.EXAMPLE_DATE
Example(int exInt, const string &exStr, double exDouble, long exLong,
const TIMESTAMP_STRUCT &exDate) :
exampleInt(exInt), exampleStr(exStr), exampleDouble(exDouble), exampleLong(exLong),
exampleDate(exDate)
{ }
};
// Create an association between table columns and fields in our object
class BCAExampleObj
{
public:
void operator()(BoundIOs &cols, Example &rowbuf)
{
cols["INT_VALUE"] >> rowbuf.exampleInt;
cols["STRING_VALUE"] >> rowbuf.exampleStr;
cols["DOUBLE_VALUE"] >> rowbuf.exampleDouble;
cols["EXAMPLE_LONG"] >> rowbuf.exampleLong;
cols["EXAMPLE_DATE"] >> rowbuf.exampleDate;
}
}
class ExampleParamObj
{
public:
int lowIntValue;
int highIntValue;
string strValue;
TIMESTAMP_STRUCT dateValue;
};
class BPAParamObj
{
public:
void operator()(BoundIOs &boundIOs, ExampleParamObj ¶mObj)
{
boundIOs[0] << paramObj.lowIntValue;
boundIOs[1] << paramObj.highIntValue;
boundIOs[2] << paramObj.strValue;
boundIOs[3] << paramObj.dateValue;
}
};
// read some Example objects from the database and return a vector of
// the results, use BPA to set join parameters
vector<Example> ReadData()
{
vector<Example> results;
// construct view
DBView<Example, ExampleParamObj>
view("DB_EXAMPLE", BCAExampleObj(),
"WHERE INT_VALUE BETWEEN (?) AND (?) AND "
"STRING_VALUE = (?) OR EXAMPLE_DATE < (?) ORDER BY EXAMPLE_LONG",
BPAParamObj());
// loop through query results and add them to our vector
// in this loop, read_it.GetLastCount() records read from DB
DBView<Example, ExampleParamObj>::select_iterator read_it = view.begin();
// set parameter values for the WHERE clause in our SQL query
read_it.Params().lowIntValue = 2;
read_it.Params().highIntValue = 8;
read_it.Params().strValue = "Example";
TIMESTAMP_STRUCT paramDate = {2000, 1, 1, 0, 0, 0, 0};
read_it.Params().dateValue = paramDate;
for ( ; read_it != view.end(); read_it++)
{
cout << "Reading element #" << read_it.GetLastCount() << endl;
results.push_back(*read_it);
cout << "read_it->exampleInt = " << read_it->exampleInt << endl;
cout << "read_it->exampleStr = " << read_it->exampleStr << endl;
}
return results;
}
//Default SelVal function to make sure fields in a row selected from the database are valid
// Default select validation behavior ... data is valid if and only if
// there are no columns which are null.
// If there are other checks you wish to make, put them in
// your own SelVal functor.
// You can also specialize this template if you wish to have different default behavior
// for your data class.
template<class DataObj> class DefaultSelValidate {
public:
bool operator()(BoundIOs &boundIOs, DataObj &rowbuf)
{
for (BoundIOs::iterator b_it = boundIOs.begin();
b_it != boundIOs.end(); b_it++)
{
BoundIO &boundIO = (*b_it).second;
if (boundIO.IsColumn() && boundIO.IsNull())
return false; // found null column ... data is invalid
}
return true; // no nulls found ... data is OK
}
};
None.
map<string, BoundIO>
X | A type that is a model of BoundIO |
a | Object of type X |
Name | Expression | Precondition | Semantics | Postcondition |
---|---|---|---|---|
Default constructor |
X a() |
Construct an empty BoundIO | ||
Copy constructor |
X a(constX &b) |
Copy construct a BoundIO. | ||
Assignment operator |
X& operator=(const X&b) |
Assignment copy | ||
Binding operator for INPUT/OUTPUT | template<class T> BoundIO operator==(T &memberToBind) | INPUT/OUTPUT parameter type. Create an association between SQL column names / parameter numbers and object field names. This is done by inserting elements into the BoundIOs map. The syntax for inserting elements into the BoundIOs map is to call BoundIOs["SQL FIELD NAME" / SQL Parameter Number] == RowObject.FieldName. This invokes the == operator for the BoundIO class. We use the == syntax here as a mnemonic to remind end users that this is a two way association. Once the association is created, values can either be read from the database to the field, or written from the field to the database. (For a list of supported C++ types see [1]). Internally, this syntax does two things. First, the operator==() analyzes the RowObject.FieldName parameter to determine what type of field we are binding to and the memory address of the given field name (there are some restrictions! see [2]). Based on this information, operator==(), determines the SQL_TYPE, the SQL_C_TYPE, the data size, and the address to pass to SQLBindCol() or SQLBindParam(). All of this information gets stored in what is known as a BoundIO object. Finally, the resulting BoundIO object is inserted into the BoundIOs map using the BoundIOs::operator[]() described below. | ||
Binding operator to bind an input parameter | template<class T> BoundIO operator << (T &memberToBind) | INPUT parameter type. The class member supplies data strictly as an input parameter to a SQL query. (See BPA for details.). | ||
Binding operator to bind an output parameter | template<class T> BoundIO operator >> (T &memberToBind) | OUTPUT parameter type. The class member supplies data strictly as an output parameter to a SQL query. (See BPA for details.). | ||
Column Indicator |
bool IsColumn() |
Returns true if this BoundIO represents a SQL field. | ||
Parameter Indicator |
bool IsParam() |
Returns true if this BoundIO represents a SQL parameter. | ||
NULL data Indicator |
bool IsNull() |
Returns true if value held in this field by the current rowbuf object holds NULL data. Typically this is used by the SelVal function. | ||
Set to null | void SetNull() | Sets the field to represent a null value. | ||
Make non-null | void ClearNull() | Sets the field to represent a non-null value. | ||
Set the SQL type | void SetSQLType(SDWORD newSqlType) | Override the SQL type used by SQLBindParam when binding this column. By default the SQL type that is used when binding this column as a parameter is as per the ETIMap::build() settings in bind_basics.cpp. This method can be used to override the default SQL binding for the given data type. The available list of SQL types are as per the list of SQL data types for ODBC. This value will be used as the ParameterType argument when a call is made to SQLBindParam if this column is used as a parameter. | ||
Set the SQL C type | void SetCType(SDWORD newCType) | Override the SQL C type used by ODBC when binding this column. By default the SQL C type that is used when binding this column is as per the ETIMap::build() settings in bind_basics.cpp. This method can be used to override the default SQL C binding for the given data type. The available list of SQL C types are as per the list of SQL C data types for ODBC. This value will be used as the ValueType argument when a call is made to either SQLBindParam or SQLBindCol. As an example, the default SQL C type for a char data field is SQL_C_CHAR, but you might want to use SQL_C_TINYINT to hold an integer in a char field instead. Be careful! We do not check the value you set here! If you specify a C data type that contains more bytes then the size of the field ODBC will overflow that field in memory. Similarly, if you specify a C datatype that is too small for the field your field may be only partially initialized. |
As per STL map but with the following modifications:
Name | Expression | Precondition | Semantics | Postcondition |
---|---|---|---|---|
Parameter operator | BoundIO &operator[](unsigned int paramNum) |
This is used to insert/find a BoundIO in the BoundIOs map using a stringified version of the number as the key. If this operator does not find an existing BoundIO with the given key, it creates a new BoundIO object that is marked as a SQL parameter with the name given by the key string. This new BoundIO object is then inserted into the BoundIOs list. SQL parameters are represented in the SQL string passed to a DBView by a '(?)'. | ||
Column operator | BoundIO &operator[](const string &colName) |
This is used to insert/find a BoundIO in the BoundIOs map using a the given string as the key. If this operator does not find an existing BoundIO with the given key, it creates a new BoundIO object that is marked as a SQL field with the name given by the key string. This new BoundIO object is then inserted into the BoundIOs list. |
[1] Supported C++ types for binding to fields in a database:
C++ type | ODBC C Data Type | ODBC SQL Data Type |
short | SQL_C_SSHORT | SQL_INTEGER |
unsigned short | SQL_C_USHORT | SQL_INTEGER |
int | SQL_C_SLONG | SQL_INTEGER |
unsigned int | SQL_C_ULONG | SQL_INTEGER |
long | SQL_C_SLONG | SQL_INTEGER |
unsigned long | SQL_C_ULONG | SQL_INTEGER |
double | SQL_C_DOUBLE | SQL_DOUBLE |
float | SQL_C_FLOAT | SQL_FLOAT |
char | SQL_C_CHAR | SQL_CHAR |
bool | SQL_C_BIT | SQL_INTEGER |
ODBC TIMESTAMP_STRUCT | SQL_C_TIMESTAMP | SQL_TIMESTAMP |
jtime_c | SQL_C_TIMESTAMP | SQL_TIMESTAMP |
char [xxx] | SQL_C_CHAR | SQL_VARCHAR |
wchar [xxx] | SQL_C_WCHAR | SQL_WVARCHAR |
string <--> basic_string<char> | SQL_C_CHAR | SQL_VARCHAR |
wstring<--> basic_string<wchar_t> | SQL_C_WCHAR | SQL_WVARCHAR |
binary data or blob <--> basic_string<unsigned char> | SQL_C_BINARY | SQL_VARBINARY |
[2] If you are only using this BCA/BPA with a DBView template then the field you are binding to can be either statically or dynamically allocated by the DataObj, as long as the object has a correct copy and assigment operator for copying the field. If you want to use an IndexedDBView with your BCA, then the field must be a statically allocated member of the DataObj. The reason for this is that IndexedDBView computes a relative fieldoffset via &(rowbuf.Field) - &rowbuf that it uses for comparison functions for automatic indexing and update routines in the IndexedDBView. This kind of offset logic is only valid if the field is not dynamically allocated via operator new() or malloc(). So, if you want to use IndexedDBView we recommend that your fields be statically allocated members. (In fact, there is a complicated way around this restriction if you allocate your fields as a contiguous block. You can check out DynamicRowBCA in DynaDBView.h and the data_ptr() function that we define for the variant_row class in variant_row.h for an example of how we worked around this to accomodate dynamically sized rows. The result is not for the faint of heart.) Here is an example of what we are talking about:
#define DYNAMIC_SIZE 100 class BCADynamicObj; class Dynamic { char **szDynamicString; public: Dynamic() { szDynamicString = (char **)malloc(DYNAMIC_SIZE); } Dynamic(const Dynamic &other) { szDynamicString = (char **)malloc(DYNAMIC_SIZE); memcpy(szDynamicString, other.szDynamicString, DYNAMIC_SIZE); } const Dynamic & operator=(const Dynamic &other) { if (this != &other) { if (szDynamicString == NULL) szDynamicString = (char **)malloc(DYNAMIC_SIZE); memcpy(szDynamicString, other.szDynamicString, DYNAMIC_SIZE); } } ~Dynamic() { if (szDynamicString != NULL) free(szDynamicString); } friend class BCADynamicObj; }; // This BCA will work with DBView but not IndexedDBView because szDynamicString is dynamic class BCADynamicObj { public: void operator()(BoundIOs &boundIOs, Dynamic &rowbuf) { boundIOs["STRING_VALUE"] == *(rowbuf.szDynamicString); } };
BPA, BCA, InsVal, SelVal, DBView, IndexedDBView
Copyright © 2002, Michael Gradman and Corwin Joy.
Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appears in all copies and that both that copyright notice and this permission notice appear in supporting documentation. Corwin Joy and Michael Gradman make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.