Database APIs

7.4 Database APIs

As we saw earlier, even though embedded SQL has the capabilities of executing SQL statements dynamically, it still contains some static SQL statements which mandates the pre-compilation or SQL translation step.

Moreover, there needs to be a connection to the database while pre-compiling the embedded SQL application, since in order to generate access plans, statistics information from the database catalog is required.

This brings us to another world of SQL application development that uses Database Application Programming Interfaces (APIs). Database APIs are totally dynamic in nature and can be developed independently of the database software being used, and without the need for pre-compilation.

Database APIs, as the name suggests, are a set of APIs exposed by database vendors pertaining to different programming languages like C, C++, Java and so on, which gives application developers a mechanism to interact with the database from within the application by just calling these SQL callable interfaces.

The intermediate layer between the application and the database server, which makes this interaction possible, is the database connectivity driver. The database vendors themselves provide these drivers and once the driver libraries are linked with the source code libraries, the application source code can be easily compiled and then executed.

Applications can now be developed independently of target databases, without the need for database connection at the compilation phase. This also provides application developers much more flexibility without having to know embedded SQL syntax. The following sections describe in detail these database connectivity drivers.

7.4.1 ODBC and the IBM Data Server CLI driver

In order to achieve some level of standardization amongst all the database vendors who offer connectivity drivers, the X/Open Company and the SQL Access Group jointly developed a specification for a callable SQL interface referred to as the X/Open Call Level Interface. The goal of this interface was to increase the portability of applications by enabling them to become independent of any one database vendor's programming interface. Most of the X/Open Call Level Interface specifications have been accepted as part of the ISO Call Level Interface International Standard (ISO/IEC 9075-3:1995 SQL/CLI).

Microsoft developed a callable SQL interface called Open Database Connectivity (ODBC) for Microsoft operating systems, based on a preliminary draft of X/Open CLI. However, ODBC is no longer limited to Microsoft operating systems and currently many implementations are available on other platforms as well.

Database Fundamentals 174 The IBM Data Server CLI driver is the DB2 Call level Interface which is based on the

Microsoft® ODBC specifications, and the International Standard for SQL/CLI. These specifications were chosen as the basis for the DB2 Call Level Interface in an effort to follow industry standards and to provide a shorter learning curve for those application programmers already familiar with either of these database interfaces. In addition, some DB2 specific extensions have been added to help the application programmer specifically exploit DB2 features. The DB2 CLI is a C and C++ application programming interface for relational database access that uses function calls to pass dynamic SQL statements as function arguments.

Even for PREPARE and EXECUTE statements there are equivalent APIs such as SQLPrepare(), and SQLExecute() respectively, which accept the SQL statement as argument. This means that there would be no need for any static EXEC SQL statements in the application. For example, consider a dynamic SQL statement that needs to be prepared using the SQLPrepare() API. Recall that using embedded SQL, we achieved the same by using EXEC SQL PREPARE statement.

SQLCHAR *stmt = (SQLCHAR *)"UPDATE employee.details SET emp_id = ? WHERE emp_name = ? ";

/* prepare the statement */ int rc = SQLPrepare(hstmt, stmt, SQL_NTS);

Similarly, IBM CLI offers other callable interfaces like SQLConnect(), SQLFetch(), SQLExecute, and so on.

The ODBC specifications also includes an operating environment, where database specific ODBC Drivers are dynamically loaded at runtime by a driver manager based on the data source (database name) provided on the connect request. The IBM DB2 Call Level Interface driver conforms to the ODBC 3.51 standards, which means it also acts as an ODBC driver when loaded by an ODBC driver manager.

Figure 7.4 depicts where the IBM DB2 CLI driver sits in a dynamic SQL application development environment. It also depicts how the IBM DB2 CLI driver can act as any other ODBC driver, when loaded through the ODBC driver manager.

Chapter 7 – Using SQL in an application 175

Figure 7.4 - IBM DB2 CLI and ODBC

7.4.2 JDBC

JDBC stands for Java Database Connectivity. As its name suggests, it is an SQL application programming interface similar to ODBC and CLI, but for Java applications.

In a CLI application development, the dependent CLI libraries needed to be linked to the application. Similarly, in JDBC, the relevant Java packages that contain the support for the JDBC APIs need to be imported.

An example of how a SELECT statement can be issued from a JDBC application is shown in Listing 7.4.

Database Fundamentals 176 // SQL for SELECT. The name, country, street and province information

// which has been provided by the user are stored in respective variables. String sqlSel = "select “+name+”, ” +country+”, “+street+”, “+province+”,

“+zip+” from CUSTOMER where Customer = ?";

//prepare the SELECT statement PreparedStatement pstmt=con.prepareStatement(sqlSel); pstmt.setString (1, "custCountry");

//set the Input parameter pstmt.execute();

//execute SELECT statement ResultSet result = pstmt.getResultSet (); //get the results and set

//values

List<Customer> custList = new ArrayList<Customer>();

while (result.next ()) { Customer cust = new Customer(); cust.name = result.getString (1); cust.country = result.getString (2); cust.street = result.getString (3); cust.province = result.getString (4); cust.zip = result.getString (5); custList.add (cust);

} }catch (SQLException e) {e.pringStackTrace ();}

Listing 7.4 - Code snippet using JDBC

In the above code snippet:  The complete SQL statement, once formed, is first stored in a string variable (sqlSel

above).  The dynamic SQL statement is then prepared using parameter markers in place of

predicate values.  Before execution of the SQL statement, the actual values are bound to the

