Tải bản đầy đủ (.pdf) (38 trang)

Java Data Access—JDBC, JNDI, and JAXP phần 3 ppt

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (185.32 KB, 38 trang )

JDBC SQL escape syntax
Most database languages provide you with a “programming” language so you can interact with the database in
ways you cannot achieve just using standard DML or DDL SQL statements. The languages also generally
provide you with internal helper functions that you can use to format character and numeric data as well as
mathematical functions that help you perform useful calculations.
However, all databases have unique syntaxes for their programming languages. For instance, Oracle uses
PL/SQL and Microsoft SQL Server uses Transact−SQL. Because of the uniqueness of each database’s
programming language, JDBC provides you with access to the functions and their special features using the
JDBC SQL escape syntax. When you specify a command using the syntax, the driver translates the command
into the necessary database−specific format.
The escape syntax gives you the flexibility to use database specific features unavailable to you by using
standard JDBC methods and properties. However, use escape clauses with caution. Overusing them can make
your code database−dependent because you are using specific functions unique to your database.
The general SQL escape syntax format is as follows:
{keyword parameters}
Table 5−3 lists and describes the escape keywords.
Table 5−3: SQL Escape Keywords
Keyword Description Example
d, t, ts Helps identify date, time,
and timestamp literals. As
you know, no two DBMSs
represent time and date the
same way. This escape
syntax tells the driver to
render the date or time in
the target database’s format.
{d ‘yyyy−mm−dd’}
where yyyy = year, mm = month; dd = date (for example, {d
‘2002−08−03’} is March 8, 2002
{t ‘hh:mm:ss’}
where hh = hour; mm = minute; ss = second (for example, {t


‘13:30:29’} is 1:30:29 PM)
{ts ‘d t.f ’}
where d = date format; t=time format; f = optional fractional
second
fn Represents scalar functions
used in a DBMS.
{fn length(‘Hello World’)} returns 11, the length of the
character string ‘Hello World’.
escape Identifies the escape
character used in LIKE
clauses. Useful when using
the SQL wildcard %, which
matches zero or more
characters.
String sql = "SELECT symbol FROM MathSymbols
WHERE symbol LIKE ‘\%’ {escape ‘\’}";
stmt.execute(sql);
call Use for stored procedures.
Chapter 5: Building JDBC Statements
66
For a stored procedure requiring an IN parameter, use {call
my_procedure(?)}
For a stored procedure requiring an IN parameter and
returning an OUT parameter use {? = call
my_procedure(?)};
oj Use to signify outer joins.
The syntax is as follows:
{oj outer− join} where
outer− join = table {LEFT|
RIGHT|FULL} OUTER

JOIN {table | outer−join}
on search−condition.
String sql = "SELECT emp from {oj ThisTable RIGHT
OUTER JOIN ThatTable on empid = ‘111111111’}";
stmt.execute(sql);
Using the execute() method
The execute() method provides the most flexible way to interact with a database because it enables you to
process any type of SQL statement. For example, you may issue statements that return update counts or result
sets. The executeUpdate() and executeQuery() methods can only return update counts or result sets,
respectively. The execute() method can return both.
However, the execute() method’s flexibility bears a price tag. Because you may know nothing about the
statement type passed to the database, you may also know nothing about the result the database will return.
You might receive one or more ResultSet objects, one or more update counts, or one or more of both.
Figure 5−3 is a flow chart that demonstrates how to interpret and process this command’s return value.
To begin, the execute() method always returns a boolean. If it returns true, a ResultSet object was returned. At
this point you call the Statement object’s getResultSet() method to obtain the ResultSet object populated with
data satisfying the SQL query. Once you finish processing that result set, call the Statement object’s
getMoreResults() method to determine if another result set exists. If the method returns true, call the
getResultSet() and process that result set. Continue this loop until the getMoreResults() method returns false.
Now you must check for update counts using the getUpdateCount() method. A value of >=0 indicates that an
update count exists. As I mentioned earlier, a 0 denotes an SQL DDL and anything else represents the update
count of the number of rows affected by an INSERT, DELETE, or UPDATE statement or stored procedure.
Continue processing update counts until the getUpdateCount() method returns −1. At this point you have
processed all the results from the execute() method.
As you can see, the execute() method can be fairly complex to implement if you do not know what type of
SQL statement you are processing. Fortunately, in the real world you usually know whether to expect a result
set or an update count.
Listing 5−2 provides an example of processing the execute() method’s return value. In the application, I
submit an INSERT statement to demonstrate the case in which an update count is returned, and a SELECT
statement to illustrate the case in which a result set is returned. After I execute each statement I call the

