Step 2: Building an ADT
This step demonstrates how to build a value for an Abstract Data Type and send it to the RDBMS. (The same database objects as in the previous step is used).
Building the ADT is done as follows:
Creating the Table
First, the table ADTS2 is created. The table contains 3 fields:
-
The name of a river (RIVER)
-
The length of the river (LENGTH)
-
A list of bridges associated with the river (B)
Then an ADT value is built and recorded in the B field.
Getting the ADT Description
The ADT value is an instance of the DB Link class IldADTValue. To build such an instance, the description of the Abstract Data Type is needed.
Since you now have a table that uses the ADT, another way of getting this description is presented (different from the method used in the first step). All that is required is to run a query to select the B column from the ADTS2 table. Then, the description of the column can be accessed, which means that the description of its data type can also be accessed.
This is done in the following code excerpt:
// Retrieve the descriptor of the parameter object type.
IldADTDescriptor* bridgeLstAdt = 0 ;
const char* query = "select B from ADTS2" ;
if (!request->execute(query)) {
IldDisplayError("Could not select object column : ", request) ;
localEnd(dbms) ;
exit(1) ;
}
bridgeLstAdt = request->getColDescriptor(0)->getADTDescriptor() ;
You now have the IldADTDescriptor instance that describes the upper level object: the list of bridges. This instance is required to build the ADT value and also to bind the Abstract Data Type parameter. The query parse operation and the binding of the parameter is similar to what has been done in previous steps with basic types. The only difference is that for an ADT parameter, the IldADTDescriptor of the data type must be provided. This is done as follows:
// Parse the insert query :
const char* insertStr =
(!strncmp(dbms->getName(), "oracle", 6) ?
"insert into ADTS2 values ('River Name', 30, :1)" :
"insert into ADTS2 values ('River Name', 30, ?)") ;
cout << "Parse request : " << insertStr << endl ;
if (!request->parse(insertStr)) {
IldDisplayError("Could not parse insert query : ", request) ;
localEnd(dbms) ;
exit(1) ;
}
if (!request->bindParam((IldUShort)0, IldCollectionType, -1, 0, 0, IldFalse, 0,
bridgeLstAdt)) {
IldDisplayError("Could not bind object parameter : ", request) ;
localEnd(dbms) ;
exit(1) ;
}
Retrieving the Instances
The request is now parsed and a value given to the parameter. Since the main object (the list of bridges) contains inner objects, you also need to get the IldADTDescriptor instances that describe these nested objects. These instances are retrieved through the upper level IldADTDescriptor.
This is done in the method getSubADTDescriptor, which requires the following parameters:
-
const IldADTDescriptor* adt - The main object.
-
IldUShort idx - The index of the nested object to access.
Note
The method has two other parameters. These are used only to process the error cases. |
The inner object IldADTDescriptor is retrieved as follows:
IldADTDescriptor* subAdt = 0 ;
// Get the descriptor at the given position :
if (adt->getType() == IldADTObject) {
if (adt->getAttributesCount() > idx)
desc = adt->getAttributes()[idx] ;
}
else
desc = adt->getCollectionAttribute() ;
// Get the ADT descriptor :
subAdt = desc->getADTDescriptor() ;
Then, the getSubADTDescriptor method is used to get each ADT descriptor for each object nested within the bridge collection:
// Get the ADT Descriptor for the bridge object :
IldADTDescriptor* bridgeAdt = getSubADTDescriptor(dbms, bridgeLstAdt, 0,
"bridge") ;
// Get the ADT Descriptor for the line object :
IldADTDescriptor* lineAdt = getSubADTDescriptor(dbms, bridgeAdt, 0, "line") ;
// Get the ADT Descriptor for the point object :
IldADTDescriptor* pointAdt = getSubADTDescriptor(dbms, lineAdt, 0, "point") ;
Building the Object Values
You now have everything required to build the object values. This is done from the most nested level to the upper level as follows:
IldADTValue* pointObj1 = new IldADTValue(pointAdt) ;
IldADTValue* pointObj2 = new IldADTValue(pointAdt) ;
IldADTValue* lineObj1 = new IldADTValue(lineAdt) ;
IldADTValue* bridgeObj1 = new IldADTValue(bridgeAdt) ;
IldADTValue* bridgeLst = new IldADTValue(bridgeLstAdt) ;
pointObj1->setValue((IldInt)10, 0) ; // X for point 1.
pointObj1->setValue((IldInt)20, 1) ; // Y for point 1.
pointObj2->setValue((IldInt)10, 0) ; // X for point 2.
pointObj2->setValue((IldInt)30, 1) ; // Y for point 2.
lineObj1->setValue(pointObj1, 0) ; // First point of the line.
lineObj1->setValue(pointObj2, 1) ; // Second point of the line.
bridgeObj1->setValue(lineObj1, 0) ;
bridgeObj1->setValue("Bridge Name", 1) ;
bridgeLst->setValue(bridgeObj1, 0) ;
Executing the Query
The parameter value is then set and the query executed as done with basic data types in previous steps:
if (!request->setParamValue(bridgeLst, 0)) {
IldDisplayError("Could not set parameter value : ", request) ;
localEnd(dbms) ;
exit(1) ;
}
if (!request->execute(&rowCount, 1)) {
IldDisplayError("Could not execute the query : ", request) ;
localEnd(dbms) ;
exit(1) ;
}
else
cout << rowCount << " rows inserted." << endl ;
The same method is used to add a second row with a list that contains three elements.
You can also look at the method displayData, which retrieves from the database the objects previously recorded.
Conclusion
This step demonstrated how to build a value for an Abstract Data Type and how to record it in the database. It is much simpler to do so with DB Link than with the native RDBMS API.
See ADTCommom.cpp source code (creation of the object types).
See main source code.