dtl


BoundIO and BoundIOs

Category: utilities Component type: type

Description

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.

Definition

Defined in the BoundIO.h header file.

Refinement of

None.

Associated types

NullField - a tag struct which indicates we want to set a field to null.

Example 1:

// 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;

	}

};

 

Example 2:

//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 &paramObj)

	{

	  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;

}

 

Example 3:

//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

}

};



Public base classes: BoundIO

None.

Public base classes: BoundIOs

map<string, BoundIO>

Notation:

X A type that is a model of BoundIO
a Object of type X

Expression semantics: BoundIO

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.  

Expression semantics: BoundIOs

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.  

Notes

[1] Supported C++ types for binding to fields in a database:

Supported C++ data types and mappings (see ETI_Map::build() in bind_basics.cpp for details)

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
ODBCINT64 (this is defined in sqltypes.h, under windows this tends to be defined as "_int64", under unix it tends to be defined as "long long") SQL_C_BIGINT SQL_BIGINT
double SQL_C_DOUBLE SQL_DOUBLE
float SQL_C_FLOAT SQL_FLOAT
bool SQL_C_BIT SQL_INTEGER
ODBC TIMESTAMP_STRUCT SQL_C_TIMESTAMP SQL_TIMESTAMP
jtime_c SQL_C_TIMESTAMP SQL_TIMESTAMP
TCHAR[xxx] (must be bound using DTL_CHAR_FIELD(cstr) in BCA or BPA) SQL_C_CHAR or SQL_C_WCHAR if UNICODE SQL_VARCHAR or SQL_WVARCHAR if UNICODE
tcstring<xxx> SQL_C_CHAR or SQL_C_WCHAR if UNICODE SQL_VARCHAR or SQL_WVARCHAR if UNICODE
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

In addition to these basic types, you can also add custom bindings to DTL to support your own favorite types such as complex numbers or other kinds of structures that may span multiple database fields. DTL comes with some contributed user examples such as support for fields with an optional NULL value, or the direct mapping of enumerated values to database fields. For more on this see: Support for user defined types

[2] We recommend that you only use statically allocated fields in your BCA. The reason is that some of the logic in DBView & IndexedDBView uses field offsets within the class to compare fields for sorting and assignment. If you really want to use a dynamically allocated field, it can be done if the fields are allocated as a contiguous block, but it is a bit tricky. 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.

See also

BPA, BCA, InsVal, SelVal, DBView, IndexedDBView


[DTL Home]

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.

SourceForge Logo

This site written using the ORB. [The ORB]