method processExecute() to determine the return value and display the appropriate message.
Chapter 5: Building JDBC Statements
67
Figure 5−3: This flow chart shows how to process the results from the execute() method.
Listing 5−2: ExecuteMethod.java
package Chapter5;
import java.sql.*;
public class ExecuteMethod {
public static void main(String[] args) {
//Declare Connection, Statement, and ResultSet variables
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
//Holds the execute method’s result
boolean executeResult;
//Begin standard error handling
try{
//Register driver
String driver = "oracle.jdbc.driver.OracleDriver";
Class.forName(driver).newInstance();
//Open database connection
System.out.println("Connecting to database ");
Chapter 5: Building JDBC Statements
68
String jdbcUrl = "jdbc:oracle:thin:@localhost:1521:ORCL";
conn = DriverManager.getConnection(jdbcUrl,"toddt","mypwd");
//Create a Statement object
stmt = conn.createStatement();
//Insert data and process result
String sql="INSERT INTO Employees VALUES" +

"(868653391,’Greg’,’4351’,{d ‘1996−12−31’},500)";
executeResult = stmt.execute(sql);
processExecute(stmt,executeResult);
//List employees
sql = "SELECT * FROM Employees ORDER BY hiredate";
executeResult = stmt.execute(sql);
processExecute(stmt,executeResult);
//Standard error handling.
} catch(SQLException se) {
//Handle errors for JDBC
se.printStackTrace();
} catch(Exception e) {
//Handle errors for Class.forName
e.printStackTrace();
} finally {
try {
if(conn!=null)
conn.close();
} catch(SQLException se) {
se.printStackTrace();
}//end finally try
}//end try
System.out.println("Goodbye!");
}//end main
//Method to process the execute() statement
public static void processExecute(Statement stmt,
boolean executeResult) throws SQLException {
//check executeResult to see what was returned
if(!executeResult) {
System.out.println("Update count returned ");

int updateCount = stmt.getUpdateCount();
System.out.println(updateCount + " row was " +
"inserted into Employee table.");
} else {
//ResultSet returned
ResultSet rs = stmt.getResultSet();
System.out.println("SQL query issued ");
//Table header information
System.out.println("Listing rows for Employee table.");
System.out.println("SSN" + "\t\t" + "Name" + "\t" + "Salary"
+ "\t" + "Hiredate" + "\t" + "Loc_id");
Chapter 5: Building JDBC Statements
69
//Loop through ResultSet showing all Employees
while(rs.next()){
System.out.println(rs.getInt("SSN") + "\t"
+ rs.getString("Name")+ "\t"
+ rs.getDouble("Salary") + "\t"
+ rs.getDate("Hiredate") + "\t"
+ rs.getInt("Loc_id"));
}//end while−loop
}//end if(!executeResult)
}//end processExecute()
}//end ExecuteMethod Class
The output from Listing 5−2 is as follows:
Connecting to database
Update count returned
1 row was inserted into Employee table.
SQL query issued
Listing rows for Employee table.

SSN Name Salary Hiredate Loc_id
111111111 Todd 5000.00 1995−09−16 100
987654321 John 4351.00 1996−12−31 500
868653391 Greg 4351.00 1996−12−31 500
123456789 Jimmy 3080.00 1997−09−07 400
312654987 Lori 2000.95 1999−01−11 300
419876541 Larry 1500.00 2001−03−05 200
Goodbye!
JDBC batch processing
The statement interface family supports batch processing that enables you to submit multiple DML statements
with one call to the database. This can help you minimize the number of database calls you make and
implement transactional control over your database.
For example, suppose you have an application that uses INSERT and UPDATE statements to refresh the data
in a data warehouse using a text file as a source. Most data warehouse refresh files are large and you will
likely process a large number of database calls that perform nearly identical tasks. With every call you are
issuing either an INSERT statement to add data or an UPDATE statement to update existing data.
To minimize the number of calls, you can send a batch of statements with one call and execute them together.
You can also inform the database to undo all the changes in the batch if one statement fails. This transactional
approach will ensure data integrity and consistency by preventing “orphan” data from being written to the
database.
JDBC 2.0 and beyond supports batch processing of INSERT and UPDATE statements, which may be useful
in the scenarios I describe here. However, JDBC drivers are not required to support this feature. You should
use the DatabaseMetaData. supportsBatchUpdates() method to determine if the target database supports batch
update processing. The method returns true if your JDBC driver supports this feature.
Chapter 5: Building JDBC Statements
70
XRef Chapter 8, “Mining Database Metadata with JDBC,” covers how to get and use database
information with JDBC Metadata interfaces.
To take advantage of batch processing with a Statement object you must use the setAutoCommit(),
addBatch(), and executeBatch() methods. The setAutoCommit() method controls when your database makes

your changes permanent. I cover commits more thoroughly in the next section. With each call to the
addBatch() method you add an INSERT or UPDATE SQL statement to a list for execution. When you’re
finished adding all the statements, call the executeBatch() method to submit the batch to the database for
execution.
The executeBatch() method returns an int[] containing the individual update counts for each SQL statement in
the order in which you added them to the batch. If an error occurs while executing the batch, processing stops
and a BatchUpdateError exception occurs. At this point the number of elements in the int[] equals the number
of successful statements executed within the batch.
To help visualize how batch updates work, Figure 5−4 shows a flow chart that illustrates the process. Notice
that auto−commit is set to false, and pay attention to the flow of the addBatch() and executeBatch() methods
and the explicit commit() call.
Figure 5−4: This flow chart shows JDBC batch processing.
The following code snippet provides an example of a batch update:
//Create a Statement object and add SQLstatements with the
Chapter 5: Building JDBC Statements
71
//addBatch() method. Assume a valid connection.
Statement stmt = conn.createStatement();
//Set auto−commit to false
conn.setAutoCommit(false);
String SQL = "INSERT INTO Employees (Id, Name) VALUES(9517,’Jane’)";
stmt.addBatch(SQL);
SQL = "INSERT INTO Employees (Id, Name) VALUES(9518,’Betty’)";
stmt.addBatch(SQL);
//Create an int[] to hold returned values
int[] count = stmt.executeBatch();
//Explicitly commit statements to apply changes
conn.commit();
In this example I use batch updating to add additional entries to my Employees table. Notice that the first
thing I do is set auto−commit to false with a call to setAutoCommit(). Next I add two SQL INSERT

