Module 3 Part 2
Module 3 Part 2
There are, however, two complications to bear in mind. First, the data typesrecognized by SQL
may not be recognized by the host language and vice versa.This mismatch is typically addressed by
casting data values appropriately beforepassing them to or frorn SQL commands.The second
complication hasto do with SQL being set-oriented,and is addressed using cursors.
6.1.2 Cursors
A major problem in embedding SQL statements in a host language like C is that an impedance
mismatch occurs because SQL operates on set of records, whereas languages like C do not cleanly
support a set-of-records abstraction. The solution is to essentially provide a mechanism that allows us to
retrieve rows one at a time from a relation.This mechanism is called a cursor.
We can declare a cursor on any relation or on any SQL query (because every query returns a set
of rows). Once a cursor is declared, we can open it (which positions the cursor just before the first row);
fetch the next row; move the cursor (to the next row, to the row after the next n, to the first row, or to the
previous row, etc., by specifying additional parameters for the FETCH command); or close the cursor.
Thus, a cursor essentially allows us to retrieve the rows in a table by positioning the cursor at a particular
row and reading its contents.
A cursor can be thought of as 'pointing' to a row in the collection of answers to the query
associated with it. When a cursor is opened, it is positioned just before the first row. We can use the
FETCH command to read the first row of cursor sinfo into host language variables:
FETCH sinfoINTO :c_sname, :c_age;
When the FETCH statement is executed, the cursor is positioned to point at the next row (which
is the first row in the table when FETCH is executed for the first time after opening the cursor) and the
column values in the row are copied into the corresponding host variables. By repeatedly executing this
FETCH statement (say, in a while-loop in the C program), we can read all the rows computed by the
query, one row at a time. Additional parameters to the FETCH command allow us to position a cursor in
very flexible ways.
How do we know when we have looked at all the rows associated with the cursor? By looking at
the special variables SQLCODE or SQLSTATE, of course. SQLSTATE, for example, is set to the value
02000, which denotes NO DATA, to indicate that there are no more rows ifthe FETCH statement
positions the cursor after the last row.
When we are done with a cursor, we can close it:
CLOSE sinfo;
It can be opened again if neededand the value of :c_minrating in the SQL query associated with
the cursor would be the value of the host variable c_minrating at that time.
DELETE commands allow us to update or delete the row on which the cursor is positioned.If the keyword
SCROLL is specified, the cursor is scrollable, which means that variants of the FETCH command can be
used to position the cursor in very flexible ways;If the keyword INSENSITIVE is specified, the cursor
behaves as if it is ranging over a private copy of the collection of answer rows.A holdable cursor is
specified using the WITH HOLD clause, and is not closed when the transaction is conunitted.Finally, in
what order do FETCH commands retrieve rows? In general this order is unspecified, but the optional
ORDER BY clause can be used to specify a sort order. Note that columns mentioned in the ORDER BY
clause cannot be updated through the cursor!
The first statement declares the C variable c_sqlstring and initializes its value to the string representation
of an SQL command. The second statement results in this string being parsed and compiled as an SQL
command, with the resulting executable bound to the SQL variable readytogo. (Since readytogo is an
SQL variable, just like a cursor name, it is not prefixed by a colon.) The third statement executes the
command.
6.2.1JDBC Architecture
The architecture of JDBC has four main components: the application, the driver manager,
several data source specific drivers, and the corresponding data Sources.
The application initiates and terminates the connection with a data source. It sets transaction
boundaries, submits SQL statements, and retrieves the results-----all through a well-defined interface as
specified by the JDBC API.
The primary goal of the driver manager is to load JDBC drivers and pass JDBC function calls
from the application to the correct driver.
The driver establishes the connection with the data source.
The data source processes commands from the driver and returns the results.
Driversin JDBC are classified into four types depending on the architectural relationship between the
application and the data source:
JDBC is a collection of Java classes and interfaces that enables database access from programs
written in the Java language. It contains methods for connecting to a remote data source, executing SQL
statements, examining sets of results from SQL statements, transaction management, and exception
handling. The classes and interfaces are part of the java.sql package. Thus, all code fragments in the
remainder of this section should include the statement import java. sql .* at the beginning of the code;
6.3.2 Connections
A session with a data source is started through creation of a Connection object; A connection identifies a
logical session with a data source; multiple connections within the same Java program can refer to
different data sources or the same data source. Connections are specified through a JDBC URL, a URL
that uses the jdbc protocol. Such a URL has the form
jdbc:<subprotocol>:<otherParameters>
The code example shown in Figure 6.2 establishes a connection to an Oracle database assuming that the
strings userld and password are set to valid values.
Establishing a connection to a data source is a costly operation since it involves several steps, such as
establishing a network connection to the data source, authentication, and allocation of resources such as
memory. In case an application establishes many different connections from different parties (such as a
Web server), connections are often pooled to avoid this overhead. A connection pool is a set of
established connections to a data source. Whenever a new connection is needed, one of the connections
from the pool is used, instead of creating a new connection to the data source.
The executeUpdate method returns an integer indicating the number of rows the SQL statement
modified; it returns 0 for successful execution without modifying any rows.
The executeQuery method is used if the SQL statement returns data, such &"l in a regular
SELECT query.JDBC has its own cursor mechanism in the form of a ResultSet object, which we discuss
next. The execute method is more general than executeQuery and executeUpdate.
6.3.4 ResultSets
The statement executeQuery returns a, ResultSet object, which is similar to a cursor. ResultSet
cursors in JDBC 2.0 are very powerful; they allow forward and reverse scrolling and in-place editing and
insertions.
In its most basic form, the ResultSet object allows us to read one row of the output of the query at
a time. Initially, the ResultSet is positioned before the first row, and we have to retrieve the first row with
an explicit call to the next0 method. The next method returns false if there are no more rows in the query
answer, and true other\vise. The code fragment shown in Figure 6.4 illustrates the basic usage of a
ResultSet object.
An SQL Warning is a subclass of SQLException. Warnings are not severe as errors and the program can
usually proceed without special handling of warnings. Warnings are not thrown like other exceptions, and
We will conclude our discussion of JDBC with an example code fragment that examines all database
metadata shown in Figure 6.7.
6.4 SQLJ
SQLJ (short for 'SQL-Java') was developed by the SQLJ Group, a group of database vendors and
Sun. SQLJ was developed to complement the dynamic way of creating queries in JDBC with a static
model. It is therefore very close to Embedded SQL. Unlike JDBC, having semi-static SQL queries allows
the compiler to perform SQL syntax checks, strong type checks of the compatibility of the host variables
with the respective SQL attributes, and consistency of the query with the database schema-tables,
attributes, views, and stored procedures--all at compilation time. For example, in both SQLJ and
Embedded SQL, variables in the host language always are bound statically to the same arguments,
whereas in JDBC, we need separate statements to bind each variable to an argument and to retrieve the
Comparing the JDBC and SQLJ code, we see that the SQLJ code is much easier to read than the JDBC
code. Thus, SQLJ reduces software development and maintenance costs.
Let us consider the individual components of the SQLJ code in more detail. All SQLJ statements
have the special prefix #sql. In SQLJ, we retrieve the results of SQL queries with iterator objects, which
are basically cursors. An iterator is an instance of an iterator class. Usage of an iterator in SQLJ goes
through five steps:
There are two types of iterator classes: named iterators and positional iterators. For named iterators, we
specify both the variable type and the name of each column of the iterator. This allows us to retrieve
individual columns by name as in our previous example where we could retrieve the title colunm from the
Books table using the expression books.titIe(). For positional iterators, we need to specifY only the
variable type for each column of the iterator. To access the individual columns of the iterator, we use a
FETCH ... INTO construct, similar to Embedded SQL. Both iterator types have the same performance;
which iterator to use depends on the programmer's taste.
Let us look at an example of a stored procedure with arguments. The stored procedure shown in Figure
6.9 has two arguments: book_isbn and addedQty. It updates the available number of copies of a book with
the quantity from a new shipment.
Stored procedures do not have to be written in SQL; they can be written in any host language. As an
example, the stored procedure shown in Figure 6.10 is a Java function that is dynamically executed by the
database server whenever it is called by the client:
6.5.3 SQL/PSM
All major database systems provide ways for users to write stored procedures in a simple, general
purpose language closely aligned with SQL. In this section, we briefly discuss the SQL/PSM standard,
which is representative of most vendorspecific languages. In PSM, we define modules, which are
collections of stored procedures, temporary relations, and other declarations.
Each parameter is a triple consisting of the mode (IN, OUT, or INOUT as discussed in the previous
section), the parameter name, and the SQL datatype of the parameter.
We start out with an example of a SQL/PSM function that illustrates the main SQL/PSM
constructs. The function takes as input a customer identified by her cid and a year. The function returns
the rating of the customer, which is defined a...'3 follows: Customers who have bought more than ten
books during the year are rated 'two'; customer who have purchased between 5 and 10 books are rated
'one', otherwise the customer is rated 'zero'. The following SQL/PSM code computes the rating for a given
customer and year.
Let us use this example to give a short overview of some SQL/PSM constructs:
DBDudes writes other JDBC code and stored procedures for all of the remaining tasks. They use code
similar to some of the fragments that we have seen in this chapter.
DBDudcs takes care to make the application robust by processing exceptions and warnings, as
shown in Figure 6.6.
DBDudes also decide to write a trigger, which is shown in Figure 6.12. Whenever a new order is
entered into the Orders table, it is inserted with ship~date set to NULL. The trigger processes each row in
the order and calls the stored procedure 'UpdateShipDate'. This stored procedure (whose code is not
shown here) updates the (anticipated) ship_date of the new order to 'tomorrow', in case qtyjlLstock of the
corresponding book in the Books table is greater than zero. Otherwise, the stored procedme sets the
ship_date to two weeks.