respective parameter markers. The JDBC statement “ pstmt.setString (1, "custCountry")”" would replace the first parameter marker in the dynamic SQL statement with the value ‘custCountry’.

 Once executed, the programmers would need to map the results returned by JDBC to respective Java objects.

7.5 pureQuery

Dynamic SQL provides flexibility to application developers, but it comes with an overhead. For each SQL statement that is executed, the statement first needs to be prepared in the application, the actual predicate values then have to be bound to their respective

Chapter 7 – Using SQL in an application 177 parameter markers and then the results returned by the JDBC driver need to be mapped to

Java application objects. pureQuery is a platform offered to Java developers that exploits the advantages of

dynamic SQL without having to bother about preparation and object mapping overheads. Consider the JDBC application snippet discussed in Listing 7.4. The same SQL SELECT

statement, when written using pureQuery, is limited to much fewer lines of code as shown below in Listing 7.5.

//The name, country, street and province are variables which would be //populated at runtime using user inputs. String sqlSel = "select “+name+”, ” +country+”, “+street+”, “+province+”,

“+zip+” from CUSTOMER where Customer = ?";

Data data = DataFactory.getData (con);

//execute the Select and get the list of customer List<Customer> customerList = data.queryList (sqlSel, Customer. class, "custCountry");

Listing 7.5 - Code snippet using pureQuery

The pureQuery API queryList will execute the sqlSel statement with the predicate value “ custCountry ” and return the query results into cutomerList. Like JDBC, the SQL statement is generated at runtime here as well, whereas the same code has been written in much less lines of code as compared to the JDBC application. It not only maximizes the application development speed, but also helps in reducing the complexity.

The above method of pureQuery is referred to as an inline method, which supports dynamic SQL execution. pureQuery also supports static execution of SQL applications using the annotated method.

With the inline method, SQL statements are created as Java string objects and passed to the pureQuery API. On the other hand, with the annotated method, the SQL string is defined as a pureQuery annotation. The method annotations defined by pureQuery are the following:

 @Select (which annotates SQL queries)  @Update (which annotates SQL DML statements)  @Call (which annotates SQL CALL statements).

Consider the SELECT statement

SELECT Name, Country, Street, Province, Zip FROM customer where Customer =?

For pureQuery, all that is required is to:  Place the SELECT SQL in the appropriate annotation.

Database Fundamentals 178  Then declare a user-defined function that is used onwards to execute the same

SQL. For example, in Listing 7.6, the SELECT SQL statement is placed in the @Select

annotation. public interface CustomerData

{ //Select PDQ_SC.CUSTOMER by parameters and populate Customer bean with //results @Select(sql="select Name, Country, Street, Province,Zip from CUSTOMER

where Customer =?") Customer getCustomer(int cid); }

Listing 7.6 - @Select annotation

Now to execute this SQL statement, the application does not need to implement the above CustomerData interface. pureQuery implements these user defined interfaces by using a built-in utility called pureQuery generator, which creates the data access layer for the application.

The application can directly create an instance variable of CustomerData using the pureQuery API and call the getCustomer()method directly to execute the above SQL statement, as shown in Listing 7.7.

// use the DataFactory to instantiate the user defined interface CustomerData cd = DataFactory.getData(CustomerData.class, con); // execute the SQL for getCustomer() and get the results in Customer beans Iterator<Customer> cust = cd.getCustomer();

Listing 7.7 - Executing the SQL statement using pureQuery and the annotation method

The output of the generator utility is an implemented Java file ( CustomerDataImpl.java in the above example ) of the user-defined interface (CustomerData). This implemented Java file has the actual SQL statements and the definition of declared methods ( getCustomer ).

In this programming style, an application developer specifies all the SQL statements and their corresponding methods within the interfaces. These methods are then used to execute the SQL statements in the application. In this way, SQL statements are separated from the business logic in the application code.

The pureQuery annotated method programming style supports the static mode of execution of an application, whereas the inline method supports dynamic execution. As per user requirements, either one of these pureQuery programming styles can be used to develop a Java database application.

For more details on pureQuery coding techniques, refer to the following links:

Chapter 7 – Using SQL in an application 179 http://publib.boulder.ibm.com/infocenter/idm/v2r2/index.jsp?topic=/com.ibm.datatools.javat

ool.runtime.overview.doc/topics/helpindex_pq_sdf.html http://publib.boulder.ibm.com/infocenter/idm/v2r2/index.jsp?topic=/com.ibm.datatools.javat

ool.runtime.overview.doc/topics/helpindex_pq_sdf.html

7.5.1 IBM pureQuery Client Optimizer

A very interesting feature supported by pureQuery, is the pureQuery Client Optimizer. In sections above, we discussed the performance overhead of dynamic SQL applications. The pureQuery Client Optimizer offers a technique to reduce this overhead. Existing JDBC dynamic application can be optimized to run certain SQL statements statically, without making any change to the application code. Using the pureQuery Client Optimizer, the following steps need to be undertaken:

 When the dynamic application is first run, the pureQuery Client Optimizer captures the different SQL statements issued from an application into a pureQueryXml capture file.

 The captured SQL statements in the XML file are then divided into packages, by running the command line tool Configure.

 Using the staticBinder, these packages are then created on the database server and the application is bound to these packages.

 Finally, the execution mode of the application needs to be set to ‘static’, which allows some of the SQL statements to run in static mode.

7. Basically, each SQL issued from the application is matched with the SQL statements captured in the capture file. As soon as a match is found, the corresponding package details are fetched from the capture file and since the SQL is already bound to the corresponding package at the database server, the statement can be run statically. Each new SQL, which does not find a match, stills runs in dynamic execution mode.