statements to the batch. Then I call the executeBatch() method to execute the SQL statements. Finally, I call
commit() to ensure that the changes are applied.
Note Turning off auto−commit may yield some performance increases because the number of commits is
reduced. However, remember that any DML statement may force the database to lock the row, page, or
even the table until you issue a commit. You may find this locking behavior undesirable, as it may
prohibit other users from accessing information.
As a final comment, just as you can add statements to a batch for processing, you can remove them with the
clearBatch() method. This method removes all the statements you added with the addBatch() method.
However, you cannot selectively choose which statement to remove.
JDBC transactions
Transactions enable you to control if, and when, changes are applied to the database. It treats a single SQL
statement or a group of SQL statements as one logical unit, and if any statement fails, the whole transaction
fails.
For example, Figure 5−5 illustrates a banking transaction that transfers funds from a checking account to an
investment account. If the investment−account credit operation fails, you need to undo the debit to the
checking account. This is a simple example, but it illustrates the point. Transactions are a science unto
themselves and beyond the scope of this book.
Chapter 5: Building JDBC Statements
72
Figure 5−5: This flow chart illustrates a banking transaction.
Working with transactions has both pros and cons. For example, with transactions you can maintain both data
consistency and integrity. While you make changes to a row, the DBMS prevents others from simultaneously
changing the same row. This guarantees that when you execute your commit() method you actually change
the data you expect to change, not data that was changed by someone else between the time you began the
transaction and the time you issued the commit.
Caution Do not count on transaction control or batch update support for DDL statements. Most databases will
not roll back these SQL statements once you submit them.
For the same reasons that transactions provide benefits, they can also cause problems. To prevent data from
being manipulated while a transaction takes place, the database locks the data being updated. Some systems
use row−level locks, which prevent others from changing the row you are currently working with. Others use

page−level locks that prevent others from changing data located near yours. Some systems even lock entire
tables. For obvious reasons this is undesirable.
JDBC enables you to manage transactions by manipulating the Connection object’s auto−commit mode and
using its rollback() method, which undoes all changes, up to the last commit.
JDBC 3.0 Enhancement
Chapter 5: Building JDBC Statements
73
The new JDBC 3.0 Savepoint interface gives you additional transactional control. Most modern DBMS
support savepoints within their environments such as Oracle’s PL/SQL.
When you set a savepoint you define a logical rollback point within a transaction. If an error occurs past a
savepoint, you can use the rollback method to undo either all the changes or only the changes made after the
savepoint.
The Connection object has two new methods that help you manage savepoints:
setSavepoint(String savepointName) defines a new savepoint. It also returns a Savepoint object.•
releaseSavepoint(Savepoint savepointName) "deletes" a savepoint. Notice that it requires a Savepoint
object as a parameter. This object is usually a savepoint generated by the setSavepoint() method.

The following example illustrates the use of a Savepoint object:
try{
//Assume a valid connection object conn
conn.setAutocommit(false);
Statement conn.createStatement();
String SQL = "INSERT INTO Emp(Id, Name) VALUES (10, ‘Ted’)";
stmt.executeInsert(SQL);
//set a Savepoint
Savepoint savepoint = conn.setSavepoint("Savepoint1");
//Submit a malformed SQL statement that breaks
String SQL = "TRESNI OTNI Emp(Id, Name) VALUES (10, ‘Ted’)";
stmt.executeInsert(SQL);
}catch(SQLException se){

conn.rollback(svpt1);
}
XRef Refer to Chapter 15, “Understanding Distributed Transactions” for more information on
transactions.
Listing 5−3 demonstrates transaction management using the Connection object’s auto−commit mode and
rollback() method. In this example, I add a new employee and their field office location information. If the
employee INSERT statement fails, which it does in my example, then the location data are removed with the
rollback() method.
Listing 5−3: Rollback.java
package Chapter5;
import java.sql.*;
public class Rollback {
public static void main(String[] args) {
Chapter 5: Building JDBC Statements
74
//Declare Connection and Statement objects
Connection conn = null;
Statement stmt = null;
//Holds the execute method’s result
boolean executeResult;
//Begin standard error handling
try {
//Register driver.
String driver = "oracle.jdbc.driver.OracleDriver";
Class.forName(driver).newInstance();
//Open database connection.
System.out.println("Connecting to database ");
String jdbcUrl = "jdbc:oracle:thin:@localhost:1521:ORCL";
conn = DriverManager.getConnection(jdbcUrl,"toddt","mypwd");
//Create a Statement object.

stmt = conn.createStatement();
//Set Autocommit = false and verify.
conn.setAutoCommit(false);
if (!conn.getAutoCommit())
System.out.println("Auto−commit is set to false");
//Insert location data.
String sql = "INSERT INTO Location VALUES(715,’Houston’)";
stmt.executeUpdate(sql);
//This statement will fail for invalid date.
sql = "INSERT INTO Employees VALUES" +
"(888653321,’Kirk’,’4351’,{d ‘1996−02−31’},715)";
stmt.executeUpdate(sql);
//Commit data to database.
conn.commit();
//Standard error handling.
} catch(SQLException se) {
//Handle errors for JDBC
String msg = se.getMessage();
msg = "SQLException occured with message: " + msg;
System.out.println(msg);
//Rollback transaction
System.out.println("Starting rollback operations ");
try {
conn.rollback();
} catch(SQLException se2){
se2.printStackTrace();
}
System.out.println("Rollback successfull!");
} catch(Exception e) {
//Handle errors for Class.forName

e.printStackTrace();
} finally {
try {
Chapter 5: Building JDBC Statements
75
if(conn!=null)
conn.close();
} catch(SQLException se) {
se.printStackTrace();
}//end finally try
}//end try
System.out.println("Goodbye!");
}//end main
}//end Rollback class
Output from Listing 5−3:
Connecting to database
Auto−commit is set to false
SQLException occured with message: ORA−01839:
date not valid for month specified
Starting rollback operations
Rollback successfull!
Goodbye!
Closing the Statement object
Just as you close a Connection object to save database resources, you should also close the Statement object,
for the same reason. A simple call to the close() method will do the job. If you close the Connection object
first it will close the Statement object as well. However, you should always explicitly close the Statement
object to ensure proper cleanup.
Working with PreparedStatement Objects
As you know, the PreparedStatement interface extends the Statement interface. Its added functionality also
gives it a couple of advantages over a generic Statement object.

