Database Technology
Unit-III
EMBEDDED SQL
Integrating a database application with a nice graphical user interface.
The use of SQL commands within a host language program is called embedded SQL.
There are two complications to bear in mind.
First, the data types recognized by SQL may not be recognized by the host language,
and vice versa. This mismatch is typically addressed by casting data values
appropriately before passing them to or from SQL commands. (SQL, like C and other
programming languages, provides an operator to cast values of one type into values of
another type.)
The second complication has to do with the fact that SQL is set-oriented; commands
operate on and produce tables, which are sets (or multisets) of rows. Programming
languages do not typically have a data type that corresponds to sets or multisets of
rows. Thus, although SQL commands deal with tables, the interface to the host
language is constrained to be one row at a time.
Declaring Variables and Exceptions
SQL statements can refer to variables defined in the host program. Such host-language variables
must be prefixed by a colon (:) in SQL statements and must be declared between the commands
EXEC SQL BEGIN DECLARE SECTION and EXEC SQL END DECLARE SECTION. The
declarations are similar to how they would look in a C program and, as usual in C, are separated
by semicolons.
EXEC SQL BEGIN DECLARE SECTION
char c sname[20];
long c sid;
short c rating;
float c age;
EXEC SQL END DECLARE SECTION
Embedding SQL statements
All the SQL statements that are embedded within a host program must be clearly marked with
the details dependent on the host language; in C, SQL statements must be pre-fixed by EXEC
SQL. An SQL statement can essentially appear in any place in the host language program where
a host language statement can appear.
Example
CURSORS
A major problem in embedding SQL statements in a host language like C is that an impedance
mismatch occurs because SQL operates on sets 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.
Basic Cursor Definition and Usage
Cursors enable us to examine in the host language program a collection of rows computed
by an embedded SQL statement:
We usually need to open a cursor if the embedded statement is a SELECT (i.e., a
query). However, we can avoid opening a cursor if the answer contains a single
row, as we will see shortly.
INSERT, DELETE, and UPDATE statements typically don't require a cursor, although
some variants of DELETE and UPDATE do use a cursor.
As an example, we can find the name and age of a sailor, specified by assigning a value
to the host variable c sid, declared earlier, as follows:
EXEC SQL SELECT S.sname, S.age INTO :c sname, :c age FROM Sailors S WHERE S.sid = :c
sid;
The INTO clause allows us to assign the columns of the single answer row to the host variables c
sname and c age. Thus, we do not need a cursor to embed this query in a host language program.
But what about the following query, which computes the names and ages of all sailors with a
rating greater than the current value of the host variable c minrating?
SELECT S.sname, S.age FROM Sailors S WHERE S.rating > :c minrating
This query returns a collection of rows, not just one row. When executed interactively, the
answers are printed on the screen. If we embed this query in a C program by prefixing the
command with EXEC SQL, how can the answers be bound to host language variables? The
INTO clause is not adequate because we must deal with several rows.
The solution is to use a cursor:
DECLARE sinfo CURSOR FOR
SELECT S.sname, S.age
FROM Sailors S
WHERE S.rating > :c minrating;
This code can be included in a C program, and once it is executed, the cursor sinfo is defined.
Subsequently, we can open the cursor:
OPEN sinfo;
The value of c minrating in the SQL query associated with the cursor is the value of this variable
when we open the cursor.
FETCH sinfo INTO :c sname, :c age;
CLOSE sinfo;
Properties of Cursors
The general form of a cursor declaration is:
DECLARE cursorname [INSENSITIVE] [SCROLL] CURSOR FOR
some query
[ ORDER BY order-item-list ]
[ FOR READ ONLY j FOR UPDATE ]
A cursor can be declared to be a read-only cursor (FOR READ ONLY) or, if it is a cursor on a
base relation or an updatable view, to be an updatable cursor (FOR UPDATE). If it is
updatable, simple variants of the UPDATE and DELETE commands allow us to update or delete
the row on which the cursor is positioned. For example, if sinfo is an updatable cursor and is
open, we can execute the following statement:
UPDATE Sailors S
SET S.rating = S.rating - 1
WHERE CURRENT of sinfo;
This embedded SQL statement modi_es the rating value of the row currently pointed to by
cursor sinfo; similarly, we can delete this row by executing the next statement:
DELETE Sailors S
WHERE CURRENT of sinfo;
A cursor is updatable by default unless it is a scrollable or insensitive cursor (see below), in
which case it is read-only by default.
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; otherwise, only the
basic FETCH command, which retrieves the next row, is allowed.
If the keyword INSENSITIVE is specified, the cursor behaves as if it is ranging over a private
copy of the collection of answer rows. Otherwise, and by default, other actions of some
transaction could modify these rows, creating unpredictable behavior. For example, while we are
fetching rows using the sinfo cursor, we might modify rating values in Sailor rows by
concurrently executing the command:
UPDATE Sailors S
SET S.rating = S.rating - 1
Consider a Sailor row such that: (1) it has not yet been fetched, and (2) its original rating value
would have met the condition in the WHERE clause of the query associated with sinfo, but the
new rating value does not. Do we fetch such a Sailor row? If INSENSITIVE is specified, the
behavior is as if all answers were computed and stored when sinfo was opened; thus, the update
command has no effect on the rows fetched by sinfo if it is executed after sinfo is opened. If
INSENSITIVE is not specified, the behavior is implementation dependent in this situation.
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 order-item-list is a list of order-items; an order-item is a column name, optionally
followed by one of the keywords ASC or DESC. Every column mentioned in the ORDER BY
clause must also appear in the select-list of the query associated with the cursor; otherwise it is
not clear what columns we should sort on. The keywords ASC or DESC that follow a column
control whether the result should be sorted with respect to that column in ascending or
descending order; the default is ASC. This clause is applied as the last step in evaluating the
query.
ORDER BY minage ASC, rating DESC
The answer is sorted _rst in ascending order by minage, and if several rows have the same
minage value.
DYNAMIC SQL
Consider an application such as a spreadsheet or a graphical front-end that needs to access data
from a DBMS. Such an application must accept commands from a user and, based on what the
user needs, generate appropriate SQL statements to retrieve the necessary data. In such
situations, we may not be able to predict in advance just what SQL statements need to be
executed, even though there is (presumably) some algorithm by which the application can
construct the necessary SQL statements once a user's command is issued.
SQL provides some facilities to deal with such situations; these are referred to as dynamic SQL.
There are two main commands, PREPARE and EXECUTE, which we illustrate through a simple
example:
char c sqlstring[] = {“DELETE FROM Sailors WHERE rating>5"};
EXEC SQL PREPARE readytogo FROM :c sqlstring;
EXEC SQL EXECUTE readytogo;
The first statement declares the C variable c sqlstring and initializes its value to the string
representation of an SQL command.
Introduction to JDBC
JDBC stands for Java Database Connectivity. JDBC is a Java API to connect and execute
the query with the database. It is a part of JavaSE (Java Standard Edition). JDBC API
uses JDBC drivers to connect with the database.
All direct interaction with a specific DBMS happens through a DBMS specific driver. A
driver is a software program that translates the ODBC or JDBC calls into DBMS-specific
calls.
Existing drivers are registered with a driver manager, which manages the set of existing
drivers.
data storage subsystem with which a driver interacts as a data source.
An application that interacts with a data source through ODBC or JDBC performs the
following steps.
A data source is selected, the corresponding driver is dynamically loaded, and a
connection with the data source is established. There is no limit on the number of
open connections and an application can have several open connections to
different data sources. Each connection has transaction semantics; that is, changes
from one connection are only visible to other connections after the connection has
committed its changes. While a connection is open, transactions are executed by
submitting SQL statements, retrieving results, processing errors and finally
committing or rolling back. The application disconnects from the data source to
terminate the interaction.
Architecture
The architecture of ODBC/JDBC has four main components: the application, the driver
manager, several data source specific drivers, and the corresponding data sources. Each
component has different roles.
JDBC Driver Management
JDBC Driver is a software component that enables java application to interact with
the database. There are 4 types of JDBC drivers:
1. Type I (bridges) This type of driver translates JDBC function calls into function calls of
another API that is not native to the DBMS. An example is an ODBCJDBC bridge. In this case
the application loads only one driver, namely the bridge.
2. Type II (direct translation to the native API) This driver translates JDBC function calls
directly into method invocations of the API of one specific data source. The driver is
dynamically linked, and is specific to the data source.
3. Type III (network bridges) The driver talks over a network to a middle-ware server that
translates the JDBC requests into DBMS-specific method invocations. In this case, the driver on
the client site (i.e., the network bridge) is not DBMS specific.
4. Type IV (direct translation over sockets) Instead of calling the DBMS API directly, the
driver communicates with the DBMS through Java sockets. In this case the driver on the client
side is DBMS-specific.
JDBC Classes and Interfaces
The java.sql package contains classes and interfaces for JDBC API. A list of
popular interfaces of JDBC API are given below:
o Driver interface
o Connection interface
o Statement interface
o PreparedStatement interface
o CallableStatement interface
o ResultSet interface
o ResultSetMetaData interface
o DatabaseMetaData interface
o RowSet interface
A list of popular classes of JDBC API are given below:
o DriverManager class
o Blob class
o Clob class
o Types class
Connections
After you've installed the appropriate driver, it is time to establish a database
connection using JDBC.
The programming involved to establish a JDBC connection is fairly simple. Here are
these simple four steps −
Import JDBC Packages − Add import statements to your Java program to
import required classes in your Java code.
Register JDBC Driver − This step causes the JVM to load the desired driver
implementation into memory so it can fulfill your JDBC requests.
Database URL Formulation − This is to create a properly formatted address
that points to the database to which you wish to connect.
Create Connection Object − Finally, code a call to
the DriverManager object's getConnection( ) method to establish actual
database connection.
Steps for Connectivity Between Java Program and Database
1. Import the Packages
2. Load the drivers using the forName() method
3. Register the drivers using DriverManager
4. Establish a connection using the Connection class object
5. Create a statement
6. Execute the query
7. Close the connections
Let us discuss these steps in brief before implementing by writing suitable code to
illustrate connectivity steps for JDBC/
Step 1: Import the Packages
Step 2: Loading the drivers
In order to begin with, you first need to load the driver or register it before using it in the
program. Registration is to be done once in your program. You can register a driver in
one of two ways mentioned below as follows:
2-A Class.forName()
Here we load the driver’s class file into memory at the runtime. No need of using new or
create objects. The following example uses Class.forName() to load the Oracle driver as
shown below as follows:
Class.forName(“oracle.jdbc.driver.OracleDriver”);
2-B DriverManager.registerDriver()
DriverManager is a Java inbuilt class with a static member register. Here we call the
constructor of the driver class at compile time. The following example uses
DriverManager.registerDriver()to register the Oracle driver as shown below:
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver())
Step 3: Establish a connection using the Connection class object
After loading the driver, establish connections as shown below as follows:
Connection con = DriverManager.getConnection(url,user,password)
user: Username from which your SQL command prompt can be accessed.
password: password from which the SQL command prompt can be accessed.
con: It is a reference to the Connection interface.
Url: Uniform Resource Locator which is created as shown below:
String url = “ jdbc:oracle:thin:@localhost:1521:xe”
Where oracle is the database used, thin is the driver used, @localhost is the IP Address
where a database is stored, 1521 is the port number and xe is the service provider. All 3
parameters above are of String type and are to be declared by the programmer before
calling the function. Use of this can be referred to form the final code.
Step 4: Create a statement
Once a connection is established you can interact with the database. The JDBCStatement,
CallableStatement, and PreparedStatement interfaces define the methods that enable you
to send SQL commands and receive data from your database.
Use of JDBC Statement is as follows:
Statement st = con.createStatement();
Note: Here, con is a reference to Connection interface used in previous step .
Step 5: Execute the query
Now comes the most important part i.e executing the query. The query here is an SQL
Query. Now we know we can have multiple types of queries. Some of them are as
follows:
The query for updating/inserting a table in a database.
The query for retrieving data.
The executeQuery() method of the Statement interface is used to execute queries of
retrieving values from the database. This method returns the object of ResultSet that can
be used to get all the records of a table.
The executeUpdate(sql query) method of the Statement interface is used to execute
queries of updating/inserting.
Pseudo Code:
int m = st.executeUpdate(sql);
if (m==1)
System.out.println("inserted successfully : "+sql);
else
System.out.println("insertion failed");
Step 6: Closing the connections
So finally we have sent the data to the specified location and now we are on the verge of
completing our task. By closing the connection, objects of Statement and ResultSet will
be closed automatically. The close() method of the Connection interface is used to close
the connection. It is shown below as follows:
con.close();
SQLJ
SQLJ is a working title for efforts to combine Java and SQL.
SQLJ is an industry standard for embedding SQL statements in Java programs. SQLJ provides a
way to integrate SQL with Java similar to how embedded SQL combines SQL with high−level
languages.SQLJ acts as a simpler and easier−to−use alternative to JDBC (Java Database
Connectivity). Developers may use the power of SQL for database operations while gaining
access to Java's object−oriented characteristics by writing SQL statements directly within Java
code. Developers must utilize an SQLJ translator to use SQLJ, which transforms SQL statements
into Java code that can be run using the JDBC interface. SQLJ also requires importing specific
classes, such as java.sql, for database connectivity.
Example
This example will show how the SQLJ query is written to insert a value into the database.
#sql [ctx] { INSERT INTO employee (ID, NAME, AGE, SALARY) VALUES
(:id, :name, :age, :salary) };
Advantages of SQLJ
Type Safety: SQLJ provides enhanced type safety compared to traditional JDBC. The SQLJ
translator performs static type checking, ensuring that SQL statements are syntactically correct
and compatible with the underlying database schema. This reduces the chances of runtime errors
caused by incorrect SQL statements
Simplified Syntax: SQLJ simplifies the process of embedding SQL statements in Java code. The
#sql syntax allows developers to write SQL statements directly within the Java code, making it
easier to understand and maintain the code. The SQLJ translator converts these SQL statements
into Java code that can be executed through JDBC.
Integration with Java Objects: SQLJ seamlessly integrates with Java objects, allowing
developers to work with database data using Java classes and objects. This integration enables
developers to leverage Java's object−oriented features for data manipulation and processing.
Enhanced Performance: SQLJ can provide performance improvements compared to traditional
JDBC in certain scenarios. The static type checking performed by the SQLJ translator allows for
better optimization and query execution plans. Additionally, SQLJ simplifies the process of
binding Java objects to SQL data, reducing the overhead of data conversion.