First, it gives you the flexibility of supplying arguments dynamically. Although you can use the Statement
object to build and execute your SQL statements on the fly, the PreparedStatement object reduces your work.
All you do is assign the values you want to use to the appropriate parameter placeholders.
Caution Not all DMBSs support the concept of a PreparedStatement. In those that don’t, the
database flushes the compiled SQL statement from memory after it is executed. Refer to
your database or JDBC documentation for details.
Second, when you create a PreparedStatement object JDBC "prepares" the SQL statement for execution by
sending it to the database, which then parses, compiles, and builds a query execution plan. This parsed
statement lives in memory and remains ready to use during your database session or until you close the
PreparedStatement object.
Chapter 5: Building JDBC Statements
76
Tip PreparedStatement objects can improve performance of frequently used SQL statements. The
database pre−processes the SQL statements, which saves time when you reuse the statement.
Creating the PreparedStatement object
Just as a Connection object creates the Statement object, it also creates a PreparedStatement object. The
following code snippet shows how to employ its prepareStatement() method to instantiate a
PreparedStatement object:
//Assume conn is a valid Connection object
String SQL = "Update employees SET salary = ? WHERE ename = ?";
PreparedStatement prepStmt = conn.prepareStatement(SQL);
What Are JDBC Parameters?
All parameters in JDBC are represented by the ? symbol, which is known as the parameter marker. You must
supply values for every parameter before executing the SQL statement. The setXXX() methods bind values to
the parameters. If you forget to supply the values, you will receive an SQLException.
Each parameter marker is referred to by its ordinal position. The first marker represents position 1, the next
position 2, and so forth. This method differs from that of Java array indices, which start at 0.
Three types of parameters exist: IN, OUT, and INOUT. The PreparedStatement object only uses the IN
parameter. The CallableStatement object, which works with database stored procedures, can use all three.
Here are the definitions of each:

IN — A parameter whose value is unknown when the SQL statement is created. You bind values to
IN parameters with the setXXX() methods.

OUT — A parameter whose value is supplied by the SQL statement it returns. You retrieve values
from theOUT parameters with the getXXX() methods.

INOUT — A parameter that provides both input and output values. You bind variables with the
setXXX() methods and retrieve values with the getXXX() methods.

The SQL String you supply when calling the method can represent an DELETE, UPDATE, SELECT,
INSERT, or DDL SQL statement. Notice too that a ? represents the unknown values that you supply at
runtime.
Using the PreparedStatement object
All of the Statement object’s methods for interacting with the database — execute(), executeQuery(),
executeUpdate(), and executeBatch() — work with the PreparedStatement object. However, the methods are
modified to use SQL statements that can take input the parameters. When using the PreparedStatement object
you must bind values to all parameters otherwise a SQLException occurs.
To bind values to parameters you use the setXXX() methods. (XXX represents the Java data type of the value
you wish to bind to the input parameter.) JDBC uses the setXXX methods to convert the Java data type to the
appropriate SQL data type for your target database, as shown in the following code snippet:
Chapter 5: Building JDBC Statements
77
//Assume conn is a valid Connection object
String SQL = "UPDATE employees SET salary = ? WHERE ename = ?";
PreparedStatement pstmt = conn.prepareStatement(SQL);
//bind variables
pstmt.setInt(1,"100000");
pstmt.setString(2,"toddt");
pstmt.executeUpdate();
Note The parameter values are not reset after you execute the prepared statement. You can

overwrite them with another setXXX() method call or use the clearParameters() method.
Sometimes you may not know the data type of a value supplied at runtime. The PreparedStatement object’s
setObject() method handles this situation by taking any Java object and converting it into the appropriate
JDBC data type. This method is extremely useful when you’re working with SQL3 data types.
Listing 5−4 provides an example of how to use the PreparedStatement object. In this example I am simply
adding a record to the Employees table. First I create the PreparedStatement object with parameter
placeholders for SSN, Name, Salary, Hiredate, and Loc_Id. Next I bind these values to the corresponding
parameter with the appropriate setXXX() method. Finally, I call the executeUpdate() method to insert the row
into the table. This example only uses the executeUpdate() method, but the execute() and executeQuery()
methods work in a similar fashion.
Listing 5−4: PrepStmt.java
package Chapter5;
import java.sql.*;
public class PrepStmt{
public static void main(String[] args) {
//Declare Connection object
Connection conn = null;
//Declare PreparedStatement object
PreparedStatement pstmt = null;
//Begin standard error handling
try {
//Register driver.
String driver = "oracle.jdbc.driver.OracleDriver";
Class.forName(driver).newInstance();
//Open database connection.
System.out.println("Connecting to database ");
String jdbcUrl = "jdbc:oracle:thin:@localhost:1521:ORCL";
conn = DriverManager.getConnection(jdbcUrl,"toddt","mypwd");
//Create PreparedStatement object
String SQL = "INSERT INTO Employees VALUES (?,?,?,?,?)";

pstmt = conn.prepareStatement(SQL);
//Bind values into the parameters.
Chapter 5: Building JDBC Statements
78
int randomSsn = ((int)Math.floor(Math.random() * 899999999));
randomSsn = randomSsn + 100000000;
pstmt.setInt(1,randomSsn);
pstmt.setString(2,"Andy");
pstmt.setDouble(3,1400.51);
pstmt.setDate(4,Date.valueOf("2002−06−11"));
pstmt.setInt(5,400);
//Check to ensure that the INSERT worked properly
int updateCount = pstmt.executeUpdate();
if(updateCount==1)
System.out.println("Record inserted into " +
" \"Employees\" table.");
//Standard error handling
} catch(SQLException se) {
//Handle errors for JDBC
se.printStackTrace();
} catch(Exception e) {
//Handle errors for Class.forName
e.printStackTrace();
} finally {
try {
if(conn!=null)
conn.close();
} catch(SQLException se) {
se.printStackTrace();
}//end finally try

}//end try
System.out.println("Goodbye!");
}//end main
}// end PrepStmt class
Output from Listing 5−4:
Connecting to database
Record inserted into "Employees" table.
Goodbye!
Streaming data with PreparedStatement objects
A PreparedStatement object has a feature that the Statement object does not: the ability to use input and
output streams to supply parameter data. This enables you to place entire files into database columns that can
hold large values, such as CLOB and BLOB data types. Streaming this data to the database saves you from
having to assign it to a variable or an object in your application. This technique reduces the memory footprint
of your program by not having to store the data in memory.
The following list explains the methods you use to stream data:
setAsciiStream() is used to supply large ASCII values.•
setCharacterStream() is used to supply large UNICODE values.•
setBinaryStream() is used to supply large binary values.•
Chapter 5: Building JDBC Statements
79
The setXXXStream() method requires an extra parameter, the file size, besides the parameter placeholder. This
parameter informs the driver how much data should be sent to the database using the stream. Listing 5−5
provides an example storing and retrieving an XML file in a database.
Listing 5−5: StreamingXML.java
package Chapter5;
import java.sql.*;
import java.io.*;
import java.util.*;
public class StreamingXML {
public static void main(String[] args) {

//Declare Connection, Statement, PreparedStatement and ResultSet
//variables
Connection conn = null;
PreparedStatement pstmt = null;
Statement stmt = null;
ResultSet rset = null;
//Begin standard error handling
try {
//Register driver.
String driver = "oracle.jdbc.driver.OracleDriver";
Class.forName(driver).newInstance();
//Open database connection.
System.out.println("Connecting to database ");
String jdbcUrl = "jdbc:oracle:thin:@localhost:1521:ORCL";
conn = DriverManager.getConnection(jdbcUrl,"toddt","mypwd");
//Create a Statement object and build table
stmt = conn.createStatement();
createXMLTable(stmt);
//Open a FileInputStream
File f = new File("employee.xml");
long fileLength = f.length();
FileInputStream fis = new FileInputStream(f);
//Create PreparedStatement and stream data
String SQL = "INSERT INTO XML_Data VALUES (?,?)";
pstmt = conn.prepareStatement(SQL);
pstmt.setInt(1,100);
pstmt.setAsciiStream(2,fis,(int)fileLength);
pstmt.execute();
//Close input stream
fis.close();

// Do a query to get the row
SQL = "SELECT Data FROM XML_Data WHERE id=100";
rset = stmt.executeQuery (SQL);
// Get the first row
if (rset.next ()){
Chapter 5: Building JDBC Statements
80
//Retrieve data from input stream
InputStream xmlInputStream = rset.getAsciiStream (1);
int c;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while (( c = xmlInputStream.read ()) != −1)
bos.write(c);
//Print results
System.out.println(bos.toString());
}
//Standard error handling.
} catch(SQLException se) {
//Handle errors for JDBC
se.printStackTrace();
} catch(Exception e) {
//Handle errors for Class.forName
e.printStackTrace();
} finally {
try {
if(conn!=null)
conn.close();
} catch(SQLException se) {
se.printStackTrace();
}//end finally try

}//end try
System.out.println("Goodbye!");
}//end main
public static void createXMLTable(Statement stmt)
throws SQLException{
//Create SQL Statement
String streamingDataSql = "CREATE TABLE XML_Data
(id INTEGER, Data LONG)";
//Drop table first.
try{
stmt.executeUpdate("DROP TABLE XML_Data");
}catch(SQLException se){
//Handle Oracle DROP table error
if(se.getErrorCode()==942)
System.out.println("Error dropping XML_Data table:"
+ se.getMessage() );
}//end try
//Build table.
stmt.executeUpdate(streamingDataSql);
}//end createStreamingXMLTable
}//end StreamingXML class
The output from Listing 5−5 is as follows:
Chapter 5: Building JDBC Statements
81
Connecting to database
<?xml version="1.0"?>
<Employee>
<SSN>123963741</SSN>
<name>Art</name>
<Salary>56321.87</Salary>

<Hiredate>08−08−1988</Hiredate>
<Loc_Id>200</Loc_id>
<Employee>
Notice that to use streaming data requires binding an input stream to a parameter. Any class derived from the
InputStream interface will work. In this instance I read a file from a disk, but you can just as easily use a
network socket as a data source.
Batch updates with PreparedStatement objects
As I mentioned earlier, PreparedStatement objects support the Statement object’s executeBatch() method. The
only difference between the two is that you add "parameter sets" to the batch once you supply the SQL
statement.
The following code snippet demonstrates how to use the executeBatch() method with a PreparedStatement
object:
//Assume conn is a valid Connection object
String SQL = "UPDATE employees SET salary = ? WHERE ename = ?";
PreparedStatement prepStmt = conn.prepareStatement(SQL);
//Set the variables
int sal = 150000;
prepStat.setInt(1,sal);
String name = "toddt";
prepStmt.setString(2,name);
//add it to the batch
prepStmt.addBatch();
//add more batches
.
.
.
prepStmt.addBatch();
//Now send the batch to the database
prepStmt.executeBatch();
All of the guidelines regarding batch updates that apply to the Statement object apply to the

PreparedStatement object, particularly the auto−commit property. Remember, if you want every statement
permanently applied to the database when it is executed, leave auto−commit on its default value of true. When
you need transactional control, set auto−commit to false and explicitly use the commit() method to apply your
changes.
Chapter 5: Building JDBC Statements
82
Working with CallableStatement Objects
CallableStatement objects enable you to execute stored procedures located on the database from your Java
application. If you look back at Figure 5−1 you can see that the CallableStatement interface extends the
PreparedStatement interface. One extra feature is that the CallableStatement object not only handles IN
parameters, but also has additional support for handling OUT and INOUT parameters. The CallableStatement
object can use all three to adequately represent a stored procedure’s behavior.
Creating the CallableStatement object
Just as a Connection object creates the Statement and PreparedStatement objects, it also creates the
CallableStatement object.
Before you can create the object you need to know about the stored procedure you want to access. Suppose,
for example, that you need to execute the following Oracle stored procedure:
CREATE OR REPLACE PROCEDURE getEmpName
(Emp_SSN IN NUMBER, Emp_Name OUT VARCHAR) AS
BEGIN
SELECT name
INTO Emp_Name
FROM Employees
WHERE SSN = EMP_SSN;
END;
The following code snippet shows how to employ the Connection.prepareCall() method to instantiate a
CallableStatement object based on the preceding stored procedure:
//Assume conn is a valid Connection object
String SQL = "{call getEmpName (?,?)}";
CallableStatement cstmt = conn.prepareCall (SQL);

The String variable SQL represents the stored procedure, with parameter placeholders, using JDBC’s SQL
escape syntax. The escape syntax tells the driver, which is database−specific, to convert the call into the
correct format. As you can see, you must know the stored procedure’s name and signature.
JDBC 3.0 JDBC 3.0 enables you to use named OUT parameters in the registerOutParameter() method. Prior
versions only enabled you to refer to OUT parameters by their ordinal position. Enabling you to
specify the name of the parameter makes the method function like the getXXX() methods in terms
of parameter identification.
Table 5−4 shows the valid formats for the escape syntaxes you can use, depending on whether you need IN or
OUT parameters.
Table 5−4: PrepareCall() Parameter Formats
Format IN Parameter OUT Parameter
Chapter 5: Building JDBC Statements
83
{call stored_procedure_name} No No
{? = call stored_procedure_name} No Yes
{call stored_procedure_name
(?, ?, ,?)}
Yes No
{? = call stored_procedure_name
(?, ?, ,?)}
Yes Yes
As you can see, the first prepareCall() in Table 5−4 calls only the stored procedure and accepts no parameters.
You would use this format to call a stored procedure that performs some internal operation within the
database and does not supply feedback — for example, if you are purging historical data from a data
warehouse.
The next format returns an OUT parameter at the completion of the stored procedure. The value might
represent a method’s success or failure flag, or a value calculated within the stored procedure.
The third format enables you to supply IN parameters. You would likely use this format to call a stored
procedure to update tables with the values you supplied.
The last format enables you to supply both IN and OUT parameters. Here you supply values as IN parameters,

perform calculations or query a table, then get the result as an OUT parameter.
Using the CallableStatement object
Using CallableStatement objects is much like using PreparedStatement objects. You must bind values to all
parameters before executing the statement, or you will receive an SQLException.
If you have IN parameters, just follow the same rules and techniques that apply to a PreparedStatement object;
use the setXXX() method that corresponds to the Java data type you are binding.
When you use OUT and INOUT parameters you must employ an additional CallableStatement method,
registerOutParameter(). The following sections describe each type of parameter and how to use each with the
method.
OUT parameters
The registerOutParameter() method binds the JDBC data type to the data type the stored procedure is expected
to return. This method is different from the setXXX() method that associates a Java data type with an IN
parameter. OUT parameters require the JDBC type, which maps to SQL data types, for database
compatibility.
Once you call your stored procedure, you retrieve the value from the OUT parameter with the appropriate
getXXX() method. This method casts the retrieved value of SQL type to a Java data type.
Listing 5−6 shows you how to access the getEmpName stored procedure I presented at the beginning of this
section. Notice that it uses both IN and OUT parameters. First I bind the SSN to parameter 1 with the setInt()
method. Then I use the registerOutParameter() method to set the JDBC data type for the OUT parameter.
Finally, I use the execute() method to execute the stored procedure and use the getString() method to retrieve
the data.
Listing 5−6: CallableStmts.java
Chapter 5: Building JDBC Statements
84
package Chapter5;
import java.sql.*;
public class CallableStmt {
public static void main(String[] args) {
//Create Connection, Statement, and ResultSet objects
Connection conn = null;

CallableStatement cstmt = null;
//Begin standard error handling
try {
//Register driver.
String driver = "oracle.jdbc.driver.OracleDriver";
Class.forName(driver).newInstance();
//Open database connection.
System.out.println("Connecting to database ");
String jdbcUrl = "jdbc:oracle:thin:@localhost:1521:ORCL";
conn = DriverManager.getConnection(jdbcUrl,"toddt","mypwd");
//Create CallableStatement object
cstmt = conn.prepareCall ("{call getEmpName (?,?)}");
//Bind IN parameter first, then bind OUT parameter
int ssn = 111111111;
cstmt.setInt(1,111111111);
cstmt.registerOutParameter(2,java.sql.Types.VARCHAR);
//Use execute method to run stored procedure.
cstmt.execute();
//Retrieve employee name with getXXX method
String empName = cstmt.getString(2);
System.out.println("Employee with SSN:" + ssn
+ " is " + empName);
//Standard error handling
} catch(SQLException se) {
//Handle errors for JDBC
se.printStackTrace();
} catch(Exception e) {
//Handle errors for Class.forName
e.printStackTrace();
} finally {

try {
if(conn!=null)
conn.close();
} catch(SQLException se) {
se.printStackTrace();
}//end finally try
}//end try
System.out.println("Goodbye!");
}//end main
Chapter 5: Building JDBC Statements
85
}//end CallableStmt
The output from Listing 5−6 is as follows:
Connecting to database
Employee with SSN:111111111 is Todd
Goodbye!
INOUT parameters
An INOUT parameter plays the role of both an IN and an OUT parameter. Using an INOUT parameter is
relatively simple. First, use the setXXX() method to bind a value to the parameter. This will cause the
parameter to act as an IN parameter. Next, use the registerOutParameter() method to register the same
parameter as OUT.
Consider the following Oracle stored procedure, which has both an IN and an INOUT parameter:
CREATE OR REPLACE PROCEDURE updateEmpName (Emp_SSN IN NUMBER,
Emp_Name IN OUT VARCHAR)
AS
BEGIN
UPDATE Employees
SET name = Emp_name
WHERE SSN = EMP_SSN;
COMMIT;

SELECT name
INTO Emp_Name
FROM Employees
WHERE SSN = EMP_SSN;
END;
The following code snippet shows how to use the preceding stored procedure:
String SQL = "{call updateEmpName (?,?)}";
CallableStatement cstmt = conn.prepareCall (SQL);
//Set the IN parameter
cstmt.setInt(1,111111111);
cstmt.setString(2,"Todd Thomas");
//Set the same parameter as OUT
cstmt.registerOutParameter(2,java.sql.Types.VARCHAR);
//Execute the call to the stored procedure
cstmt.executeUpdate();
//Retrieve the result from the stored procedure.
String str = cstmt.getString(2);
Note Be aware of data type compatibility issues when using INOUT parameters. You should use Java and
JDBC data types that map to each other for both the setXXX() methods and the registerOutParameter()
method. For example, if you use a setBoolean() method to bind the IN parameter, you should register the
OUT parameter as a JDBC BIT data type with the registerOutParameter() method. Or, if you are using
JDBC 3.0, you can register the OUT parameter as a JDBC BOOLEAN data type.
Chapter 5: Building JDBC Statements
86
Batch updates with the CallableStatement object
Because the CallableStatement interface extends PreparedStatement interface, it inherits the executeBatch()
method. However, the CallableStatement object cannot use OUT an INOUT parameters in batch update
operations. A BatchUpdateException is thrown if:
you try to use either OUT or INOUT parameters.•
your stored procedure returns anything but an update count.•

All other aspects associated with using batch updates remain the same as with prepared statements.
Summary
This chapter illustrated how to interact with your database once you open a connection. JDBC 3.0 provides
you with several different ways to submit SQL statements and control how the database processes them. In
particular you should remember:
You can use the Statement object to submit static DDL and DML commands.•
You use the PreparedStatment and CallableStatement objects to submit SQL statements with
parameters.

You use a CallableStatement object to access database stored procedures.•
You use Connection.setAutoCommit() and Connection.commit() methods enable you to control
transactions.

Chapter 5: Building JDBC Statements
87
Chapter 6: Working with Result Sets
In This Chapter
Creating different types of result sets•
Moving around and viewing data in a result set•
Updating data in a result set•
Retrieving data from a result set•
Handling data−type issues•
In Chapter 4, “Connecting to Databases with JDBC,” and Chapter 5, “Building JDBC Statements,” I covered
how to create and use Connection and Statement objects. These objects, combined with the ResultSet object,
provide all the components you need to build a full−featured JDBC application.
ResultSet objects hold data from SQL queries you execute using a Statement, PreparedStatement, or
CallableStatement object. In some respects a result set is a view into your database. To use the data contained
in a ResultSet object you must extract it using one of the getXXX() methods defined by the ResultSet
interface.
In this chapter I introduce the ResultSet object and explain its purpose in a JDBC program. I also detail the

different types of ResultSet objects, what each one does, and how to decide which one to use. Finally I discuss
data type differences that exist between SQL and Java; a topic that is important when you begin to extract and
use data from a ResultSet object.
What Are JDBC Result Sets?
Connection objects represent database connections; Statement objects enable you to execute SQL statements
against the database. These objects provide the foundation that enables you to build views of data in your
database.
The term “result set” refers to the row and column data contained in a ResultSet object. This is a logical view
of row and column data in your database that meets the criteria of your SQL query. A result set can have any
number of rows and columns: the actual number depends on the query. If you place a WHERE clause in your
SQL statement, only the row data meeting those criteria will be retrieved.
The ResultSet interface defines methods that enable you to interact with the data in the database. The JDBC
driver provides the ResultSet class that implements the interface. When a Statement, PreparedStatement, or
CallableStatement object executes an SQL query successfully it returns a ResultSet object.
Note Not all databases support stored−procedures that return result sets, and so the CallableStatement object
might not be able to return a result set. Check the developer’s guide for your DBMS, or the
documentation for your JDBC driver, to determine whether your database supports this feature.
The default ResultSet object enables you to only view the data in your database that meet the criteria of your
SQL query. However, you can also create ResultSet objects that can update the data in the row you are
viewing, insert a new row into the table, or even delete a row. Thus, a ResultSet object can enable you to
perform DML statements programmatically without having to explicitly issue SQL INSERT or UPDATE
88
statements.
The following section provides an overview of the concepts you need to understand in order to work
effectively with a ResultSet object.
Introducing Result Set Concepts
Although JDBC defines different types of result sets to meet different programmatic needs, many concepts
apply to all types. This section is devoted to describing these common concepts. Ideally, this will provide you
with a foundation for understanding the different types of result sets.
Result set cursors

Despite the number of rows in a result set, you can only access one row at a time. A ResultSet object points to
this "active" row using a cursor. If you want to reference another row in the result set you must explicitly
move the cursor with one of the ResultSet object’s cursor−movement methods.
Figure 6−1 illustrates a result set cursor and how it moves through the data set. Notice that the cursor points to
the third row of a seven−row result set. If you issue the ResultSet.next() method, the cursor advances one
position to the fourth row. Remember, when working with result set data you always work with the row where
the cursor points.
Two cursor positions within a result set warrant special mention. These are the “before first row” (BFR) and
“after last row” (ALR) cursor positions in the result set. These areas are devoid of data and an SQLException
occurs if you use the getXXX() or updateXXX() methods in these locations. Figure 6−1 also illustrates these
positions within a result set.
Figure 6−1: Result set cursor illustration
When a ResultSet object is initially populated the cursor defaults to the BFR position. You must explicitly
move the cursor to a location that contains data before invoking any data−access methods. In addition, when
scrolling through a result set you may run past the last row of data. This will place the cursor in the ALR
position. Depending upon the result set type, you may or may not be able to go backwards to a valid cursor
position. If you are unable to do this you must recreate your result set by executing the SQL statement again.
Chapter 6: Working with Result Sets
89
Result set types
JDBC provides different types of ResultSet objects that enables you to interact with the database in a variety
of ways. The first, which is the default, has minimal functionality and enables you only to move forward
through the result; it does not enable you to update data. The second type enables you to move forward,
backward, or to any row within the result set. The third enables you to update the contents in the result set.
JDBC 3.0 Prior to JDBC 3.0, the Connection.commit() method could close ResultSet objects. The driver
implementation determined this behavior. A new ResultSet interface property, holdability, enables
you to specify when a ResultSet object is closed. Your options are to close it when you call the
Connection.commit() method or to close it explicitly some time after a commit.
When creating either a Statement, PreparedStatement, or CallableStatement object you define the type of
result set it creates by passing in parameters to the respective create statement method of a Connection object.

You do not need to pass any parameters to create a standard result set.
Table 6−1 provides a quick summary of each result set type, and the following sections provide more detail.
Table 6−1 : Result Set Types
ResultSet Description
Standard Enables basic access to the result set data. Does not reflect changes to underlying
data on server. Moves in one direction, forward. Data within the result set cannot be
updated.
Scrollable Provides enhanced movement capabilities over the standard ResultSet object. Moves
forward, backward, or to any row. Can reflect data changes on the server. You set
this type with the resultSetType parameter when creating the Statement,
PreparedStatement, or CallableStatement object.
Updateable Enables you to update underlying database information through the ResultSet object.
You set this type with the resultSetConcurrency parameter when creating the
Statement, PreparedStatement, or CallableStatement object.
Note Scrollable and updateable ResultSet objects are mutually exclusive. For example, you can have an
updateable ResultSet object that is forward only, or a scrollable ResultSet object is not updateable.
Standard result sets
A standard result set is forward−only and non−updateable. This type enables you to move the cursor forward
through the result set but does not enable you to update the result set data. An SQLException is thrown if you
try to move the cursor backward, or insert or update the data in the result set.
You should consider using standard result sets when you need to do basic work with result set data. For
example, if you need to populate a listbox to display a list of inventory items, using a forward−only result set
makes sense. Or, this type will prove useful if you need to loop through the result set to count the number of
rows in a database that meet some criteria defined by a WHERE clause.
Chapter 6: Working with Result Sets
90

×