Java Database Programming With JDBC
Java Database Programming With JDBC
Database
Programming
(Publisher:
The
Coriolis
Author(s):
Pratik
ISBN:
1576100561
Publication Date: 10/01/96
Search
with
JDBC
Group)
Patel
this
book:
Introduction
Chapter 1JDBC: Databases The Java Way!
What Is The JDBC?
The JDBC Structure
ODBCs Part In The JDBC
Summary
Chapter 2SQL 101
The Relational Model And SQL
Understanding The Basics
Putting It Into Perspective: Schema And Catalog
Introducing Keys
Using Multiple Tables And Foreign Keys
Data Definition Language
Declaring Domains
Performing Checks
Creating Tables
Manipulating Tables
Data Maintenance Language
Data Query Language
Coming Up Next
Chapter 3Using JDBC Drivers
Quick Start Guide
Installing java.sql.*
Registering And Calling JDBC Drivers
The sql.drivers Property
Theres Always A Class For A Name
Just Do It
JDBC URL And The Connection
Using ODBC Drivers
Installing The JDBC-ODBC Bridge
Setting Up ODBC Drivers
Summary
Chapter 4The InteractiveSQL Applet
Your First JDBC Applet
The Blueprint
Getting A Handle On The JDBC Essentials: The
Complete Applet Source Code
The Look Of The Applet
Handling Events
Opening The Connection
No Guts, No Glory: Executing Queries And
Processing Results
Wrapping It Up
The HTML File That Calls The Applet
The Final Product
Coming Up Next
Chapter 5Accessing ODBC Services Using JDBC
Bridge Requirements
The Bridge Is Great, But...
The ODBC URL
JDBC To ODBC Calls: A Roadmap
Chapter 6SQL Data Types In Java And ORM
Mapping SQL Data To Java
ResultSetMetaData
Understanding The Object Relation Model
Mapping A Table Into A Java Object
Summary
Chapter 7Working With Query Results
A Basic Java Object For Storing Results
Showing The Results
JDBC
Issues:
Database
Advantages Of Middleware
Disadvantages Of Middleware
The Application Server: A Complete Example
With Code
The Client: A Complete Example With Code
Summary
Chapter 12The JDBC API
Classes
public class Date
public class DriverManager
public class DriverPropertyInfo
public final class Numeric
public class Time
public class TimeStamp
public class Types
Interfaces
public interface CallableStatement
public interface Connection
public interface DatabaseMetaData
public interface Driver
public interface PreparedStatement
public interface ResultSet
public interface ResultSetMetaData
public interface Statement
Exceptions
public class DataTruncation
public class SQLException
public class SQLWarning
Appendix A
Appendix B
Appendix C
Appendix D
Index
Chapter
JDBC: Databases The Java Way!
Figure 1.1 shows the architecture of the JDBC. The DriverManager class is
used to open a connection to a database via a JDBC driver, which must
register with the DriverManager before the connection can be formed.
When a connection is attempted, the DriverManager chooses from a given
list of available drivers to suit the explict type of database connection. After
a connection is formed, the calls to query and fetch results are made directly
with the JDBC driver. The JDBC driver must implement the classes to
process these functions for the specific database, but the rigid specification
of the JDBC ensures that the drivers will perform as expected. Essentially,
the developer who has JDBC drivers for a certain database does not need to
worry about changing the code for the Java program if a different type of
database is used (assuming that the JDBC driver for the other database is
available). This is especially useful in the scenario of distributed databases.
Figure 1.1 The architecture of the JDBC.
The JDBC uses a URL syntax for specifying a database. For example, a
connection to a mSQL database, which was used to develop some of the
Java applets in this book, is:
jdbc:msql://mydatabase.server.com:1112/testdb
This statement specifies the transport to use (jdbc), the database type (msql),
the server name, the port (1112), and the database to connect to (testdb).
Well discuss specifying a database more thoroughly in Chapter 3.
The data types in SQL are mapped into native Java types whenever possible.
When a native type is not present in Java, a class is available for retrieving
data of that type. Consider, for example, the Date type in the JDBC. A
developer can assign a date field in a database to a JDBC Date class, after
which the developer can use the methods in the Date class to display or
perform operations. The JDBC also includes support for binary large objects,
or BLOB data types; you can retreive and store images, sound, documents,
and other binary data in a database with the JDBC. In Chapter 6, well cover
the SQL data types and their mapping into Java/JDBC, as well objectrelational mapping.
ODBCs Part In The JDBC
The JDBC and ODBC share a common parent: Both are based on the same
X/OPEN call level interface for SQL. Though there are JDBC drivers
emerging for many databases, you can write database-aware Java programs
using existing ODBC drivers. In fact, Javasoft and Intersolv have written a
JDBC driverthe JDBC-ODBC Bridgethat allows developers to use
exisiting ODBC drivers in Java programs. Figure 1.2 shows the place of the
JDBC-ODBC Bridge in the overall architecture of the JDBC. However, the
JDBC-ODBC Bridge requires pre-installation on the client, or wherever the
Java program is actually running, because the Bridge must make native
method calls to do the translation from ODBC to JDBC. This pre-installation
issue is also true for JDBC drivers that use native methods. Only 100 percent
Java JDBC drivers can be downloaded across a network with a Java applet,
thus requiring no pre-installation of the driver.
Figure 1.2 ODBC in the JDBC model.
ODBC drivers function in the same manner as true JDBC drivers; in fact,
the JDBC-ODBC bridge is actually a sophisticated JDBC driver that does
low-level translation to and from ODBC. When the JDBC driver for a
certain database becomes available, you can easily switch from the ODBC
driver to the new JDBC driver with few, if any, changes to the code of the
Java program.
Summary
The JDBC is not only a specification for using data sources in Java applets
and applications, but it also allows you to create and use low-level drivers to
connect and talk with data sources. You have now explored the JDBC
architecture and seen how the ODBC fits into the picture. The important
concept to remember about the JDBC is that the modular design of the
JDBC interface allows you to change between drivershence databases
without recoding your Java programs.
In the next chapter, well take a step back to give you a quick primer on
SQL, one of the pillars of the JDBC. If you are already familiar with SQL92, feel free to skip the chapter. However, I think that you may find the
chapter helpful in clarifying the SQL queries performed in the sample JDBC
programs we develop in this book.
Chapter
SQL 101
Now that you understand how to logically separate your data, its time to
take our model one step higher and introduce you to the schema/catalog
relationship. The schema is a higher-level container that is defined as a
collection of zero or more tables, where a table belongs to exactly one
schema. In the same way, a catalog can contain zero or more schemas. This
abstract is a necessary part of a robust relational database management
system (RDBMS). The primary reason is access control: It facilitates who
can read a table, who can change a table, and even who can create or destroy
tables. Figure 2.3 demonstrates this point nicely. Here we have added
another table, called CONFIDENTIAL. It contains the home address, home
phone number, and salary of each employee. This information needs to
belong in a separate schema so that anyone who is not in payroll cannot
access the data, while allowing those in marketing to get the necessary data
to do their job.
Figure 2.3 The table, schema, and catalog relationship allows you to limit
access to confidential information.
Introducing Keys
As you can see in the previous example, we have purposely set up the three
tables to link to one another. The EMPLOYEE table contains a column that
has the department number that the employee belongs in. This department
number also appears in the DEPARTMENT table, which describes each
department in the company. The EMPLOYEE and CONFIDENTIAL tables
are related, but we still need to add one corresponding entry (row) in one
table for each entry in the other, the distinction coming from the employees
number.
The linkemployee number and department numberwe have set up can
be thought of as a key. A key is used to identify information within a table.
Each individual employee or department should have a unique key to aid in
various functions performed on the tables. In keeping with the relational
model, the key is supposed to be unique within the table: No other entry in
the table may have the same primary key.
A single column is sometimes enough to uniquely identify a row, or entry.
However, a combination of rows can be used to compose a primary keyfor
example, we might want to just use the combination of the title and city
DDL is also used to drop tables and perform a variety of other functions,
such as adding and deleting rows (entries) from a table, and adding and
deleting columns from a table. Ill show you some of these along the way.
Declaring Domains
One of the handy shortcuts that the DDL offers is a way to create predefined
data objects. Though we havent really talked about the data types available
in SQL, you can probably guess the common ones like integer, character,
decimal (floating point), date, etc. Domains allow you to declare a data type
of specific length and then give the declared type a name. This can come in
handy if you have numerous data columns that are of the same data type and
characteristics. Heres the SQL statement you use to declare a domain:
CREATE DOMAIN EMP_NUMBER AS CHAR(5)
Tip: Smart
domain
declaration
habits.
When you are actually creating or altering tables,
this domain can be used instead of specifying
CHAR(20) each time. There are a number of reasons
why this is good practice. Notice that we chose to
make EMP_NUMBER a domain. This is a column that
appears in several tables.
If we mistakenly use the wrong type or length in one of the table definitions
where we have employee numbers, it could cause havoc when running SQL
queries. Youll have to keep reading to find out the other reason.
Performing Checks
Predefining a data object is also useful for making sure that a certain entry in
a column matches the data we expect to find there. For example, our empno
field should contain a number. If it doesnt, performing a check of that data
will alert us to the error. These checks can exist in the actual table definition,
but its efficient to localize a check in a domain. Hence, we can add a check
to our employee number domain:
I can hear you now, Whats this VARCHAR data type? SQL has two
defined string types: CHAR and VARCHAR. The RDBMS allocates exactly
the amount of space you specify when you use a CHAR data type; when you
set an entry that is defined as a CHAR(N) to a string smaller than the size of
occur. In this situation, you would have to drop any referencing tables first,
and then rebuild them without the referencing.
Altering a table definition is as straightforward as dropping a table. To
remove a column from a table, issue a command like this:
ALTER TABLE EMPLOYEE
DROP firstname;
Of course, if this column is part of the tables key, you wont be able to
remove it. Also, if the column is referenced by another table, or there is
another column in any table that is dependent on this column, the operation
is not allowed.
To add a column to a table, run a query like this:
ALTER TABLE CONFIDENTIAL
ADD dateofbirth DATE NOT NULL;
You can also make multiple alterations at one time with the ALTER
clause.
VALUES (
'00201', 'Pratik', 'Patel',
);
'Author', ''
If you dont want to add all the fields in the row, you can specify only the
fields you wish to add:
INSERT INTO EMPLOYEE (empno, lastname, firstname,
function)
VALUES (
'00201', 'Pratik', 'Patel', 'Author'
);
As you can see, I chose not to add anything in the department field. Note
that if a fields check constraint is not met, or a table check is not met, an
error will be produced. For example, if we did not add something under the
firstname field, an error would have been returned because we defined the
tables firstname column check as NOT NULL. We did not set up a check
for the department field, so the previous command would not produce an
error.
To delete a tables contents without removing the table completely, you can
run a command like this:
DELETE FROM EMPLOYEE;
This statement will wipe the table clean, leaving no data in any of the
columns, and, essentially, deleting all of the rows in the table. Deleting a
single entry requires that you specify some criteria for deletion:
DELETE FROM EMPLOYEE
WHERE empno='00201';
You can delete multiple rows with this type of operation, as well. If the
WHERE clause matches more than one row, all of the rows will be deleted.
You can also delete multiple entries by using the SELECT command in the
WHERE clause; we will get to the SELECT command in the next section.
If you really want to get fancy, you can use one statement to delete the same
row from more than one table:
DELETE FROM EMPLOYEE, CONFIDENTIAL
WHERE empno='00201';
The final command I want to cover in this section is UPDATE. This
command allows you to change one or more existing fields in a row. Here is
FROM table_names
WHERE predicates
Lets take a look at the various functions of the SELECT command. To
retrieve a complete table, run this query:
SELECT * FROM EMPLOYEE;
To get a list of employees in the Editorial department, run this query:
SELECT * FROM EMPLOYEE
WHERE department = 'Editorial';
To sort the list based on the employees last names, use the ORDER BY
directive:
SELECT * FROM EMPLOYEE
WHERE department= 'Editorial'
ORDER BY lastname;
To get this ordered list but only see the employee number, enter the
following statements:
SELECT empno FROM EMPLOYEE
WHERE department = 'Editorial'
ORDER BY lastname;
To get a list of users with the name Pratik Patel, you would enter:
SELECT * FROM EMPLOYEE
WHERE (firstname='Pratik') AND (lastname='Patel');
What if we want to show two tables at once? No problem, as shown here:
SELECT EMPLOYEE.*, CONFIDENTIAL.*
FROM EMPLOYEE, CONFIDENTIAL;
Heres a more challenging query: Show the salary for employees in the
Editorial department. According to our tables, the salary information is in the
CONFIDENTIAL table, and the department in which an employee belongs
is in the EMPLOYEE table. How do we associate a comparison in one table
to another? Since we used the reference of the employee number in the
CONFIDENTIAL table from the EMPLOYEE table, we can specify the
employees that match a specified department, and then use the resulting
employee number to retrieve the salary information from the
CONFIDENTIAL table:
SELECT c.salary
FROM EMPLOYEE as e, CONFIDENTIAL as c
WHERE e.department = 'Editorial'
As a developer whos using the JDBC, one of the first things you need to
understand is how to use JDBC drivers and the JDBC API to connect to a
data source. This chapter outlines the steps necessary for you to begin that
process. Well be covering the details of getting JDBC drivers to work, as
well as the driver registration process we touched on in Chapter 1. Well also
take some time to explore JavaSofts JDBC-ODBC Bridge, which allows
your Java programs to use ODBC drivers to call ODBC data sources.
Before our discussion gets underway though, I need to point out a few things
about JDBC drivers. First, there are no drivers packaged with the JDBC API;
you must get them yourself from software vendors. Check out this books
Web site for links to demo versions of drivers for your favorite database
server, as well as free JDBC drivers available on the Internet. Second, if you
want to use ODBC, dont forget that youll need ODBC drivers, as well. If
you dont have a database server, but you want to use JDBC, dont despair:
You can use the ODBC drivers packaged with Microsoft Access. Using the
JDBC-ODBC Bridge, you can write Java applications that can interact with
an Access database.
Unfortunately, applets enforce a security restriction that does not allow
access to the local disk, so ODBC drivers might not work in the applet
context (inside a Web browser). A future release of the Java Development
Kit (JDK) may change or relax this security restriction. A workaround for
Java-enabled Web browsers is being prepared, and by the time you read this,
it may very well be possible to use the JDBC-ODBC bridge. Using ODBC
drivers in Java programs also requires pre-installation of the ODBC drivers
and JDBC-ODBC Bridge on the client machine. In contrast, JDBC drivers
that are 100 percent Java class files can be downloaded dynamically over the
network, along with the calling applets class file. Ill provide a more
thorough discussion of this point in Chapter 9.
Quick Start Guide
So youre a regular Java hacker, and youve already figured out how to
install the JDBC API package. Now you want to jump right into it. This
section will outline the four basic steps for running your first query and
getting the results. The steps are explained in greater detail in Chapter 4.
Figure 3.1 is a diagram relating the four classes that youll call on in your
JDBC Java program, and it is the skeleton around which you can build
database-aware Java programs. The diagram does not list all of the methods
available in the respective classes. See Chapter 12, the JDBC API reference,
for the complete class and method list.
Figure 3.1 The JDBC classes to call.
The following (Listing 3.1) is a very simple JDBC application that follows
these four steps. It runs a query and gets one row from the returned result. If
you dont understand everything going on here, dont worryits all
explained in detail in Chapter 4.
Listing 3.1 Example JDBC application.
import java.net.URL;
import java.sql.*;
class Select {
public static void main(String argv[]) {
try {
new imaginary.sql.iMsqlDriver();
String
url
=
"jdbc:msql://elanor.oit.unc.edu:1112/bcancer";
Connection
con
=
DriverManager.getConnection(url, "prpatel", "");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT *
FROM Users");
System.out.println("Got results:");
while(rs.next()) {
String UID=
rs.getString(1);
rs.getString(2);
rs.getString(3);
rs.getString(4);
rs.getString(5);
String Password=
String Last=
String First=
String OfficeID=
System.out.print
"+Last+"
System.out.print
("\n");
}
stmt.close();
con.close();
}
catch( Exception e ) {
e.printStackTrace();
}
}
Installing java.sql.*
The java.sql.* package contains the JDBC base API classes, which are
supposed to be
Installing java.sql.*
The java.sql.* package contains the JDBC base API classes, which are
supposed to be in the normal java.* hierachy that is distributed as part of the
Java API (which includes the java.awt, java.io, and java.lang packages).
Currently, the JDBC API is not distributed with the JDK, but it is slated to be
included in the next release. I have a sneaking suspicion that the java.sql.*
package will also be included in the future APIs of popular Java-enabled
Web browsers.
However, you dont have to wait for this updated software to be released.
You can grab the JDBC API classes from the accompanying CD-ROM or
from the JavaSoft Web site at http://splash.java.com/jdbc. As I was writing
this chapter, the classes were stored in a file named jdbc.100.tar.Z. By the
time you read this chapter, however, the file name may be slightly different.
Once you have your software, simply follow these easy instructions to install
the API classes in the proper place on your computers hard disk. The
method shown here allows you to compile and run Java applications and
applets (using the Appletviewer) that use the JDBC:
I must stress that you should make sure that you have the CLASSPATH set
properly. The package will be called in the following way in your Java
program:
import java.sql.*
You need to point the CLASSPATH at the parent of the java directory you
copied in Step 2, which is why we set the CLASSPATH in Step 3. The
package is contained in the java/sql/ folder, which is exactly as it should be
according to the calling code snippet above.
Registering And Calling JDBC Drivers
Now that weve installed the JDBC classes, lets cover how you load a
JDBC driver. Note that the java.sql.* must be imported into your Java
program if you want to use a JDBC driver. These JDBC base classes contain
the necessary elements for properly instantiating JDBC drivers, and they
serve as the middleman between you and the low-level code in the JDBC
driver. The JDBC API provides you with an easy-to-use interface for
interacting with data sources, independent of the driver you are using. The
con
=
DriverManager.getConnection(url,
Name,
password);
// remember to register the driver before doing
this!
Chapter 4 shows a complete example of how to use the DriverManager and
Connection classes, as well as how to execute queries against the database
server and get the results.
Using ODBC Drivers
In an effort to close the gap between existing ODBC drivers for data sources
and the emerging pure Java JDBC drivers, JavaSoft and Intersolv released
the JDBC-ODBC Bridge. Note that there is a Java interface (hidden as a
JDBC driver called JdbcOdbcDriver and found in the jdbc/odbc/ directory
below) that does the necessary JDBC to ODBC translation with the native
method library that is part of the JDBC-ODBC bridge package. Although
Chapter 5 covers the inner workings of the Bridge, I would like to show you
how to install it here. Once the Bridge is set up, the JDBC handles access to
the ODBC data sources just like access to normal JDBC drivers; in essence,
you can use the same Java code with either JDBC drivers or ODBC drivers
that use the Bridgeall you have to do is change the JDBC URL to reflect a
different driver.
Installing The JDBC-ODBC Bridge
There are three steps to installing the JDBC-ODBC Bridge. Youll need to
get the package first. Look on the CD-ROM, or grab the latest version from
JavaSofts Web site at http://splash.javasoft.com/jdbc.
1. Uncompress the package.
2. Move the jdbc directory (located in the jdbcodbc/classes directory) into a directory listed in your
CLASSPATH, or move it to your regular Java API tree.
3. Move JdbcOdbc.dll into your java/bin directory to
make sure that the system and Java executables can
find
the
file.
You
can
also:
For Unix:
Add the path location of the JdbcOdbc.dll to your
LD_LIBRARY_PATH, or move the DLL into a directory
covered by this environment variable.
into
the
\WINDOWS\SYSTEM
The data sources for the ODBC driver and the drivers themselves must be
configured before you can run Java programs that access them. Consult your
platform documentation and ODBC servers documentation for specific
information.
One of the great features of the Bridge is that it allows you to use existing
data sources to start developing database-aware Java applications. And with
Access, you dont even need a database server! In Chapter 11, I present the
full source code for writing an application server that can use the JDBCODBC Bridge, the Access ODBC drivers that come with Access 95, and an
Access database to develop Java applets that can interact with a database
without having a database server.
To set up an Access database for ODBC, follow these steps (Im assuming
that you are using Windows 95):
1. Make sure you have the Access 95 ODBC drivers
installed. These ODBC drivers can be installed from
the Access install program.
2. Select Start Menu|Settings|Control Panels.
3. Click on 32 bit ODBC.
4. Click on the Add button and choose the Access
Driver.
5. Type in a Data Source Name and Description
(anything you like).
6. In the Database area, click on Select.
7. Select the Access database file; a sample
database is located in MSoffice\ACCESS\Samples (if
you installed it during the Access installation).
However, you can specify any Access database you
want.
8. You may want to click on the Advanced button
and set the Username and Password. Click on OK and
then on Close to complete the configuration.
That is all you need to do to set up the ODBC data source. Now you can
write Java applications to interact with the data source on the machine in
which you performed the configuration; the ODBC driver is not directly
accessible over the network. You can access the data source by using the
name you supplied in Step 5. For example, the URL would be something
like
jdbc:odbc:DataSourceName
and the statement
Class.forName("jdbc.odbc.JdbcOdbcDriver")
would load the JDBC-ODBC bridge.
Summary
The next chapter works through a complete example of using a JDBC driver.
I use the mSQL driver to query an mSQL database server over the network.
The JDBC driver can easily be changed to use an ODBC driver or another
JDBC driver to connect to a different data source.
Chapter
The
SQL Applet
4
Interactive
Now that you have seen how to use JDBC drivers, its time we ante up. In
this chapter, we jump into the JDBC with an example applet that well build
on and derive from through the rest of the book. Our Interactive Query
applet will accomplish a number of tasks. It will:
Connect to a database server, using a JDBC driver
Wait for a user to enter an SQL query for the
database server to process
Display the results of the query in another text
area
Of course, before we can get to the programming details of the applet, we
need to take a step back, review the basics, and put together a plan. I know,
plans take time to develop, and you want to get into the good stuff right
away. But trust me, well be saving ourselves a lot of trouble later on by
figuring out just the right way to get to where we want to go.
Your First JDBC Applet
within). This approach also adds to the overall efficiency; the code shows
how to deal with some of the events directly in the event handler.
Figure 4.1 Flow diagram of the Interactive Query applet.
The Applet Four-Step
As indicated in Figure 4.2, Java applets have a distinct life cycle of four
basic steps: initialization, execution, termination, and clean up. Its often
unnecessary to implement all four, but we can use them to our advantage to
make our database-aware applet more robust. Why does an applet have this
flow? Applets run inside a Java Virtual Machine (JVM), or Java interpreter,
like the one embedded within a Java-enabled Web browser. The interpreter
handles the allocation of memory and resources for the applet, thus the
applet must live within the context of the JVM. This is a pre-defined
specification of the Java environment, designed to control the applets
behavior. Note that Java applications do not follow this life-cycle, as they
are not bound to run in the context of Java applets. Heres a synopsis of what
the four overridable methods, or steps, do in the context of Java applets:
Figure 4.2 An applets life cycle.
init This is the method called when the applet is
first started. It is only called once, and it is the place
where the initialization of objects (via construction or
assignment) should be done. It is also a good place
to set up the user interface.
start Once the applet has been initialized, this
method is called to begin the execution of the applet.
If you are using threads, this is the ideal place to
begin threads that you create to use in the applet.
This method is called when the Web browser (or
appletviewer) becomes active; that is, when the user
brings up the window or focuses attention to the
window.
stop This method is called when the applet
window (which can be within a Web browser)
becomes inactive. For instance, iconifying the Web
browser calls this method. This can be used to
The object evt is the local instantiation of the Event parameter that is part of
the handleEvent method, as well see later in the complete source code
listing. We use the target property to see which object the event occurred in,
then we look at the key property to see if the Enter key was pressed. The
Java escape sequence for Enter is \n. The rest of the code shown in the
listing is fairly straightforward: We compare the pressed key to the enter
escape sequence, and if we come up with a match, we set the Name string
variable to the text in the NameField using the TextArea getText method.
Because we have processed the event, and we want to let the rest of the
event handler know that weve dealt with it, we return true. If this wasnt the
key we were looking for in this specific object (NameField), we would
return false so that the other event handling code could attempt to process
this event.
Finishing Up
One of Javas great strengths lies in its ability to automatically allocate and
de-allocate memory for objects created in the program, so the programmer
doesnt have to. We primarily use the destroy method to close the database
connection that we open in the applet. The JDBC driver that we used to
connect to the data source is alerted to the fact that the program is exiting, so
it can gracefully close the connection and flush input and output buffers.
Getting A Handle On The JDBC Essentials: The Complete Applet
Source Code
Okay, enough talk, lets get busy! The complete source code is shown in
Listings 4.2 though 4.9. The HTML file that we use to call our applet is
shown in Listing 4.10. I bet youre not too keen on entering all that code.
But wait! Theres no need to type it all in, just pull out the CD-ROM and
load the source into your favorite editor or IDE. Dont forget, though, that
you need to have the JDBC driver installed, and you may need your
CLASSPATH set so that the applet can find the driver. If youre planning on
loading the driver as a class along with the applet, make sure you put the
driver in the same place as the applet. See Chapter 3 if you have trouble
getting the applet to run and you keep getting the Cant Find a Driver or
Class not found error.
Tip: Source
code
on
the
CD-ROM.
Theres no need to type in the source code because
the Interactive Query applet can be found on the CDROM, as is true for all source code in this book.
to
result_label.setFont(new
Font("Helvetica",
Font.PLAIN, 16));
result_label.setForeground(Color.blue);
gridbag.setConstraints(result_label, Con);
add(result_label);
// Here we add a label on its own line. We also set
the colors for it.
Con.weighty=1.0;
gridbag.setConstraints(OutputField, Con);
OutputField.setForeground(Color.white);
OutputField.setBackground(Color.black);
add(OutputField);
// This is what we were talking about before. We
want the large OutputField to
// take up as much of the remaining space as
possible, so we set the
// weighty=1 at this point. This sets the field
apart from the previously
// added components, and gives it more room to
exist in.
show();
} //init
Everything has been added to the user interface, so lets show it! We also
dont need to do anything else as far as preparation, so that ends the init
method of our applet. Now we can move on to handling events.
Handling Events
We want to watch for four events when our applet is running: the user
pressing the Enter key in the DBurl, NameField, and QueryField TextAreas,
and the user clicking on the Connect button. Earlier in the chapter, we saw
how to watch for events, but now we get to see what we do once the event is
trapped, as shown in Listing 4.5. The event handling code is contained in the
generic handleEvent method.
Listing 4.5 Handling events.
public boolean handleEvent(Event evt) {
// The standard format for this method includes the
Event class where
}
return false;
} // handleEvent() end
No Guts, No Glory: Executing Queries And Processing Results
Now that we have opened the connection to the data source (Listing 4.6), its
time to set up the mechanism for executing queries and getting the results, as
shown in Listings 4.7 and 4.8. The parameter that we need in this method is
a String containing the SQL query the user entered into the QueryField. We
will return the results of the query as a string because we only want to pipe
all of the results into the OutputField TextArea. We cast all of the returned
results into a Stringhowever, if the database contains binary data, we
could get some weird output, or even cause the program to break. When I
tested the applet, the data source that I queried contained numerical and
strings only. In Chapter 7, Ill show you how to deal with different data
types in the ANSI SQL-2 specification, upon which the data types for the
JDBC are based.
Listing 4.7 Executing a statement.
e.printStackTrace();
System.out.println(e.getMessage());
}
} // end destroy
} // end applet
The HTML File That Calls The Applet
We need to call this applet from an HTML file, which is shown in Listing
4.10. We dont pass in any properties, but we could easily include a default
data source URL and user name that the applet would read in before
initializing the user interface, and then set the appropriate TextField to show
these defaults. Note that we set the width and height carefully in the
<APPLET> tag. This is to make sure that our applets user interface has
enough room to be properly laid out.
Listing 4.10 HTML code to call the interactive query applet.
<HTML>
<HEAD>
<TITLE>JDBC Client Applet - Interactive SQL Command
Util</TITLE>
</HEAD>
<BODY>
<H1>Interactive JDBC SQL Query Applet</H1>
<hr>
<applet code=IQ.class width=450 height=350>
</applet>
<hr>
</BODY>
</HTML>
The Final Product
Figure 4.3 shows a screen shot of the completed applet, and Figure 4.4
shows the applet running. Not too shabby for our first try. Weve covered a
lot of ground in creating this applet, so lets take some time to recap the
important details. We learned how to:
Figure 4.3 The completed Interactive Query applet.
Chapter
Accessing ODBC Services Using JDBC
One of JavaSofts first tasks in developing the JDBC API was to get it into
the hands of developers. Defining the API specification was a major step,
but JDBC drivers must be implemented in order to actually access data.
Because ODBC has already established itself as an industry standard, what
better way to make JDBC usable by a large community of developers than to
provide a JDBC driver that uses ODBC. JavaSoft turned to Intersolv to
provide resources to develop a bridge between the two, and the resulting
JDBC driverthe Bridgeis now included with the Java Developers kit.
The Bridge works great, but there are some things you need to understand
before you can implement it properly. In this chapter, well cover the
requirements necessary to use the Bridge, the limitations of the Bridge, and
the most elegant way to make a connection to a JDBC URL. Ill also provide
you with a list of each JDBC method and the corresponding ODBC call
(broken down by the type of call).
Bridge Requirements
One thing to note about the JDBC-ODBC Bridge is that it contains a very
thin layer of native code. This librarys sole purpose is to accept an ODBC
call from Java, execute that call, and return any results back to the driver.
There is no other magic happening within this library; all processing,
including memory management, is contained within the Java side of the
Bridge. Unfortunately, this means that there is a library containing C code
that must be ported to each of the operating systems that the Bridge will
execute on. This is obviously not an ideal situation, and invalidates one of
Javas major advantagesportability. So, instead of being able to download
Java class files and execute on the fly, you must first install and configure
additional software in order to use the Bridge. Heres a short checklist of
required components:
The Java Developers Kit
The JDBC Interface classes (java.sql.*)
The JDBC-ODBC Bridge classes (jdbc.odbc.* or
sun.jdbc.odbc.* for JDBC version 1.1 and higher)
An ODBC Driver Manager (such as the one
provided by Microsoft for Win95/NT); do not confuse
this with the JDBC DriverManager class
Any ODBC drivers to be used from the Bridge
(from vendors such as Intersolv, Microsoft, and
Visigenic)
Before actually attempting to use the Bridge, save yourself lots of headaches
be sure to test the ODBC drivers that you will be using! I have pursued
countless reported problems that ended up being nothing more than an
ODBC configuration issue. Make sure you setup your data sources properly,
and then test them to make sure you can connect and perform work. You can
accomplish this by either using an existing tool or writing your own sample
ODBC application. Most vendors include sample source code to create an
ODBC application, and Microsoft provides a tool named Gator (a.k.a
from which they were downloaded. Normally, the Java Security Manager
will prohibit a TCP connection from being made to an unauthorized
hostname; that is, if the TCP connection is being made from within the Java
Virtual Machine (JVM). In the case of the Bridge, this connection would be
made from within the ODBC driver, outside the control of the JVM. If the
Bridge could determine the hostname that it will be connected to, a call to
the Java Security Manager could easily check to ensure that a connection is
allowed. Unfortunately, it is not always possible to determine the hostname
for a given ODBC data source name. For this reason, the Bridge always
assumes the worst. An untrusted applet is not allowed to access any ODBC
data source. What this means is that if you cant convince the Internet
browser in use that an applet is trusted, you cant use the Bridge from that
applet.
The ODBC URL
To make a connection to a JDBC driver, you must supply a URL. The
general structure of the JDBC URL is
jdbc:<subprotocol>:<subname>
where subprotocol is the kind of database connectivity being requested, and
subname provides additional information for the subprotocol. For the
Bridge, the specific URL structure is:
jdbc:odbc:<ODBC
datasource
name>[;attributename=attribute-value]...
The Bridge can only provide services for URLs that have a subprotocol of
odbc. If a different subprotocol is given, the Bridge will simply tell the
JDBC DriverManager that it has no idea what the URL means, and that it
cant support it. The subname specifies the ODBC data source name to use,
followed by any additional connection string attributes. Heres a code
snippet that you can use to connect to an ODBC data source named
Accounting, with a user name of dept12 and a password of Julie:
// Create a new instance of the JDBC-ODBC Bridge.
new jdbc.odbc.JdbcOdbcDriver();
// The JDBC-ODBC Bridge will have registered itself
with the JDBC
prop
new
object
with
each
prop.put("UID", "dept12");
prop.put("PWD", "Julie");
Connection
con
=
DriverManager.getConnection("jdbc:odbc:Accounting",
prop);
JDBC To ODBC Calls: A Roadmap
For all of you ODBC junkies, Tables 5.1 through 5.8 show each JDBC
method and the corresponding ODBC call (only JDBC methods that actually
make an ODBC call are included). I can hear you now: But isnt this a
closely guarded national secret? What if someone takes this information to
write another Bridge? First of all, the information provided here can be
easily gathered by turning on the JDBC logging facility
(DriverManager.setLogStream). The Bridge is nice enough to log every
ODBC call as it is made, providing a log stream has been set via the
DriverManager (all good JDBC drivers should provide adequate logging to
aid in debugging). And second, the Bridge is provided for free. No one could
possibly take this information to create a better Bridge at a lower price. It
simply cant be done. I provide this information in an effort to help you
better understand how the Bridge operates, and, if you are well versed in
ODBC, to give you the direct correlation between the Bridge and ODBC.
This should enable you to write advanced JDBC applications right off the
starting line.
SQLDriverC ODBC Call
onnect The
Bridge
creates
a
connection
string
using
the
java.util.
Properties
attribute
given
Comments
Table
5.1
Driver ODBC
calls. JDBC
Interface
Method
getPropertyInfo connect
SQLBrowseC
onnect Each
property
returned is
converted
into a
DriverPrope
rtyInfo
object
Table 5.2 Connection
ODBC
calls.
JDBC
Interface Method
ODBC Call
Comments
prepareStatement
SQLPrepare
Prepares
statement for use
IN parameters
prepareCall
SQLPrepare
Prepares
statement for use
IN
and
parameters (JDBC
not defined the us
IN/OUT
parame
together)
nativeSQL
SQLNativeSql
Converts
the
g
SQL
into
na
format,
expan
escape sequences
setAutoCommit
SQLSetConnectOption fOption
SQL_AUTOCOMMIT
getAutoCommit
SQLGetConnectOption
fOption
SQL_AUTOCOMMIT
commit
SQLTransact
fType = SQL_COMM
rollback
SQLTransact
fType
SQL_ROLLBACK
close
SQLFreeConnect
setReadOnly
SQLSetConnectOption fOption
SQL_ACCESS_MODE
this is only a hin
the ODBC driver;
underlying driver
not actually change
behavior
isReadOnly
SQLGetConnectOption
setCatalog
SQLSetConnectOption fOption
SQL_CURRENT_
QUALIFIER
getCatalog
SQLGetInfo
fOption
SQL_ACCESS_MODE
fInfoType
SQL_DATABASE_NA
setTransactionIsolation SQLSetConnectOption
fOption
SQL_TXN_ISOLATIO
getTransactionIsolation SQLGetConnectOption
fOption
SQL_TXN_ISOLATIO
setAutoClose
getAutoClose
SQLGetInfo fInfoType =
SQL_CURSOR_COMMIT_
BEHAVIOR and
fInfoType =
SQL_CURSOR_
ROLLBACK_BEHAVIOR;
the Bridge makes both
calls, and if either are
true, then getAutoClose
returns true
Table 5.3 DatabaseMetaData
ODBC calls. JDBC Interface
Method
ODBC Call
Comments
allProceduresAreCallable
SQLGetInfo
fInfoType =
PROCEDURES
SQL_
allTablesAreSelectable
SQLGetInfo
fInfoType
TABLES
SQL_
getUserName
SQLGetInfo
fInfoType = SQL_US
isReadOnly
SQLGetInfo
fInfoType
=
SOURCE_READ_ONL
nullsAreSortedHigh
SQLGetInfo
fInfoType
SQL_NULL_COLLATIO
must be SQL_NC_HI
nullsAreSortedLow
SQLGetInfo
fInfoType
SQL_NULL_COLLATIO
must be SQL_NC_LO
nullsAreSortedAtStart
SQLGetInfo
fInfoType
SQL_NULL_COLLATIO
must be SQL_NC_ST
nullsAreSortedAtEnd
SQLGetInfo
fInfoType
SQL_NULL_COLLATIO
must be SQL_NC_EN
getDatabaseProductName
SQLGetInfo
fInfoType = SQL_DB
getDatabaseProductVersion
SQLGetInfo
fInfoType = SQL_DB
usesLocalFiles
SQLGetInfo
fInfoType
=
SQL
the
result
SQL_FILE_QUALIFIE
usesLocalFilePerTable
SQLGetInfo
fInfoType
=
SQL
the
result
SQL_FILE_TABLE
supportsMixedCaseIdentifiers
SQLGetInfo
fInfoType
SQL_IDENTIFIER_CA
result must be S
SQL_IC_LOWER or S
storesUpperCaseIdentifiers
SQLGetInfo
fInfoType
SQL_IDENTIFIER_CA
result must be SQL_
storesLowerCaseIdentifiers
SQLGetInfo
fInfoType
SQL_IDENTIFIER_CA
result must be SQL_
storesMixedCaseIdentifiers
SQLGetInfo
fInfoType
SQL_IDENTIFIER_CA
result must be SQL_
supportsMixedCaseQuoted
Identifiers
SQLGetInfo
fInfoType
SQL_QUOTED_IDENT
the
result
SQL_IC_UPPER, SQ
or SQL_IC_MIXED
storesUpperCaseQuoted
Identifiers
SQLGetInfo
fInfoType
SQL_QUOTED_IDENT
the result must be S
storesLowerCaseQuoted
Identifiers
SQLGetInfo
fInfoType
SQL_QUOTED_IDENT
the
result
SQL_IC_LOWER
storesMixedCaseQuoted
Identifiers
SQLGetInfo
fInfoType
SQL_QUOTED_IDENT
the result must be S
getIdentifierQuoteString
SQLGetInfo
fInfoType
SQL_IDENTIFIER_QU
getSQLKeywords
SQLGetInfo
fInfoType = SQL_KE
getNumericFunctions
SQLGetInfo
fInfoType
SQL_NUMERIC_FUNC
result is a bitmask
the scalar numeric f
bitmask is used
comma-separated lis
getStringFunctions
SQLGetInfo
fInfoType
SQL_STRING_FUNCT
result is a bitmask
the scalar string f
bitmask is used
comma-separated lis
getSystemFunctions
SQLGetInfo
fInfoType
=
FUNCTIONS;
the
bitmask enumeratin
system functions; t
used
to
create
separated list of fun
getTimeDateFunctions
SQLGetInfo
fInfoType
=
SQ
FUNCTIONS;
the
bitmask enumeratin
date and time fu
bitmask is used
comma-separated lis
getSearchStringEscape
SQLGetInfo
fInfoType
SQL_SEARCH_PATTE
ESCAPE
getExtraNameCharacters
SQLGetInfo
fInfoType
=
CHARACTERS
supportsAlterTableWithAdd
Column
SQLGetInfo
fInfoType = SQL_
result
must
SQL_AT_ADD_COLUM
supportsAlterTableWithDrop
Column
SQLGetInfo
fInfoType
=SQL_
the
result
must
SQL_AT_DROP_
COLUMN bit set
supportsColumnAliasing
SQLGetInfo
fInfoType = SQL_CO
nullPlusNonNullIsNull
SQLGetInfo
fInfoType
SQL_CONCAT_NULL_
the result must be S
supportsConvert
SQLGetInfo
fInfoType
=
S
FUNCTIONS; the re
SQL_FN_CVT_CONVE
supportsTableCorrelation
Names
SQLGetInfo
fInfoType = SQL_C
NAME;
the
resu
SQL_CN_
DIFFERENT or SQL_C
supportsDifferentTable
CorrelationNames
SQLGetInfo
fInfoType = SQL_C
NAMES;
the
resu
SQL_CN_
DIFFERENT
supportsExpressionsIn
OrderBy
SQLGetInfo
fInfoType = SQL_E
IN_ORDER_BY
supportsOrderByUnrelated
SQLGetInfo
fInfoType
=
SQ
COLUMNS_IN_SELEC
supportsGroupBy
SQLGetInfo
fInfoType = SQL_G
result must not be S
SUPPORTED
supportsGroupByUnrelated
SQLGetInfo
fInfoType = SQL_G
result
must
be
RELATION
supportsGroupByBeyond
Select
SQLGetInfo
fInfoType = SQL_G
result
mus
SQL_GB_GROUP_BY
CONTAINS_SELECT
supportsLikeEscapeClause
SQLGetInfo
fInfoType
SQL_L
CLAUSE
supportsMultipleResultSets
SQLGetInfo
fInfoType = SQL_M
SETS
supportsMultipleTransactions
SQLGetInfo
fInfoType
=
ACTIVE_TXN
supportsNonNullableColumns
SQLGetInfo
fInfoType
=
NULLABLE_COLUMNS
must
be
SQ
NULL
supportsMinimumSQL
Grammar
SQLGetInfo
fInfoType
=
SQ
CONFORMANCE; re
SQL_OSC_MINIMUM
SQL_OSC_CORE,
SQL_OSC_EXTENDED
supportsCoreSQLGrammar
SQLGetInfo
fInfoType
=
SQL_CONFORMANCE
must
be
SQL_O
SQL_OSC_EXTENDED
supportsExtendedSQL
Grammar
SQLGetInfo
fInfoType
=
SQL_CONFORMANCE
must
be
EXTENDED
=
SQ
supportsIntegrityEnhancement SQLGetInfo
Facility
fInfoType
OPT_IEF
SQ
supportsOuterJoins
SQLGetInfo
fInfoType = SQL_O
the result must not
supportsFullOuterJoins
SQLGetInfo
fInfoType = SQL_O
the result must be
supportsLimitedOuterJoins
SQLGetInfo
fInfoType = SQL_O
the result must be
getSchemaTerm
SQLGetInfo
fInfoType = SQL_OW
getProcedureTerm
SQLGetInfo
fInfoType
SQL_PROCEDURE_TE
getCatalogTerm
SQLGetInfo
fInfoType
SQL_QUALIFIER_TER
isCatalogAtStart
SQLGetInfo
fInfoType
=
SQ
LOCATION; the re
SQL_QL_START
getCatalogSeparator
SQLGetInfo
fInfoType
SQL_QUALIFIER_NA
SEPARATOR
supportsSchemasInData
Manipulation
SQLGetInfo
fInfoType = SQL_OW
the
result
must
SQL_OU_DML_
STATEMENTS bit set
supportsSchemasInProcedure
Calls
SQLGetInfo
fInfoType = SQL_OW
the
result
must
SQL_OU_
PROCEDURE_INVOCA
supportsSchemasInTable
Definitions
SQLGetInfo
fInfoType = SQL_OW
the
result
must
SQL_OU_TABLE_
DEFINITION bit set
supportsSchemasInIndex
Definitions
SQLGetInfo
fInfoType = SQL_OW
the
result
must
SQL_OU_INDEX_
DEFINITION bit set
supportsSchemasInPrivilege
Definitions
SQLGetInfo
fInfoType = SQL_OW
the
result
must
SQL_OU_
PRIVILEGE_DEFINIT
supportsCatalogsInData
Manipulation
SQLGetInfo
fInfoType
SQL_QUALIFIER_US
result
must
SQL_QU_DML_STATE
set
supportsCatalogsInProcedure
Calls
SQLGetInfo
fInfoType
SQL_QUALIFIER_US
result must have
PROCEDURE_INVOCA
supportsCatalogsInTable
Definitions
SQLGetInfo
fInfoType
=
SQ
USAGE; the result m
SQL_QU_TABLE_DEF
set
supportsCatalogsInIndex
Definitions
SQLGetInfo
fInfoType
SQL_QUALIFIER_US
result
must
SQL_QU_INDEX_DEF
set
supportsCatalogsInPrivilege
Definitions
SQLGetInfo
fInfoType
SQL_QUALIFIER_US
result must have
PRIVILEGE_DEFINIT
supportsPositionedDelete
SQLGetInfo
fInfoType = SQL_
STATEMENTS; the
have
SQL_PS_POSITIONE
set
supportsPositionedUpdate
SQLGetInfo
fInfoType = SQL_
STATEMENTS; the
have
SQL_PS_POSITIONE
set
supportsSelectForUpdate
SQLGetInfo
fInfoType = SQL_
STATEMENTS; the
have
SQL_PS_SELECT_FO
set
supportsStoredProcedures
SQLGetInfo
fInfoType = SQL_PR
supportsSubqueriesIn
Comparisons
SQLGetInfo
fInfoType = SQL_
the
result
must
SQL_SQ_
COMPARISON bit set
supportsSubqueriesInExists
SQLGetInfo
fInfoType
SQL_
the
result
must
SQL_SQ_EXISTS bit
supportsSubqueriesInIns
SQLGetInfo
fInfoType = SQL_
the
result
must
SQL_SQ_IN bit set
supportsSubqueriesIn
Quantifieds
SQLGetInfo
fInfoType = SQL_
the
result
must
SQL_SQ_
QUANTIFIED bit set
supportsCorrelatedSubqueries SQLGetInfo
fInfoType = SQL_
the
result
must
SQL_SQ_
CORRELATED_SUBQU
supportsUnion
SQLGetInfo
fInfoType
=
SQL
result
must
SQL_U_UNION bit se
supportsUnionAll
SQLGetInfo
fInfoType
=
SQL
result
must
SQL_U_UNION_ALL
supportsOpenCursors
Across Commit
SQLGetInfo
fInfoType
SQL_CURSOR_COMM
BEHAVIOR; the re
SQL_CB_PRESERVE
supportsOpenCursors
Across Rollback
SQLGetInfo
fInfoType
=
S
ROLLBACK_BEHAVIO
must be SQL_CB_PR
supportsOpenStatements
Across Commit
SQLGetInfo
fInfoType
=
S
COMMIT_BEHAVIOR;
must be SQL_CB_
SQL_CB_CLOSE
supportsOpenStatements
Across Rollback
SQLGetInfo
fInfoType
=
S
ROLLBACK_BEHAVIO
must be SQL_CB_
SQL_CB_CLOSE
getMaxBinaryLiteralLength
SQLGetInfo
fInfoType =
LITERAL_LEN
SQL_
getMaxCharLiteralLength
SQLGetInfo
fInfoType
=
LITERAL_LEN
getMaxColumnNameLength
SQLGetInfo
fInfoType = SQL_M
NAME_LEN
getMaxColumnsInGroupBy
SQLGetInfo
fInfoType = SQL_MA
IN_GROUP_BY
getMaxColumnsInIndex
SQLGetInfo
fInfoType = SQL_MA
IN_INDEX
getMaxColumnsInOrderBy
SQLGetInfo
fInfoType = SQL_MA
IN_ORDER_BY
getMaxColumnsInSelect
SQLGetInfo
fInfoType = SQL_MA
IN_SELECT
getMaxColumnsInTable
SQLGetInfo
fInfoType = SQL_MA
IN_TABLE
getMaxConnections
SQLGetInfo
fInfoType
=
CONNECTIONS
getMaxCursorNameLength
SQLGetInfo
fInfoType =
NAME_LEN
getMaxIndexLength
SQLGetInfo
getMaxSchemaNameLength
SQLGetInfo
fInfoType =
NAME_LEN
getMaxProcedureNameLength
SQLGetInfo
fInfoType
=
PROCEDURE_NAME_
getMaxCatalogNameLength
SQLGetInfo
fInfoType
=
QUALIFIER_NAME_L
getMaxRowSize
SQLGetInfo
fInfoType = SQL_MA
doesMaxRowSizeIncludeBlobs
SQLGetInfo
fInfoType
SQL_MAX_ROW_SIZ
INCLUDES_LONG
getMaxStatementLength
SQLGetInfo
fInfoType
SQL
SQL_M
fInfoType
SQL_MAX_INDEX_SI
SQL_
STATEMENT_LEN
getMaxStatements
SQLGetInfo
fInfoType
=
STATEMENTS
getMaxTableNameLength
SQLGetInfo
fInfoType
=
NAME_LEN
getMaxTablesInSelect
SQLGetInfo
fInfoType =
IN_SELECT
getMaxUserNameLength
SQLGetInfo
fInfoType
NAME_LEN
SQL
SQL_
SQ
getDefaultTransactionIsolation SQLGetInfo
fInfoType = SQL_D
ISOLATION
supportsTransactions
SQLGetInfo
fInfoType = SQL_T
the
result
mus
SQL_TC_NONE
SQLGetInfo
fInfoType
SQL_TXN_ISOLATIO
OPTION
SQLGetInfo
fInfoType = SQL_T
the
result
must
SQL_TC_ALL bit set
supportsDataManipulation
TransactionsOnly
SQLGetInfo
fInfoType = SQL_T
the
result
must
SQL_TC_DML bit set
dataDefinitionCauses
Transaction Commit
SQLGetInfo
fInfoType = SQL_T
the
result
must
SQL_TC_DDL_COMM
dataDefinition
IgnoredIn Transactions
SQLGetInfo
fInfoType = SQL_T
the
result
must
SQL_TC_DDL_IGNOR
getProcedures
SQL
Procedures
getProcedureColumns
supportsTransactionIsolation
Level
supportsDataDefinitionAnd
DataManipulationTransactions
getTables
SQLTables
getSchemas
SQLTables
Catalog = , Sch
Table = , TableT
only the TABLE_SCH
returned
getCatalogs
SQLTables
Catalog = %, S
Table = , TableT
only the TABLE_CA
returned
getTableTypes
SQLTables
Catalog = , Schem
= , TableType = %
getColumns
SQLColumns
getColumnPrivileges
SQLColumn
Privileges
Returns a list of
associated
privileg
specified table
getTablePrivileges
SQLTable
Privileges
Returns a list of ta
privileges associate
table
getBestRowIdentifier
SQLSpecial
Columns
fColType = SQL_BES
getVersionColumns
SQLSpecial
Columns
fColType = SQL_ROW
getPrimaryKeys
SQLPrimary
Keys
Returns a list of c
that comprise the p
a table
getImportedKeys
SQLForeign
Keys
PKTableCatalog
PKTableSchema
PKTableName = NUL
getExportedKeys
SQLForeign
Keys
FKTableCatalog
FKTableSchema
FKTableName = NUL
getCrossReference
SQLForeign
Keys
Returns a list of fo
the specified table
SQLGetType
Info
getTypeInfo
fSqlType = SQL_ALL
getIndexInfo
SQLStatistics Returns a list of
statistics about the specified
table and the indexes
associated with the table
Table
5.4
Statement
ODBC
calls.
JDBC
Interface Method
ODBC Call
Comments
close
SQLFreeStmt
fOption = SQL_CLOSE
getMaxFieldSize
SQLGetStmtOption
fOption
SQL_MAX_LENGTH
setMaxFieldSize
SQLSetStmtOption
fOption
SQL_MAX_LENGTH
getMaxRows
SQLGetStmtOption
fOption
SQL_MAX_ROWS
setMaxRows
SQLSetStmtOption
fOption
SQL_MAX_ROWS
fOption
SQL_NOSCAN
setEscapeProcessing SQLSetStmtOption
getQueryTimeout
SQLGetStmtOption fOption
=
SQL_QUERY_TIMEOUT
setQueryTimeout
SQLSetStmtOption fOption
=
SQL_QUERY_TIMEOUT
cancel
SQLCancel
setCursorName
Cancels
processing
statement
on
the
a
statement
execute
SQLExecDirect
getUpdateCount
SQLRowCount
getMoreResults
SQLMoreResults
Determines whether
there are more
results available on
a statement and, if
so, initializes
processing for those
results
Table
5.5
PreparedStatement
ODBC calls. JDBC
Interface Method
ODBC Call
setNull
setBoolean
setByte
setShort
Comments
SQLBindParameter fParamType
=
SQL_PARAM_INPUT;
fSqlType = sqlType
passed as parameter
setInt
setLong
setFloat
setDouble
setNumeric
setString
setBytes
setDate
setTime
setTimestamp
SQLBindParameter fParamType
=
SQL_PARAM_INPUT;
fSqlType is derived
by the type of get
method
setAsciiStream
setUnicodeStream
setBinaryStream
execute
SQLExecute May
return
SQL_NEED_DATA
(because of
setAsciiStream,
setUnicodeStream,
or setBinary
Stream); in this
case, the Bridge
will call
SQLParamData and
SQLPutData until
SQLBindParameter fParamType
=
SQL_PARAM_INPUT,
pcbValue
=
SQL_DATA_AT_EXEC
no more data is
needed
Table
5.6
CallableStatement
ODBC calls. JDBC
Interface
Method
ODBC Call
Comments
S
Q
L
B
i
n
d
P
a
r
a
m
e
t
e
r
registerOutParameter
fParamType =
SQL_PARAM_OUTP
UT; rgbValue is a
buffer that has
been allocated in
Java; when using
the getXXX
methods, this
buffer is used to
retrieve the data
Table
ResultSet
Comments
calls.
Interface
Method
JDBC
next
SQLFetch
close
SqlFreeStmt
Fetches a row
data
from
ResultSet
of
a
fOption
SQL_CLOSE
getString
getBoolean
getByte
getShort
getInt
getLong
getFloat
getDouble
getNumeric
getBytes
getTime
getTimestamp
SQLGetData
fCType is derived by
the
type
of
get
method
SQLGetData
An
InputStream
object is created to
provide a wrapper
around
the
SQLGetData
call;
data is read from
the data source as
needed
getAsciiStream
getUnicodeStream
getBinaryStream
S
Q
L
G
e
t
C
u
r
s
o
r
N
a
m
e
getCursorName
Comments
close
SQLFreeStmt
fOption = SQL_CLOSE
getMaxFieldSize
SQLGetStmtOption
fOption
SQL_MAX_LENGTH
setMaxFieldSize
SQLSetStmtOption
fOption
SQL_MAX_LENGTH
getMaxRows
SQLGetStmtOption
fOption
SQL_MAX_ROWS
setMaxRows
SQLSetStmtOption
fOption
SQL_MAX_ROWS
setEscapeProcessing SQLSetStmtOption
fOption
SQL_NOSCAN
getQueryTimeout
SQLGetStmtOption fOption
=
SQL_QUERY_TIMEOUT
setQueryTimeout
SQLSetStmtOption fOption
=
SQL_QUERY_TIMEOUT
cancel
SQLCancel
Cancels
processing
statement
on
the
a
setCursorName
execute
SQLExecDirect
getUpdateCount
SQLRowCount
getMoreResults
SQLMoreResults
Determines whether
there are more
results available on
a statement and, if
so, initializes
processing for those
results
Table
Comments
PreparedStatement
ODBC calls. JDBC
Interface Method
setNull
SQLBindParameter fParamType
=
SQL_PARAM_INPUT;
fSqlType = sqlType
passed as parameter
setBoolean
setByte
setShort
setInt
setLong
setFloat
setDouble
setNumeric
setString
setBytes
setDate
setTime
setTimestamp
SQLBindParameter fParamType
=
SQL_PARAM_INPUT;
fSqlType is derived
by the type of get
method
setAsciiStream
setUnicodeStream
setBinaryStream
execute
SQLBindParameter fParamType
=
SQL_PARAM_INPUT,
pcbValue
=
SQL_DATA_AT_EXEC
SQLExecute May
return
SQL_NEED_DATA
(because of
setAsciiStream,
setUnicodeStream,
or setBinary
Stream); in this
case, the Bridge
will call
SQLParamData and
SQLPutData until
no more data is
needed
Table
5.6
CallableStatement
ODBC calls. JDBC
Interface
Method
ODBC Call
registerOutParameter
Comments
S
Q
L
B
i
n
d
P
a
r
a
m
e
t
e
r
fParamType =
SQL_PARAM_OUTP
UT; rgbValue is a
buffer that has
been allocated in
Java; when using
the getXXX
methods, this
buffer is used to
retrieve the data
Table
ResultSet
calls.
Interface
Method
5.7
ODBC
JDBC
ODBC Call
Comments
next
SQLFetch
Fetches a row
data
from
ResultSet
of
a
close
SqlFreeStmt
fOption
SQL_CLOSE
SQLGetData
fCType is derived by
getString
getBoolean
getByte
getShort
getInt
getLong
getFloat
getDouble
getNumeric
getBytes
getTime
getTimestamp
the
type
method
of
get
getAsciiStream
getUnicodeStream
getBinaryStream
SQLGetData
An
InputStream
object is created to
provide a wrapper
around
the
SQLGetData
call;
data is read from
the data source as
needed
S
Q
L
G
e
t
C
u
r
s
o
r
N
a
m
e
getCursorName
Many of the standard SQL-92 data types, such as Date, do not have a native
Java equivalent. To overcome this deficiency, you must map SQL data types
into Java. This process involves using JDBC classes to access SQL data
types. In this chapter, well take a look at the classes in the JDBC that are
used to access SQL data types. In addition, well briefly discuss the Object
Relation Model (ORM), an interesting area in database development that
attempts to map relational models into objects.
You need to know how to properly retrieve equivalent Java data typeslike
int, long, and Stringfrom their SQL counterparts and store them in your
database. This can be especially important if you are working with numeric
data (which requires careful handling of decimal precision) and SQL
timestamps (which have a well-defined format). The mechanism for
handling raw binary data is touched on in this chapter, but it is covered in
more detail in Chapter 8.
Mapping SQL Data To Java
Mapping Java data types into SQL is really quite simple. Table 6.1 shows
how Java data types map into equivalent SQL data types. Note that the types
beginning with java.sql. are not elemental data types, but are classes that
have methods for translating the data into usable formats.
Table 6.1 Java data
type
mapping
into
SQL data types. Java SQL Type
Type
string
VARCHAR or LONGVARCHAR
java.sql.Numeric
NUMERIC
boolean
BIT
byte
TINYINT
short
SMALLINT
int
INTEGER
long
BIGINT
float
REAL
double
DOUBLE
byte[]
VARBINARY or LONGVARBINARY
java.sql.Date
DATE
java.sql.Time
TIME
java.sql.Timestamp
T
I
M
E
S
T
A
M
P
The byte[] data type is a byte array of variable size. This data structure is
used to store binary data; binary data is manifest in SQL as VARBINARY
and LONGVARBINARY. These types are used to store images, raw
document files, and so on. To store or retrieve this data from the database,
you would use the stream methods available in the JDBC: setBinaryStream
and getBinaryStream. In Chapter 8, well use these methods to build a
multimedia Java/JDBC application.
Table 6.2 shows the mapping of SQL data types into Java. You will find that
both tables will come in handy when youre attempting to decide which
types need special treatment. You can also use the tables as a quick reference
to make sure that youre properly casting data that you want to store or
retrieve.
Table 6.2 SQL data
type
mapping
into
Java and JDBC. Java SQL Type
Type
CHAR
String
VARCHAR
String
LONGVARCHAR
String
NUMERIC
java.sql.Nueric
DECIMAL
java.sql.Numeric
BIT
boolean
TINYINT
byte
SMALLINT
short
INTEGER
int
BIGINT
long
REAL
float
FLOAT
double
DOUBLE
souble
BINARY
byte[]
VARBINARY
byte[]
LONGBINARY
byte[]
DATE
java.sql.Date
TIME
java.sql.Time
TIMESTAMP
j
a
v
a
.
s
q
l
.
T
i
m
e
s
t
a
m
p
Now that youve seen how these data types translate from Java to SQL and
vice versa, lets look at some of the methods that youll use to retrieve data
from a database. These methods, shown in Table 6.3, are contained in the
ResultSet class, which is the class that is passed back when you invoke a
Statement.executeQuery function. Youll find a complete reference of the
ResultSet class methods in Chapter 12.
The parameters int and String allow you to specify the column you want by
column number or column name.
Table 6.3 A few ResultSet
methods for getting data.
Description
Method
getAsciiStream(String),
getAsciiStream(int)
getBinaryStream(int),
getBinaryStream(String)
getBoolean(int),
getBoolean(String)
getDate(int),
getDate(String)
getObject(int), getObject(String)
R
e
t
u
r
n
s
t
h
e
v
a
l
u
e
o
f
a
c
o
l
u
m
n
a
s
a
J
a
v
a
o
b
j
e
c
t
ResultSetMetaData
One of the most useful classes you can use to retrieve data from a ResultSet
is the ResultSetMetaData class. This class contains methods that allow you
to obtain vital information about the querys result. After a query has been
executed, you can call the ResultSet.getMetaData method to fetch a
ResultSetMetaData object for the resulting data. Table 6.4 shows some of
the methods that you will most likely use. Again, more ResultSetMetaData
methods are listed in Chapter 12.
Table 6.4 Handy methods in the ResultSetMetaData
class. Method
Description getColumnCount() Indicates the number of columns in the
ResultSet getColumnLabel(int) Returns the database-assigned
Label for the column at position int in the ResultSet
getColumnName(int) Returns the columns name (for query
reference) getColumnType(int) Returns the specified columns
SQL type isNullable(int) Tells you if the specified column can
contain NULLs isSearchable(int) Indicates whether the specified
column is searchable via a WHERE clauseUnderstanding The
Object Relation Model
The Object Relation Model (ORM) attempts to fuse object-orientation with
the relational database model. Because many of todays programming
languages, such as Java, are object-oriented, a tighter integration between
the two would provide easier abstraction for developers who program in
object-oriented languages and also are required to program in SQL. Such
an integration would also relieve the necessity of constant translation
between database tables and object-oriented data structures, which can be an
arduous task.
Mapping A Table Into A Java Object
Lets look at a simple example to demonstrate the basics of ORM. Suppose
we create the following table in a database:
First_Name Last_Name Phone_Number Employee_Number
Pratik
Patel
800-555-1212
30122
Karl
Moss
800-555-1213
30124
Keith
Weiskamp
800-555-1214
09249
Ron
Pronk
800-555-1215
10464
You can easily map this table into a Java object. Heres the Java code you
would write to encapsulate the data contained in the table:
class Employee {
int Key;
String First_Name;
String Last_Name;
String Phone_Number;
int Employee_Number;
Key=Employee_Number;
}
To retrieve this table from the database into Java, we simply assign the
respective columns to the Employee object we created previously for each
row we retrieve, as shown here:
Employee emp_object = new Employee();
emp_object.First_Name=
resultset.getString("First_Name");
emp_object.Last_Name=
resultset.getString("Last_Name");
emp_object.Phone_Number=resultset.getString("Phone_
Number");
emp_object.Employee_Number=resultset.getInt("Employ
ee_Number");
With a larger database model (with links between tables), a number of
problems can arise, including scalability due to multiple JOINs in the data
model and cross-linking of table keys. Fortunately, a number of products are
already available that allow you to create these kinds of objectoriented/relational bridges. Moreover, there are several solutions being
developed to work specifically with Java.
Ive given you an idea of what ORM is all about. If you would like to
investigate this topic further, check out The Coriolis Group Web site
(http://www.coriolis.com/jdbc-book) for links to ORM vendors and some
really informative ORM documents. The ODMG (Object Database
Management Group) is a consortium that is working on a revised standard
for object database technology and the incorporation of this concept into
Chapter
Working With Query Results
So far, weve been concentrating on how to use the classes in the JDBC to
perform SQL queries. Thats great, but now we have to do something with
the data weve retrieved. The end user of your JDBC applets or applications
will want to see more than just rows and rows of data. In this chapter, well
learn how to package the raw table data that is returned after a successful
SQL query into a Java object, and then how to use this packaged data to
produce easy-to-read graphs.
The first issue well look at is creating a Java object to store the results of a
query. This object will provide a usable interface to the actual query results
so they can be plugged into a Java graphics library. Well create a simple
data structure to hold the column results in a formatted way so that we can
easily parse them and prepare them for display. Second, well look at taking
these results in the Java object and setting up the necessary code to plug the
data into a pie chart and bar graph Java package.
In the next chapter, well go one step further and work with BLOB data
types (like images). Between these chapters, I will be providing plenty of
examples, complete with code, to help you work up your own JDBC
programs. At the very least, these chapters will give you some ideas for
dealing with raw table data and displaying it in an effective manner.
A Basic Java Object For Storing Results
Although the JDBC provides you with the ResultSet class to get the data
from an SQL query, you will still need to store and format within your
program the results for display. The smart way to do this is in a re-usable
fashion (by implementing a generic object or class) which allows you to reuse the same class you develop to retrieve data from a query in any of your
JDBC programs. The code snippet in Listing 7.1 is a method that will keep
your results in a Java object until you are ready to parse and display it.
Lets begin by defining the data we will be getting from the source, and
determining how we want to structure it within our Java applet. Remember
that the ResultSet allows us to retrieve data in a row-by-row, column-bycolumn fashion; it simply gives us sequential access to the resulting table
data. Table 7.1 shows the example table we will be using in this chapter.
Table
7.1
Example
table.
first_name
emp_no
last_name
salary
01234
Pratik
Patel
8000
1235
Karl
Moss
23000
0002
Keith
Weiskamp
90000
0045
Ron
Pronk
59999
530000067
David Friedel
The optimal way to store this data in our Java program is to put each
columns data in its own structure and then link the different columns by
using an index; this will allow us to keep the columnar relationship of the
table intact. We will put each columns data in an array. To simplify matters,
well use the getString method, which translates the different data types
returned by a query into a String type. Then, well take the data in a column
and delimit the instances with commas. Well use an array of String to do
this; each place in the array will represent a different column. The data
object we will create is shown here:
table_data[0] => 01234,1235,0002,0045,0067
table_data[1] => Pratik,Karl,Keith,Ron,David
table_data[2] => Patel,Moss,Weiskamp,Pronk,Friedel
exists.
more=rs.next();
// Get the next row of the result if it
}
stmt.close();
// All done. Close the statement object.
}
catch( Exception e ) {
e.printStackTrace();
System.out.println(e.getMessage());
}
return column;
// Finally, return the entire column[] array.
}
Showing The Results
Now that we have the data nicely packaged into our Java object, how do we
show it? The code in Listing 7.2 dumps the data in the object to the screen.
We simply loop through the array and print the data.
Listing 7.2 Code to print retrieved data to the console.
public void ShowFormattedData(String[] columnD ) {
int i;
for ( i=0; i< columnD.length; i++) {
System.out.println(columnD[i]+"\n");
}
}
Charting Your Data
Now that weve covered the preliminaries, we are ready to get to the fun
stuff! Instead of creating a package that has graphics utilities, were going to
use the NetCharts library, which is stored on the accompanying CD-ROM.
The package on the CD is only an evaluation copy. Stop by
http://www.netcharts.com to pick up the latest version (and some helpful
documentation). Well use the table in Table 7.1 and a bar chart to display
the salary information for our fictional company. Figure 7.1 shows the applet
that is generated from the code in Listing 7.3. Remember, the code for this
example can be found on the accompanying CD-ROM, or at The Coriolis
Group Web site at http://www.coriolis.com/jdbc-book.
Figure 7.1 The bar chart applet.
ShowChartData(String
Data1,
String
bar.start();
// Initialize it, and start it
running.
// Below is where we load the
parameters for the chart.
// See the documentation at the
NetCharts Web site, or
// the CD-ROM for details.
bar.loadParams(
"Header
Information');"+
red);"+
";"+
";"+
'TimesRoman', 14, 0,
= ('Salary
"DataSets
= ('Salary',
"DataSet1
= "+ Data1 +
"BarLabels
= "+ Data2 +
"GraphLayout= HORIZONTAL;"+
"BottomAxis = (black,
0,100000)"
);
put
bar.loadParams ("Update");
// Tell the bar chart class we've
// some new parameters in.
} catch (Exception e) {
System.out.println
(e.getMessage());
}
} // More to come following some comments
The bar chart class from the NetCharts package uses a method to load the
values for the chart. We have to define the labels and corresponding values,
but this is generally straightforward. Because our data is formatted in a
comma-delimited fashion, we dont have to parse the data again to prepare it
for use. In the next example (the pie chart example), we do have to parse it
to put it in the proper format for the charting class to recognize it. Listing 7.4
picks up the code where we left off in Listing 7.3.
Listing 7.4 Dynamically generating a bar chart from a database queryPart
II.
public String[] getData( String QueryLine ) {
int columns, pos;
String column[]=new String[4];
boolean more;
try {
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(QueryLine);
columns=(rs.getMetaData()).getColumnCount();
column = new String[columns];
// Initialize the columns to be blank
for(pos=1; pos<=columns; pos++) {
column[pos-1]="";
}
more=rs.next();
while(more) {
for (pos=1; pos<=columns; pos++) {
column[pos-1]+=(rs.getString(pos));
}
more=rs.next();
for (pos=1; pos<=columns; pos++) {
if(more) {
column[pos-1]+=(",");
}
}
stmt.close();
}
catch( Exception e ) {
e.printStackTrace();
System.out.println(e.getMessage());
}
return column;
}
Thats it! Weve successfully queried a database, formatted the resulting
data, and created a bar chart to present a visual representation of the results.
Listing 7.5 shows the generation of a pie chart, and Figure 7.2 shows the pie
chart applet.
Figure 7.2 The pie chart applet.
ConnectToDB();
add("North", OutputField);
String columnData[] = getData("select
Cost");
from
ShowFormattedData(columnData);
ShowChartData(columnData[1],columnData[0]);
add("Center", pie);
}
public void ConnectToDB() {
try {
new imaginary.sql.iMsqlDriver();
con
=
DriverManager.getConnection(url,
"prpatel", "");
}
catch( Exception e ) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
public void ShowFormattedData(String[] columnD ) {
int i;
for ( i=0; i< columnD.length; i++) {
OutputField.appendText(columnD[i]+"\n");
}
}
public void ShowChartData(String dataNumber, String
dataLabel) {
pie chart
// class(NFPieChartAPP) at the
pie.loadParams(
"Background=(black, RAISED, 4);"+
"Header=('Cost Information
(millions)');"+
"LabelPos=0.7;"+
"DwellLabel
= ('', black,
'TimesRoman', 16);"+
"Legend
= ('Legend',
black);"+
"LegendBox
= (white, RAISED,
4);"+
"Slices=(12.3, blue, 'Marketing',
cyan), (4.6,
antiquewhite, 'Sales'), (40.1,
aqua, 'Production'),
(18.4, aquamarine, 'Support');");
for the pie chart,
which we generated
labels
sent it new
// parameters to display.
} catch (Exception e) {
System.out.println
(e.getMessage());
}
}
// Below is the same as before except for the new
ColorGenerator class
// that we needed to produce distinct colors.
public String[] getData( String QueryLine ) {
int columns, pos;
String column[]=new String[4];
boolean more;
try {
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(QueryLine);
columns=(rs.getMetaData()).getColumnCount();
column = new String[columns];
// Initialize the columns to be blank
for(pos=1; pos<=columns; pos++) {
column[pos-1]="";
}
more=rs.next();
{
column[pos-1]+=(rs.getString(pos));
more=rs.next();
}
stmt.close();
// con.close();
}
catch( Exception e ) {
e.printStackTrace();
System.out.println(e.getMessage());
}
return column;
}
public void destroy() {
}
}
try {con.close();}
catch( Exception e ) {
e.printStackTrace();
System.out.println(e.getMessage());
}
class ColorGenerator {
// This class is needed to produce colors that the
pie chart can use to
// color the slices properly. They are taken from
the NetCharts color
// class, NFColor.
public ColorGenerator() {
}
int color_count = -1;
// Keep a running count of the colors we have used.
We'll simply index
Chapter
The IconStore Multimedia JDBC Application
In the previous chapter, we learned how to process query results with JDBC.
In this chapter, well take these query results and put them to use in a
multimedia application. The application well be developing, IconStore, will
connect to a database, query for image data stored in database tables, and
display the images on a canvas. Its all very simple, and it puts the JDBC to
good use by building a dynamic application totally driven by data stored in
tables.
IconStore Requirements
The IconStore application will utilize
ICONCATEGORY and ICONSTORE. The
contains information about image categories,
printers, sports, and tools. The ICONSTORE
about each image. Tables 8.1 and 8.2 show the
data structures.
Note that the CATEGORY column in the ICONSTORE is a foreign key into
the ICONCATEGORY table. If the category ID for sports is 1, you can
obtain a result set containing all of the sports images by using this statement:
SELECT ID, DESCRIPTION, ICON FROM ICONSTORE WHERE
CATEGORY = 1
INTEGER
Category
ID
Table
8.1
The
ICONCATEGORY
SQL Type
table.
Column
Name
Description
DESCRIPTION CATEGORY
VARCHAR
Description of the
image category
Table
8.2
The SQL Type
ICONSTORE table.
Column Name
Description
ID
INTEGER
Image ID
DESCRIPTION
VARCHAR
Description of the
image
CATEGORY
INTEGER
Category ID
ICON
VARBINARY
Binary image
Now, lets take a look at whats going on in the application:
An Icons menu, which is dynamically created by
the ICONCATEGORY table, contains each of the image
categories as an option. The user can select an
image category from this menu to display the proper
list of image descriptions in a list box. The
ICONSTORE table is used to dynamically build the
list.
The user can select an image description from the
list box to display the corresponding image.
Once an image has been displayed, the user can
select the Save As menu option to save the image to
disk.
As you can see, IconStore will not be too complicated, but it will serve as a
very good foundation for developing database-driven applications.
Building The Database
Now that weve established the applications requirements, we need to build
the underlying database. Well look at a simple JDBC application to
accomplish this, although it may be created by any number of methods.
Listing 8.1 shows the BuildDB.java source code. This application uses the
SimpleText JDBC driver (covered in great detail in Chapter 10) to create the
ICONCATEGORY and ICONSTORE tables, but any JDBC driver can be
used in its place.
Listing 8.1 Building the IconStore database.
import java.sql.*;
import java.io.*;
class BuildDB {
//
// main
//
public static void main(String args[]) {
try {
// Create an instance of the driver
java.sql.Driver d = (java.sql.Driver)
Class.forName (
"jdbc.SimpleText.SimpleTextDriv
er").newInstance();
// Properties for the driver
java.util.Properties
java.util.Properties();
prop
new
}
catch (SQLException ex) {
System.out.println("\n*** SQLException
caught ***\n");
while (ex != null) {
System.out.println("SQLState: " +
ex.getSQLState());
System.out.println("Message:
" +
ex.getMessage());
System.out.println("Vendor:
" +
ex.getErrorCode());
ex = ex.getNextException ();
}
System.out.println("");
}
catch (java.lang.Exception ex) {
ex.printStackTrace ();
}
}
//
// BuildCategory
// Given a connection object and a table name,
create the IconStore
// category database table.
//
protected static void buildCategory(
Connection con,
String table)
throws SQLException
{
System.out.println("Creating " + table);
Statement stmt = con.createStatement();
// Create the SQL statement
String sql = "create table " + table +
" (CATEGORY NUMBER, DESCRIPTION
VARCHAR)";
// Create the table
stmt.executeUpdate(sql);
// Create some data using the statement
+ table + "
+ table + "
+ table + "
table
name,
int category;
int id = 1;
// Add the printer icons
category = 1;
addIconRecord(ps, id++, "Printer 1", category,
"printers/print.gif");
addIconRecord(ps, id++, "Printer 2", category,
"printers/print0.gif");
// Add the sports icons
category = 2;
addIconRecord(ps, id++, "Archery", category,
"sports/
sport_archery.gif");
addIconRecord(ps, id++, "Baseball", category,
"sports/
sport_baseball.gif");
// Add the tools
category = 3;
addIconRecord(ps, id++, "Toolbox 1", category,
"tools/toolbox.gif");
addIconRecord(ps, id++, "Toolbox 2", category,
"tools/toolbox1.gif");
ps.close();
}
//
// AddIconRecord
// Helper method to add an IconStore record. A
PreparedStatement is
// provided to which this method binds input
parameters. Returns
// true if the record was added.
//
// Now execute
int rows = ps.executeUpdate();
return (rows == 0) ? false : true;
}
The BuildDB application connects to the SimpleText JDBC driver, creates
the ICONCATEGORY table, adds some image category records, creates the
ICONSTORE table, and adds some image records. Note that when the image
records are added to the ICONSTORE table, a PreparedStatement object is
used. Well take a closer look at PreparedStatements in Chapter 11; for
now, just realize that this is an efficient way to execute the same SQL
statement multiple times with different parameters values. Also note that the
image data is coming out of GIF files stored on disk. An InputStream is
created using these files, which is then passed to the JDBC driver for input.
The JDBC driver reads the InputStream and stores the binary data in the
database table. Simple, isnt it? Now that weve created the database, we can
start writing our IconStore application.
Application Essentials
The source code for the IconStore application is shown throughout the rest
of this chapter, broken across the various sections. As always, you can pick
up a complete copy of the source code on the CD-ROM. Remember, you
need to have the SimpleText JDBC driver installed before using the
IconStore application. See Chapter 3, if you have trouble getting the
application to run.
Writing The main Method
Every JDBC application must have an entry point, or a place at which to
start execution. This entry point is the main method, which is shown in
Listing 8.2. For the IconStore application, main simply processes any
command line arguments, creates a new instance of the IconStore class
(which extends Frame, a top-level window class), and sets up the window
attributes. The IconStore application accepts one command line argument:
the location of the IconStore database. The default location is /IconStore.
Listing 8.2 IconStore main method.
import
import
import
import
java.awt.*;
java.io.*;
java.util.*;
java.sql.*;
if (myHome.endsWith("/") ||
myHome.endsWith("\\")) {
myHome = myHome.substring(0,
myHome.length() - 1);
frame.pack();
frame.resize(300, 400);
frame.show();
which these files are kept. As I mentioned in the previous section, the default
location is /IconStore (the IconStore directory of your current drive), but this
can be overridden to be any location.
If successful, a JDBC Connection object is returned to the caller. If there is
any reason a database connection cannot be established, the pertinent
information will be displayed and the application will be terminated.
ResultSet rs = stmt.executeQuery(
"SELECT DESCRIPTION,CATEGORY FROM
ICONCATEGORY");
return table;
The flow of this routine is very basic, and well be using it throughout our
IconStore application. First, we create a Statement object; then, we submit
an SQL statement to query the database; next, we process each of the
resulting rows; and finally, we close the Statement. Note that a Hashtable
object containing a list of all the categories is returned; the category
description is the key and the category ID is the element. In this way, we can
(category
setMenuBar(menuBar);
Notice that the Hashtable containing a list of the image categories is used to
create our menu. The only way to examine the contents of a Hashtable
without knowing each of the keys is to create an Enumeration object,
which can be used to get the next key value of the Hashtable. Figure 8.1
shows our database-driven menu.
Figure 8.1 The IconStore menu.
Creating The Lists
Next on our agenda: creating the list boxes containing the image
descriptions. Well create a list for each category, so when the user selects a
category from the Icons menu, only a list of the images for the selected
category will be shown. Well use a CardLayout to do this, which is a nifty
way to set up any number of lists and switch between them effortlessly. For
each of the categories that we read from the ICONCATEGORY table, we
also read each of the image descriptions for that category from the
ICONSTORE table and store those descriptions in a Hashtable for use later.
At the same time, we add each description to a list for the category. Listing
8.6 shows the code used to read the ICONSTORE table.
results
ResultSet rs = stmt.executeQuery(
"SELECT DESCRIPTION,ID FROM
ICONSTORE WHERE CATEGORY=" +
category);
// Loop while more rows exist
while (rs.next()) {
desc = rs.getString(1);
Hashtable
}
// Close the statement
stmt.close();
}
catch (SQLException ex) {
// An SQLException was generated. Dump
the exception contents.
// Note that there may be multiple
SQLExceptions chained
// together.
System.out.println("\n*** SQLException
caught ***\n");
while (ex != null) {
System.out.println("SQLState: " +
ex.getSQLState());
System.out.println("Message:
" +
ex.getMessage());
System.out.println("Vendor:
" +
ex.getErrorCode());
ex = ex.getNextException();
}
System.exit(1);
}
}
return table;
default
if (listNo == 0) {
currentList = desc;
}
of 20
It is very important to note how the CardLayout has been set up. Each of
the lists is added to the CardLayout with a description as a title, which, in
our case, is the name of the category. When the user selects a category from
the Icons menu, we can use the category description to set the new
CardLayout list. Figure 8.2 shows the initial screen after loading the
database tables.
Figure 8.2 The IconStore main screen.
Handling Events
There are two types of events that we need to be aware of in the IconStore
application: selecting menu options and clicking on the image list to select
an icon. As with the Interactive SQL applet we discussed in Chapter 4, the
event handling code is contained in the handleEvent method, as shown in
Listing 8.8.
Listing 8.8 IconStore handleEvent.
//
// handleEvent
// Handle an event by the user.
//
public boolean handleEvent(
Event evt)
{
switch (evt.id) {
case Event.ACTION_EVENT:
occurred
if (currentFile != null) {
fileDialog.setFile("");
fileDialog.pack();
fileDialog.show();
String saveFile =
fileDialog.getFile();
if (saveFile == null) {
return true;
}
// If this is a new file, it
if (saveFile.endsWith(".*.*"))
saveFile
saveFile.substring(0,
saveFile.length() -
4);
// If no extension is
if (saveFile.indexOf(".") <
0) {
}
}
if successful.
saveFile += ".gif";
(currentFile, saveFile);
}
return true;
}
// 'Exit' - Exit the application
else if (selection.equals("Exit")) {
// If there was an image file, delete
it
if (currentFile != null) {
File(currentFile)).delete();
}
}
(new
System.exit(0);
return false;
When the user selects Exit from the menu, the temporary image file (which
is discussed later) is deleted from disk, and the application is terminated.
This is the perfect time to close the Connection that was in use. I
purposefully omitted this step to illustrate a point: The JDBC specification
states that all close operations are purely optional. It is up to the JDBC
driver to perform any necessary clean-up in the finalize methods for each
object. I strongly recommend, though, that all JDBC applications close
objects when it is proper to do so.
Listing 8.9 Loading and displaying the selected image.
//
// displayIcon
// Display the currently selected icon.
//
public void displayIcon(
Connection con)
{
// Get the proper list element
int n = getCategoryElement(currentList);
// Get the item selected
String item = lists[n].getSelectedItem();
// Only continue if an item was selected
if (item == null) {
return;
}
// Get the ID
String id = (String) iconDesc[n].get(item);
try {
// Create a Statement object
Statement stmt = con.createStatement();
// Execute the query and process the
results
ResultSet rs = stmt.executeQuery(
"SELECT ICON FROM ICONSTORE WHERE
ID=" + id);
not found
if (inputStream == null) {
stmt.close();
return;
}
// Here's where things get ugly. Currently,
there is no way
// to display an image from an InputStream.
We'll create a
// new file from the InputStream and load
the Image from the
// newly created file. We need to create a
unique name for
// each icon; the Java VM caches the image
file.
String name = myHome + "/IconStoreImageFile"
+ id + ".gif";
FileOutputStream outputStream
FileOutputStream(name);
// Write the data
int bytes = 0;
byte b[] = new byte[1024];
new
while (true) {
// Read from the input. The number of
bytes read is returned.
bytes = inputStream.read(b);
if (bytes == -1) {
break;
// Write the data
outputStream.write(b, 0, bytes);
outputStream.close();
inputStream.close();
// Close the statement
stmt.close();
// Now, display the icon
loadFile(name);
// If there was an image file, delete it
if (currentFile != null) {
if (!currentFile.equals(name)) {
(new File(currentFile)).delete();
}
}
// Save our current file name
currentFile = name;
}
catch (SQLException ex) {
// An SQLException was generated. Dump the
exception contents.
// Note that there may be multiple
SQLExceptions chained
// together.
System.out.println("\n*** SQLException
caught ***\n");
while (ex != null) {
System.out.println("SQLState: " +
ex.getSQLState());
ex.getMessage());
System.out.println("Message:
System.out.println("Vendor:
ex.getErrorCode());
ex = ex.getNextException();
}
System.exit(1);
}
catch (java.lang.Exception ex) {
ex.printStackTrace();
System.exit(1);
}
" +
" +
}
Notice that each time an image is selected from the list, the image is read
from the database. It could be very costly in terms of memory resources to
save all of the images, so well just get the image from the database when
needed. When the user selects an item from the list, we can get the image
description. This description is used to get the icon ID from the image
Hashtable. For the most part, we follow the same steps we have seen
several times before in getting results from a database. Unfortunately, weve
had to use a very nasty workaround here. The image is retrieved from the
database as a binary InputStream, and it is from this InputStream that we
need to draw the image on our canvas. This technique seems like it should
be a simple matter, but it turns out to be impossible as of the writing of this
book. To get around this problem, the IconStore application uses the
InputStream to create a temporary file on disk, from which an image can be
loaded and drawn on the canvas. Hopefully, a method to draw images from
an InputStream will be part of Java in the future.
Figure 8.3 shows the IconStore screen after the user has selected an image
from the initial category list. Figure 8.4 shows the IconStore screen after the
user has changed the category (from the Icons menu) to sports and has made
a selection.
Figure 8.3 Selecting on image from the category list box.
FileInputStream in
FileInputStream(source);
FileOutputStream out
FileOutputStream(target);
new
new
int bytes;
byte b[] = new byte[1024];
// Read chunks from the input stream and
write to the output
// stream.
while (true) {
bytes = in.read(b);
if (bytes == -1) {
break;
}
out.write(b, 0, bytes);
}
in.close();
out.close();
rc = true;
}
}
}
Figure 8.5 shows the IconStore screen after the user has selected the Save As
menu option.
Figure 8.5 The IconStore Save As dialog box.
Thats all there is to it.
Summary
Lets recap the important details that we have covered in this chapter:
Creating a basic GUI Java application
Opening a connection to a data source
Using database data to create dynamic
components (menus and lists)
Handling user events
Handling JDBC InputStreams
GUI
If you would like to take the IconStore application further, one obvious
enhancement would be to allow the user to add images to the
database. Ill leave this as an exercise for you. Chapter 9
Java And Database Security
Security is at the top of the list of concerns for people sharing databases on
the Internet and large intranets. In this chapter, well have a look at security
in Java and how Java/JDBC security relates to database security. Well also
have a peek at the new security features planned for Java, which will
incorporate encryption and authentication into the JDBC.
Database Server Security
The first issue Id like to tackle, and the first one you need to consider, is the
security of your actual database server. If you are allowing direct
connections to your database server from your Java/JDBC programs, you
need to prepare for a number of potential security pitfalls. Although security
breaks are few and far between, I advise you to cover all the angles so you
dont get caught off-guard.
Rooting Out The Packet Sniffers
Information is sent over networks in packets, and packet sniffing happens
because a computers network adapter is configured to read all of the
packets that are sent over the network, instead of just packets meant for that
computer. Therefore, anyone with access to a computer attached to your
LAN can check out all transactions as they occur. Of course, a well-managed
network and users you can trust are the best methods of preventing an inside
job. Unfortunately, you must also consider another possibility: the outside
threat. The possibility that someone from outside your LAN might break
into a computer inside your LAN is another issue altogether; you must make
sure that the other computers on your LAN are properly secured. To prevent
such a situation, a firewall is often the best remedy. Though not completely
foolproof, it does not allow indiscriminate access to any computers that are
behind the firewall from outside. There are several good books on basic
Internet security, and this books Website contains a list of URLs that
highlight several books on firewalls.
Packet sniffing doesnt necessarily involve only your local network; it can
occur on the route the packet takes from the remote client machine
somewhere on the Internet to your server machine. Along one of the many
hops a packet takes as it travels across the Internet, a hacker who has
gained entry into one of these hop points could be monitoring the packets
sent to and from your server. Although this is a remote possibility, its still a
possibility. One solution is to limit the IP addresses from which connections
to the database server can be made. However, IP authorization isnt
bulletproof eitherIP spoofing is a workaround for this method. For more
information on these basic security issues, please see this books Web site
for references to security material.
Web Server CGI Holes
If you only allow local direct access to your database server via pre-written
software, like CGI scripts run from Web pages, youll still find yourself with
a possible security hole. Some folks with too much time on their hands take
browsers Java interpreters, and will also be linked in heavily with the
security features of the Web browser itself. At the time this manuscript was
written, however, these APIs were still under construction.
Applet Security: Can I Trust You?
As weve seen, setting up safe connections is quite possible. However,
applet security is an entirely different issue. This aspect of security, where an
applet that has been downloaded to your computer is running in your Web
browser, has been under scrutiny since Java-enabled Web browsers
appeared.
To account for these tight security restrictions, the Java Commerce API
addresses easing security if the applet comes from a trusted source. This
means that if the Web browser recognizes as genuine the certification of the
Web page, applets on the page may also be considered certified. To obtain
such a status, you must apply for certification from the proper authority.
When you receive certification, simply attach it to applets that are served
from your Web site. The Commerce and Security APIs allow for the fetching
of trusted applets, so if the user uses a Java interpreter that incorporates the
Java Commerce API and Security API, you (the developer) can serve applets
that can connect to an application server or database server running on a
different machine than the Web server. In fact, you can even attach to
different database servers simultaneously if necessary. In addition, this
approach may allow the applet to save the contents of a database session on
the users disk, or read data from the users disk to load previous session
data.
The exact security restrictions of trusted applets are not set in stone, and they
may differ depending on the Web browser the applet is run on. Also, the Java
Commerce and Security specifications and related APIs have not been
finalized as of the writing of this book, so much may change from the
preliminary details of the security scheme by the time the APIs are released
and implemented.
Summary
Security in data transactions is a top priority in the Internet community. In
this chapter, weve discussed possible security holes and techniques to sew
them up. We also took a look at Javasofts approach to easing security
restrictions for applets that come from a certified trusted source.
In the next chapter, we jump back into the meat of the JDBC when we
explore writing JDBC drivers. Well explore the heart of the JDBCs
implementation details, and well also develop a real JDBC driver that can
serve as the basis for drivers you write in the future.
Chapter
Writing Database Drivers
10
Weve covered a lot of territory so far in this book. Now we can put some of
your newly gained knowledge to use. In this chapter, we will explore what it
takes to develop a JDBC driver. In doing so, we will also touch on some of
the finer points of the JDBC specification. Throughout this chapter, I will
use excerpts from the SimpleText JDBC driver that is included on the CDROM. This driver allows you to manipulate simple text files; you will be
able to create and drop files, as well as insert and select data within a file.
The SimpleText driver is not fully JDBC-compliant, but it provides a strong
starting point for developing a driver. Well cover what the JDBC
components provide, how to implement the JDBC API interfaces, how to
write native code to bridge to an existing non-Java API, some finer points of
driver writing, and the major JDBC API interfaces that must be
implemented.
The JDBC Driver Project: SimpleText
The SimpleText JDBC driver is just thata JDBC driver that manipulates
simple text files, with a few added twists. It is not a full-blown relational
database system, so I would not recommend attempting to use it as one. If
you are looking for a good way to prototype a system, or need a very
lightweight database system to drive a simplistic application or applet, then
SimpleText is for you. More importantly, though, the SimpleText driver can
serve as a starting point for your own JDBC driver. Before continuing, lets
take a look at the SimpleText driver specifications.
SimpleText SQL Grammar
The SimpleText JDBC driver supports a very limited SQL grammar. This is
one reason that the driver is not JDBC compliant; a JDBC-compliant driver
must support ANSI92 entry level SQL grammar. The following SQL
statements define the base SimpleText grammar:
create-table-statement ::= CREATE TABLE table-name
element [, column-element]...)
( column-
insert-value]...)
(insert-value [,
}
catch (SQLException ex) {
// If an SQLException is thrown, we'll end up
here. Output the error
// message, SQLstate, and vendor code.
System.out.println("A
SQLException
was
caught!");
System.out.println("Message:
"
+
ex.getMessage());
System.out.println("SQLState:
"
+
ex.getSQLState());
System.out.println("Vendor Code: " +
ex.getErrorCode());
}
An SQLWarning is similar to an SQLException (it extends
SQLException). The main difference is in semantics. If an SQLException
is thrown, it is considered to be a critical error (one that needs attention). If
an SQLWarning is thrown, it is considered to be a non-critical error (a
warning or informational message). For this reason, JDBC treats
SQLWarnings much differently than SQLExceptions. SQLExceptions are
thrown just like any other type of exception; SQLWarnings are not thrown,
but put on a list of warnings on an owning object type (for instance,
Connection, Statement, or ResultSet, which well cover later). Because
they are put on a list, it is up to the application to poll for warnings after the
completion of an operation. Listing 10.1 shows a method that accepts an
SQLWarning and places it on a list.
Listing 10.1 Placing an SQL Warning on a list.
//------------------------------------------------------------------------// setWarning
// Sets the given SQLWarning in the warning chain.
If null, the
// chain is reset. The local attribute lastWarning
is used
// as the head of the chain.
//-------------------------------------------------------------- --------protected void setWarning(
SQLWarning warning)
Now well call the method that puts two SQLWarnings on our warning
stack, then poll for the warning using the JDBC method getWarnings, as
shown in Listing 10.3.
Listing 10.3 Polling for warnings.
// Call fooBar to create a warning chain
fooBar();
// Now, poll for the warning chain. We'll simply
dump any warning
// messages to standard output.
SQLWarning chain = getWarnings();
if (chain
!= null) {
System.out.println("Warning(s):");
"
}
DataTruncation objects work in the same manner as SQLWarnings. A
DataTruncation object indicates that a data value that was being read or
written was truncated, resulting in a loss of data. The DataTruncation class
has attributes that can be set to specify the column or parameter number,
whether a truncation occurred on a read or a write, the size of the data that
should have been transferred, and the number of bytes that were actually
transferred. We can modify our code from Listing 10.2 to include the
handling of DataTruncation objects, as shown in Listing 10.4.
Listing 10.4 Creating dDataTruncation warnings.
//----------------------------------------------------------------------// fooBar
// Do nothing but put two SQLWarnings on our local
// warning stack (lastWarning) and a DataTruncation
// warning.
//----------------------------------------------------------------------protected void fooBar()
{
// First step should always be to clear the
stack. If a warning
// is lingering, it will be discarded. It is
up to the application to
// check and clear the stack.
setWarning(null);
// Now create our warnings
setWarning(new SQLWarning("Warning 1"));
setWarning(new SQLWarning("Warning 2"));
// And create a DataTruncation indicating that
a truncation
// occurred on column 1, 1000 bytes were
requested to
// read, and only 999 bytes were read.
setWarning(new DataTruncation(1, false, true,
1000, 999);
}
Listing 10.5 shows the modified code to handle the DataTruncation.
Listing 10.5 Processing DataTruncation warnings.
// Call fooBar to create a warning chain
fooBar();
// Now, poll for the warning chain. We'll simply
dump any warning
// messages to standard output.
SQLWarning chain = getWarnings();
if (chain
!= null) {
System.out.println("Warning(s):");
exist
while (chain != null) {
// The only way we can tell if this warning
is a DataTruncation
// is to attempt to cast it. This may
fail, indicating that
// it is just an SQLWarning.
try {
DataTruncation trunc =
(DataTruncation) chain;
System.out.println("Data Truncation
on column: " +
trunc.getIndex());
}
catch (Exception ex) {
System.out.println("Message: " +
chain.getMessage());
}
// Advance to the next warning in the
chain. null will be
// returned if no more entries exist.
chain = chain.getNextWarning();
}
JDBC Data Types
The JDBC specification provides definitions for all of the SQL data types
that can be supported by a JDBC driver. Only a few of these data types may
be natively supported by a given database system, which is why data
coercion becomes such a vital service (well discuss data coercion a little
later in this chapter). The data types are defined in Types.class:
public class Types
{
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
final
final
final
final
final
final
final
final
final
final
final
final
final
final
final
final
final
final
final
final
static
static
static
static
static
static
static
static
static
static
static
static
static
static
static
static
static
static
static
static
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int
int
BIT = -7;
TINYINT = -6;
SMALLINT = 5;
INTEGER = 4;
BIGINT = -5;
FLOAT = 6;
REAL = 7;
DOUBLE = 8;
NUMERIC = 2;
DECIMAL = 3;
CHAR = 1;
VARCHAR = 12;
LONGVARCHAR = -1;
DATE = 91;
TIME = 92;
TIMESTAMP = 93;
BINARY = -2;
VARBINARY = -3;
LONGVARBINARY = -4;
OTHER = 1111;
}
At a minimum, a JDBC driver must support one (if not all) of the character
data types (CHAR, VARCHAR, and LONGVARCHAR). A driver may
also support driver-specific data types (OTHER) which can only be
accessed in a JDBC application as an Object. In other words, you can get
data as some type of object and put it back into a database as that same type
of object, but the application has no idea what type of data is actually
contained within. Lets take a look at each of the data types more closely.
Character Data: CHAR, VARCHAR, And LONGVARCHAR
CHAR, VARCHAR, and LONGVARCHAR data types are used to express
character data. These data types are represented in JDBC as Java String
objects. Data of type CHAR is represented as a fixed-length String, and
may include some padding spaces to ensure that it is the proper length. If
data is being written to a database, the driver must ensure that the data is
properly padded. Data of type VARCHAR is represented as a variablelength String, and is trimmed to the actual length of the data.
LONGVARCHAR data can be either a variable-length String or returned
by the driver as a Java InputStream, allowing the data to be read in chunks
of whatever size the application desires.
Exact Numeric Data: NUMERIC And DECIMAL
The NUMERIC and DECIMAL data types are used to express signed,
exact numeric values with a fixed number of decimal places. These data
types are often used to represent currency values. NUMERIC and
DECIMAL data are both represented in JDBC as Numeric objects. The
Numeric class is new with JDBC, and well be discussing it shortly.
Binary Data: BINARY, VARBINARY, And LONGVARBINARY
The BINARY, VARBINARY, and LONGVARBINARY data types are
used to express binary (non-character) data. These data types are represented
in JDBC as Java byte arrays. Data of type BINARY is represented as a
fixed-length byte array, and may include some padding zeros to ensure that it
is the proper length. If data is being written to a database, the driver must
ensure that the data is properly padded. Data of type VARBINARY is
represented as a variable-length byte array, and is trimmed to the actual
length of the data. LONGVARBINARY data can either be a variable-length
byte array or returned by the driver as a Java InputStream, allowing the
data to be read in chunks of whatever size the application desires.
Boolean Data: BIT
The BIT data type is used to represent a boolean valueeither true or false
and is represented in JDBC as a Boolean object or boolean data type.
Integer Data: TINYINT, SMALLINT, INTEGER, And BIGINT
The TINYINT, SMALLINT, INTEGER, and BIGINT data types are used
to represent signed integer data. Data of type TINYINT is represented in
JDBC as a Java byte data type (1 byte), with a minimum value of -128 and a
maximum value of 127. Data of type SMALLINT is represented in JDBC
as a Java short data type (2 bytes), with a minimum value of -32,768 and a
maximum value of 32,767. Data of type INTEGER is represented as a Java
int data type (4 bytes), with a minimum value of -2,147,483,648 and a
maximum value of 2,147,483,647. Data of type BIGINT is represented as a
Java long data type (8 bytes), with a minimum value of
-9,223,372,036,854,775,808
and
a
maximum
value
of
9,223,372,036,854,775,807.
Floating-Point Data: REAL, FLOAT, And DOUBLE
The REAL, FLOAT, and DOUBLE data types are used to represent signed,
approximate values. Data of type REAL supports seven digits of mantissa
precision, and is represented as a Java float data type. Data of types FLOAT
and DOUBLE support 15 digits of mantissa precision, and are represented
as Java double data types.
Tip: Be
aware
of
date
limitations.
One important note about Date and Timestamp
objects: The Java calendar starts at January 1, 1970,
which means that you cannot represent dates prior to
1970.
System.out.println("Date=" + d.toString());
// Same thing, without leading zeros
Date d2 = Date.valueOf("1996-6-30");
System.out.println("Date=" + d2.toString());
The Date class also serves very well in validating date values. If an invalid
date string is passed to the valueOf method, a java.lang.IllegalArgumentException is thrown:
String s;
// Get the date from the user
.
.
.
//
Validate the date
try {
Date d = Date.valueOf(s);
}
catch (java.lang.IllegalArgumentException ex) {
// Invalid date, notify the application
.
.
.
}
It is worth mentioning again that the Java date epoch is January 1, 1970;
therefore, you cannot represent any date values prior to January 1, 1970,
with a Date object.
Time
The Time class is used to represent times in the ANSI SQL format
HH:MM:SS, where HH is a two-digit hour, MM is a two-digit minute, and
SS is a two-digit second. The JDBC Time class extends the existing
java.util.Date class (setting the year, month, and day to zero) and, most
importantly, adds two methods to convert Strings into times, and vice-versa:
// Create a Time object with a time of 2:30:08 pm
Time t = Time.valueOf("14:30:08");
// Print the time
System.out.println("Time=" + t.toString());
database client API (the JDBC to ODBC Bridge is a perfect example). The
obvious drawback is that the JDBC driver is not portable and cannot be
automatically downloaded by todays browsers.
If a native bridge is required for your JDBC driver, you should keep a few
things in mind. First, do as little as possible in the C bridge code; you will
want to keep the bridge as small as possible, ideally creating just a Java
wrapper around the C API. Most importantly, avoid the temptation of
performing memory management in C (i.e. malloc). This is best left in Java
code, since the Java Virtual Machine so nicely takes care of garbage
collection. Secondly, keep all of the native method declarations in one Java
class. By doing so, all of the bridge routines will be localized and much
easier to maintain. Finally, dont make any assumptions about data
representation. An integer value may be 2 bytes on one system, and 4 bytes
on another. If you are planning to port the native bridge code to a different
system (which is highly likely), you should provide native methods that
provide the size and interpretation of data.
Listing 10.7 illustrates these suggestions. This module contains all of the
native method declarations, as well as the code to load our library. The
library will be loaded when the class is instantiated.
Listing 10.7 Java native methods.
//----------------------------------------------------------------------// MyBridge.java
//
// Sample code to demonstrate the use of native
methods
//----------------------------------------------------------------------package jdbc.test;
import java.sql.*;
public class MyBridge
extends Object
{
//------------------------------------------------------------------// Constructor
}
Once this module has been compiled (javac), a Java generated header file
and C file must be created:
javah
jdbc.test.MyBridge
javah
-stubs jdbc.test.MyBridge
These files provide the mechanism for the Java and C worlds to
communicate with each other. Listing 10.8 shows the generated header file
(jdbc_test_MyBridge.h, in this case), which will be included in our C bridge
code.
Listing 10.8 Machine-generated header file for native methods.
/* DO NOT EDIT THIS FILE - it is machine generated
*/
#include <native.h>
/* Header for class jdbc_test_MyBridge */
#ifndef
#define
_Included_jdbc_test_MyBridge
_Included_jdbc_test_MyBridge
#ifdef __cplusplus
}
#endif
#endif
The generated C file (shown in Listing 10.9) must be compiled and linked
with the bridge.
}
/*
SYMBOL:
"jdbc/test/MyBridge/callSomeFunction(Ljava/lang/Str
ing;[B)V",
Java_jdbc_test_MyBridge_callSomeFunction_stub */
__declspec(dllexport)
stack_item
*Java_jdbc_test_MyBridge_callSomeFunction_stub(stac
k_item
*_P_,struct
execenv *_EE_) {
extern
void
jdbc_test_MyBridge_callSomeFunction(void
*,void
*,void
*);
(void)
jdbc_test_MyBridge_callSomeFunction(_P_[0].p,
((_P_[1].p)),
((_P_[2].p)));return _P_;
}
The bridge code is shown in Listing 10.10. The function prototypes were
taken from the generated header file.
Listing 10.10 Bridge code.
//----------------------------------------------------------------------// MyBridge.c
//
// Sample code to demonstrate the use of native
methods
//----------------------------------------------------------------------#include <stdio.h>
#include <ctype.h>
#include <string.h>
// Java internal header files
#include "StubPreamble.h"
#include "javaString.h"
// Our header file generated by JAVAH
#include "jdbc_test_MyBridge.h"
//----------------------------------------------------------------------// getINTSize
// Return the size of an int
//----------------------------------------------------------------------long
jdbc_test_MyBridge_getINTSize(
struct Hjdbc_test_MyBridge *caller)
{
return sizeof(int);
}
//----------------------------------------------------------------------// getINTValue
// Given a buffer, return the value as an int
//----------------------------------------------------------------------long jdbc_test_MyBridge_getINTValue(
struct Hjdbc_test_MyBridge *caller,
HArrayOfByte *buf)
{
// Cast our array of bytes to an integer
pointer
int* pInt = (int*) unhand (buf)->body;
//----------------------------------------------------------------------// callSomeFunction
// Call some function that takes a String and an
int pointer as arguments
//----------------------------------------------------------------------void jdbc_test_MyBridge_callSomeFunction(
struct Hjdbc_test_MyBridge *caller,
new
database systems that provide a C API. The real power and ultimate solution,
though, is to develop non-native JDBC driversthose consisting of 100
percent Java code.
Implementing Interfaces
The JDBC API specification provides a series of interfaces that must be
implemented by the JDBC driver developer. An interface declaration creates
a new reference type consisting of constants and abstract methods. An
interface cannot contain any implementations (that is, executable code).
What does all of this mean? The JDBC API specification dictates the
methods and method interfaces for the API, and a driver must fully
implement these interfaces. A JDBC application makes method calls to the
JDBC interface, not a specific driver. Because all JDBC drivers must
implement the same interface, they are interchangeable.
There are a few rules that you must follow when implementing interfaces.
First, you must implement the interface exactly as specified. This includes
the name, return value, parameters, and throws clause. Secondly, you must
be sure to implement all interfaces as public methods. Remember, this is the
interface that other classes will see; if it isnt public, it cant be seen. Finally,
all methods in the interface must be implemented. If you forget, the Java
compiler will kindly remind you.
Take a look at Listing 10.12 for an example of how interfaces are used. The
code defines an interface, implements the interface, and then uses the
interface.
jdbc.test;
class MyImplementation
implements jdbc.test.MyInterface
{
//------------------------------------------------------------------// Implement the 3 methods in the interface
//------------------------------------------------------------------public void method1()
{
}
public int method2(int x)
{
return addOne(x);
}
public String method3(String y)
{
return y;
}
World.");
String y = myInterface.method3("Hello,
}
}
As you can see, implementing interfaces is easy. Well go into more detail
with the major JDBC interfaces later in this chapter. But first, we need to
cover some basic foundations that should be a part of every good JDBC
driver.
Tracing
One detail that is often overlooked by software developers is providing a
facility to enable debugging. The JDBC API does provide methods to enable
and disable tracing, but it is ultimately up to the driver developer to provide
tracing information in the driver. It becomes even more critical to provide a
detailed level of tracing when you consider the possible wide-spread
distribution of your driver. People from all over the world may be using your
software, and they will expect a certain level of support if problems arise.
For this reason, I consider it a must to trace all of the JDBC API method
calls (so that a problem can be re-created using the output from a trace).
Turning On Tracing
The DriverManager provides a method to set the tracing PrintStream to be
used for all of the drivers; not only those that are currently active, but any
drivers that are subsequently loaded. Note that if two applications are using
JDBC, and both have turned tracing on, the PrintStream that is set last will
be shared by both applications. The following code snippet shows how to
turn tracing on, sending any trace messages to a local file:
try {
// Create a new OuputStream using a file. This
may fail if the
// calling application/applet does not have
the proper security
// to write to a local disk.
java.io.OutputStream outFile = new
java.io.FileOutputStream("jdbc.out");
// Create a PrintStream object using our newly
created OuputStream
if (traceOn()) {
DriverManager.println("Trace=" + a + b + c);
}
Data Coercion
At the heart of every JDBC driver is data. That is the whole purpose of the
driver: providing data. Not only providing it, but providing it in a requested
format. This is what data coercion is all aboutconverting data from one
format to another. As Figure 10.1 shows, JDBC specifies the necessary
conversions.
Figure 10.1 JDBC data conversion table.
In order to provide reliable data coercion, a data wrapper class should be
used. This class contains a data value in some known format and provides
methods to convert it to a specific type. As an example, I have included the
CommonValue class from the SimpleText driver in Listing 10.13. This class
has several overloaded constructors that accept different types of data
values. The data value is stored within the class, along with the type of data
(String, Integer, etc.). A series of methods are then provided to get the data in
different formats. This class greatly reduces the burden of the JDBC driver
developer, and can serve as a fundamental class for any number of drivers.
Listing 10.13 The CommonValue class.
package jdbc.SimpleText;
import java.sql.*;
public class CommonValue
extends
Object
{
//----------------------------------------------------------------------// Constructors
//----------------------------------------------------------------------public CommonValue()
{
data = null;
}
public CommonValue(String s)
{
data = (Object) s;
internalType = Types.VARCHAR;
}
public CommonValue(int i)
{
data = (Object) new Integer(i);
internalType = Types.INTEGER;
}
public CommonValue(Integer i)
{
data = (Object) i;
internalType = Types.INTEGER;
}
public CommonValue(byte b[])
{
data = (Object) b;
internalType = Types.VARBINARY;
}
//---------------------------------------------------------------------// isNull
// returns true if the value is null
//----------------------------------------------------------------------public boolean isNull()
{
return (data == null);
}
//----------------------------------------------------------------------// getMethods
//-----------------------------------------------------------------------
}
break;
default:
throw new SQLException("Unable to
convert data type to
String: " +
internalType);
}
}
return s;
}
return i;
switch(internalType) {
case Types.VARCHAR:
{
// Convert the String into a byte
array. The String must
// contain an even number of hex
digits.
String s = ((String)
data).toUpperCase();
String digits = "0123456789ABCDEF";
int len = s.length();
int index;
number of hex
if ((len % 2) != 0) {
throw new SQLException(
"Data must have an even
}
digits");
index
digits.indexOf(s.charAt(i * 2));
if (index < 0) {
throw new
if (index < 0) {
SQLException("Invalid hex digit");
}
b[i] += (byte) index;
}
}
break;
throw new
case Types.VARBINARY:
b = (byte[]) data;
break;
default:
Note that the SimpleText driver supports only character, integer, and binary
data; thus, CommonValue only accepts these data types, and only attempts
to convert data to these same types. A more robust driver would need to
further implement this class to include more (if not all) data types.
Escape Clauses
Another consideration when implementing a JDBC driver is processing
escape clauses. Escape clauses are used as extensions to SQL and provide a
method to perform DBMS-specific extensions, which are interoperable
among DBMSes. The JDBC driver must accept escape clauses and expand
them into the native DBMS format before processing the SQL statement.
While this sounds simple enough on the surface, this process may turn out to
be an enormous task. If you are developing a driver that uses an existing
DBMS, and the JDBC driver simply passes SQL statements to the DBMS,
you may have to develop a parser to scan for escape clauses.
The following types of SQL extensions are defined:
Date, time, and timestamp data
Scalar functions such as numeric, string, and data
type conversion
LIKE predicate escape characters
Outer joins
Procedures
The JDBC specification does not directly address escape clauses; they are
inherited from the ODBC specification. The syntax defined by ODBC uses
the escape clause provided by the X/OPEN and SQL Access Group SQL
CAE specification (1992). The general syntax for an escape clause is:
{escape}
Well cover the specific syntax for each type of escape clause in the
following sections.
Date, Time, And Timestamp
The date, time, and timestamp escape clauses allow an application to
specify date, time, and timestamp data in a uniform manner, without concern
to the native DBMS format (for which the JDBC driver is responsible). The
syntax for each (respectively) is
{d 'value'}
{t 'value'}
{ts 'value'}
where d indicates value is a date in the format yyyy-mm-dd, t indicates
value is a time in the format hh:mm:ss, and ts indicates value is a timestamp
in the format yyyy-mm-dd hh:mm:ss[.f...]. The following SQL statements
illustrate the use of each:
UPDATE EMPLOYEE SET HIREDATE={d '1992-04-01'}
UPDATE
EMPLOYEE
SET
LAST_IN={ts
'1996-07-03
08:00:00'}
UPDATE EMPLOYEE SET BREAK_DUE={t '10:00:00'}
Scalar Functions
The five types of scalar functionsstring, numeric, time and date, system,
and data type conversionall use the syntax:
{fn
scalar-function}
To determine what type of string functions a JDBC driver supports, an
application can use the DatabaseMetaData method getStringFunctions.
This method returns a comma-separated list of string functions, possibly
containing ASCII, CHAR, CONCAT, DIFFERENCE, INSERT, LCASE,
LEFT, LENGTH, LOCATE, LTRIM, REPEAT, REPLACE, RIGHT,
RTRIM, SOUNDEX, SPACE, SUBSTRING, and/or UCASE.
To determine what type of numeric functions a JDBC driver supports, an
application can use the DatabaseMetaData method getNumericFunctions.
This method returns a comma-separated list of numeric functions, possibly
containing ABS, ACOS, ASIN, ATAN, ATAN2, CEILING, COS, COT,
DEGREES, EXP, FLOOR, LOG, LOG10, MOD, PI, POWER, RADIANS,
RAND, ROUND, SIGN, SIN, SQRT, TAN, and/or TRUNCATE.
To determine what type of system functions a JDBC driver supports, an
application can use the DatabaseMetaData method getSystemFunctions.
This method returns a comma-separated list of system functions, possibly
containing DATABASE, IFNULL, and/or USER.
To determine what type of time and date functions a JDBC driver supports,
an
application
can
use
the
DatabaseMetaData
method
getTimeDateFunctions. This method returns a comma-separated list of time
and date functions, possibly containing CURDATE, CURTIME,
DAYNAME, DAYOFMONTH, DAYOFWEEK, DAYOFYEAR, HOUR,
Driver
The Driver class is the entry point for all JDBC drivers. From here, a
connection to the database can be made in order to perform work. This class
is intentionally very small; the intent is that JDBC drivers can be preregistered with the system, enabling the DriverManager to select an
appropriate driver given only a URL (Universal Resource Locator). The only
way to determine which driver can service the given URL is to load the
Driver class and let each driver respond via the acceptsURL method. To
keep the amount of time required to find an appropriate driver to a
minimum, each Driver class should be as small as possible so it can be
loaded quickly.
Register Thyself
The very first thing that a driver should do is register itself with the
DriverManager. The reason is simple: You need to tell the DriverManager
that you exist; otherwise you may not be loaded. The following code
illustrates one way of loading a JDBC driver:
java.sql.Driver d = (java.sql.Driver)
Class.forName
("jdbc.SimpleText.SimpleTextDriver").newInstance();
Connection
con
DriverManager.getConnection("jdbc:SimpleText",
"");
=
"",
The class loader will create a new instance of the SimpleText JDBC driver.
The application then asks the DriverManager to create a connection using
the given URL. If the SimpleText driver does not register itself, the
DriverManager will not attempt to load it, which will result in a nasty No
capable driver error.
The best place to register a driver is in the Driver constructor:
public SimpleTextDriver()
throws SQLException
{
// Attempt to register this driver with the
JDBC DriverManager.
// If it fails, an exception will be thrown.
DriverManager.registerDriver(this);
}
URL Processing
As I mentioned a moment ago, the acceptsURL method informs the
DriverManager whether a given URL is supported by the driver. The
general format for a JDBC URL is
jdbc:subprotocol:subname
where subprotocol is the particular database connectivity mechanism
supported (note that this mechanism may be supported by multiple drivers)
and the subname is defined by the JDBC driver. For example, the format for
the JDBC-ODBC Bridge URL is:
jdbc:odbc:data source name
Thus, if an application requests a JDBC driver to service the URL of
jdbc:odbc:foobar
the only driver that will respond that the URL is supported is the JDBCODBC Bridge; all others will ignore the request.
Listing 10.14 shows the acceptsURL method for the SimpleText driver. The
SimpleText driver will accept the following URL syntax:
jdbc:SimpleText
Note that no subname is required; if a subname is provided, it will be
ignored.
Listing 10.14 The acceptsURL method.
//----------------------------------------------------------------------// acceptsURL - JDBC API
//
// Returns true if the driver thinks that it can
open a connection
// to the given URL. Typically, drivers will return
true if they
// understand the subprotocol specified in the URL,
and false if
// they don't.
//
//
url
The URL of the database.
//
// Returns true if this driver can connect to the
given URL.
//----------------------------------------------------------------------public boolean acceptsURL(
String url)
throws SQLException
{
if (traceOn()) {
trace("@acceptsURL (url=" + url + ")");
}
boolean rc = false;
// Get the subname from the url. If the url is
not valid for
// this driver, a null will be returned.
if (getSubname(url) != null) {
rc = true;
if (traceOn()) {
trace(" " + rc);
}
return rc;
//----------------------------------------------------------------------// getSubname
// Given a URL, return the subname. Returns null if
the protocol is
//
not
"jdbc"
or
the
subprotocol
is
not
"simpletext."
//----------------------------------------------------------------------public String getSubname(
String url)
{
String subname = null;
String protocol = "JDBC";
String subProtocol = "SIMPLETEXT";
// Convert to uppercase and trim all leading
and trailing
// blanks.
url = (url.toUpperCase()).trim();
// Make sure the protocol is jdbc:
if (url.startsWith(protocol)) {
// Strip off the protocol
url = url.substring (protocol.length());
// Look for the colon
if (url.startsWith(":")) {
url = url.substring(1);
// Check the subprotocol
if (url.startsWith(subProtocol)) {
url
url.substring(subProtocol.length());
// Look for the colon that
import java.sql.*;
class PropertyTest {
public static void main(String args[])
{
try {
// Quick way to create a driver object
java.sql.Driver d = new
jdbc.SimpleText.SimpleTextDriver();
String url = "jdbc:SimpleText";
// Make sure we have the proper URL
if (!d.acceptsURL(url)) {
throw new SQLException("Unknown
URL: " + url);
}
// Setup a Properties object. This
should contain an entry
// for all known properties to this
point. Properties that
// have already been specified in the
Properties object will
// not be returned by getPropertyInfo.
java.util.Properties props = new
java.util.Properties();
// Get the property information
DriverPropertyInfo info[]
d.getPropertyInfo(url, props);
(i + 1));
+ info[i].name);
System.out.println("Name:
"
System.out.println("Description: "
info[i].description);
System.out.println("Required:
" + info[i].required);
System.out.println("Value:
"
+ info[i].value);
System.out.println("Choices:
"
+ info[i].choices);
}
}
catch (SQLException ex) {
System.out.println
("\nSQLException(s) caught\n");
// Remember that SQLExceptions may be
chained together
while (ex != null) {
System.out.println("SQLState:
" + ex.getSQLState());
System.out.println("Message: "
+ ex.getMessage());
System.out.println ("");
ex = ex.getNextException ();
}
}
}
}
Listing 10.15 produces the following output:
Number of properties: 1
Property 1
Name:
Description:
Required:
Value:
Choices:
Directory
Initial text file directory
false
null
null
DriverPropertyInfo[] getPropertyInfo(
String url,
java.util.Properties info)
throws SQLException
DriverPropertyInfo prop[];
// Only one property required for the
SimpleText driver, the
// directory. Check the property list coming
in. If the
// directory is specified, return an empty
list.
if (info.getProperty("Directory") == null) {
// Setup the DriverPropertyInfo entry
prop = new DriverPropertyInfo[1];
prop[0]
=
new
DriverPropertyInfo("Directory", null);
prop[0].description = "Initial text
file directory";
prop[0].required = false;
}
else {
// Create an empty list
prop = new DriverPropertyInfo[0];
}
return prop;
}
Lets Get Connected
Now that we can identify a driver to provide services for a given URL and
get a list of the required and optional parameters necessary, its time to
establish a connection to the database. The connect method does just that, as
shown in Listing 10.17, by taking a URL and connection property list and
attempting to make a connection to the database. The first thing that connect
should do is verify the URL (by making a call to acceptsURL). If the URL
is not supported by the driver, a null value will be returned. This is the only
reason that a null value should be returned. Any other errors during the
connect should throw an SQLException.
Listing 10.17 Connecting to the database.
String url,
java.util.Properties info)
throws SQLException
if (traceOn()) {
trace("@connect (url=" + url + ")");
}
// Ensure that we can understand the given URL
if (!acceptsURL(url)) {
return null;
}
new
return con;
As you can see, there isnt a lot going on here for the SimpleText driver;
remember that we need to keep the size of the Driver class implementation
as small as possible. To aid in this, all of the code required to perform the
securityManager
if (securityManager != null) {
try {
be
if (s == null) {
s = System.getProperty("user.dir");
}
setCatalog(s);
}
Creating Statements
From the Connection object, an application can create three types of
Statement objects. The base Statement object is used for executing SQL
statements directly. The PreparedStatement object (which extends
Statement) is used for pre-compiling SQL statements that may contain input
parameters.
The
CallableStatement
object
(which
extends
PreparedStatement) is used to execute stored procedures that may contain
both input and output parameters.
For the SimpleText driver, the createStatement method does nothing more
than create a new Statement object. For most database systems, some type
of statement context, or handle, will be created. One thing to note whenever
an object is created in a JDBC driver: Save a reference to the owning object
because you will need to obtain information (such as the connection context
from within a Statement object) from the owning object.
Consider the createStatement method within the Connection class:
public Statement createStatement()
throws SQLException
{
if (traceOn()) {
trace("Creating new SimpleTextStatement");
}
// Create a new Statement object
SimpleTextStatement
stmt
SimpleTextStatement();
new
Statement class until the Connection class has been compiled. This is a
circular dependency. Of course, the Java compiler does allow multiple files
to be compiled at once, but some build environments do not support circular
dependency. I have solved this problem in the SimpleText driver by defining
some simple interface classes. In this way, the Statement class knows only
about the general interface of the Connection class; the implementation of
the interface does not need to be present. Our modified initialize method
looks like this:
public void initialize(
SimpleTextIConnection con)
throws SQLException
{
// Save the owning connection object
ownerConnection = con;
}
Note that the only difference is the introduction of a new class,
SimpleTextIConnection, which replaces SimpleTextConnection. I have
chosen to preface the JDBC class name with an I to signify an interface.
Heres the interface class:
public interface SimpleTextIConnection
extends java.sql.Connection
{
String[] parseSQL(String sql);
Hashtable getTables(String directory, String
table);
Hashtable getColumns(String directory, String
table);
String getDirectory(String directory);
}
Note that our interface class extends the JDBC class, and our Connection
class implements this new interface. This allows us to compile the interface
first, then the Statement, followed by the Connection. Say good-bye to
your circular dependency woes.
Now, back to the Statement objects. The prepareStatement and
prepareCall methods of the Connection object both require an SQL
statement to be provided. This SQL statement should be pre-compiled and
stored with the Statement object. If any errors are present in the SQL
DatabaseMetaData
At over 130 methods, the DatabaseMetaData class is by far the largest. It
supplies information about what is supported and how things are supported.
It also supplies catalog information such as listing tables, columns, indexes,
procedures, and so on. Because the JDBC API specification does an
adequate job of explaining the methods contained in this class, and most of
them are quite straightforward, well just take a look at how the SimpleText
driver implements the getTables catalog method. But first, lets review the
basic steps needed to implement each of the catalog methods (that is, those
methods that return a ResultSet):
1. Create the result columns, which includes the
column name, type, and other information about
each of the columns. You should perform this step
regardless of whether the database supports a given
catalog function (such as stored procedures). I
believe that it is much better to return an empty
result set with only the column information than to
raise an exception indicating that the database does
not support the function. The JDBC specification does
not currently address this issue, so it is open for
interpretation.
2. Retrieve the
catalog
information
from
the
database.
3. Perform any filtering necessary. The application
may have specified the return of only a subset of the
this.name = name;
this.type = type;
this.precision = precision;
public SimpleTextColumn(
String name,
int type)
{
this.name = name;
this.type = type;
this.precision = 0;
}
public SimpleTextColumn(
String name)
{
this.name = name;
this.type = 0;
this.precision = 0;
}
public
public
public
public
public
public
public
String name;
int type;
int precision;
boolean searchable;
int colNo;
int displaySize;
String typeName;
}
Note that I have used several constructors to set up various default
information, and that all of the attributes are public. To follow objectoriented design, I should have provided a get and set method to encapsulate
each attribute, but I chose to let each consumer of this object access them
directly. Listing 10.20 shows the code for the getTables method.
Listing 10.20 The getTables method.
//--------------------------------------------------------------------// getTables - JDBC API
1,
2,
3,
4,
5,
"TABLE_CAT", Types.VARCHAR);
"TABLE_SCHEM", Types.VARCHAR);
"TABLE_NAME", Types.VARCHAR);
"TABLE_TYPE", Types.VARCHAR);
"REMARKS", Types.VARCHAR);
singleRow);
}
}
1),
entries[i]);
tableEntry);
list.put(new Integer(i),
}
}
}
return list;
Again, I use a Hashtable for each table (or file in our case) that is found. By
now, you will have realized that I really like using Hashtables; they can
grow in size dynamically and provide quick access to data. And because a
Hashtable stores data as an abstract Object, I can store whatever is
necessary. In this case, each Hashtable entry for a table contains a
SimpleTextTable object:
public
class
SimpleTextTable
extends
Object
{
//----------------------------------------------------------------------// Constructor
//----------------------------------------------------------------------public SimpleTextTable(
String dir,
String file)
{
this.dir = dir;
this.file = file;
// If the filename has the .SDF extension,
get rid of it
if
(file.endsWith(SimpleTextDefine.DATA_FILE_EXT)) {
name = file.substring(0, file.length()
SimpleTextDefine.DATA_FILE_EX
T.length());
}
else {
name = file;
}
//
sql
Typically this is a static SQL SELECT
statement.
//
// Returns the table of data produced by the SQL
statement.
//----------------------------------------------------------------------public ResultSet executeQuery(
String sql)
throws SQLException
{
if (traceOn()) {
trace("@executeQuery(" + sql + ")");
}
java.sql.ResultSet rs = null;
// Execute the query. If execute returns true,
then a result set
// exists.
if (execute(sql)) {
rs = getResultSet();
}
else {
// If the statement does not
create a ResultSet, the
// specification indicates that
an SQLException should
// be raised.
throw new SQLException("Statement did not
create a ResultSet");
}
return rs;
}
//----------------------------------------------------------------------// executeUpdate - JDBC API
// Execute an SQL INSERT, UPDATE, or DELETE
statement. In addition,
return count;
which situation exists. If getUpdateCount returns -1, you have reached the
end of the results; otherwise, it will return the number of rows affected by
the statement.
The SimpleText driver does not support multiple result sets, so I dont have
any example code to present to you. The only DBMS that I am aware of that
supports this is Sybase. Because there are already multiple JDBC drivers
available for Sybase (one of which I have developed), I doubt you will have
to be concerned with getMoreResults. Consider yourself lucky.
PreparedStatement
The PreparedStatement is used for pre-compiling an SQL statement,
typically in conjunction with parameters, and can be efficiently executed
multiple times with just a change in a parameter value; the SQL statement
does not have to be parsed and compiled each time. Because the
PreparedStatement class extends the Statement class, you will have
already implemented a majority of the methods. The executeQuery,
executeUpdate, and execute methods are very similar to the Statement
methods of the same name, but they do not take an SQL statement as a
parameter. The SQL statement for the PreparedStatement was provided
when the object was created with the prepareStatement method from the
Connection object. One danger to note here: Because PreparedStatement
is derived from the Statement class, all of the methods in Statement are
also in PreparedStatement. The three execute methods from the Statement
class that accept SQL statements are not valid for the PreparedStatement
class. To prevent an application from invoking these methods, the driver
should also implement them in PreparedStatement, as shown here:
// The overloaded executeQuery on the Statement
object (which we
// extend) is not valid for PreparedStatement or
CallableStatement
// objects.
public ResultSet executeQuery(
String sql)
throws SQLException
{
throw new SQLException("Method is not
valid");
}
int parameterIndex,
String x)
throws SQLException
// Validate the parameter index
verify(parameterIndex);
if
(boundParams.get(new
Integer(parameterIndex)) != null) {
boundParams.remove(new
Integer(parameterIndex));
}
}
Because the CommonValue class does not yet support all of the JDBC data
types, not all of the set methods have been implemented in the SimpleText
driver. You can see, however, how easy it would be to fully implement these
methods once CommonValue supported all of the necessary data coercion.
What Is It?
Another way to set parameter values is by using the setObject method. This
method can easily be built upon the other set methods. Of interest here is the
ability to set an Object without giving the JDBC driver the type of driver
being set. The SimpleText driver implements a simple method to determine
the type of object, given only the object itself:
protected
int
getObjectType(
Object x)
throws SQLException
{
// Determine the data type of the Object by
attempting to cast
// the object. An exception will be thrown if
an invalid casting
// is attempted.
try {
if ((String) x != null) {
return Types.VARCHAR;
}
}
catch (Exception ex) {
}
try {
}
if ((Integer) x != null) {
return Types.INTEGER;
}
catch (Exception ex) {
}
try {
if ((byte[]) x != null) {
return Types.VARBINARY;
}
}
catch (Exception ex) {
}
throw new SQLException("Unknown object type");
}
Setting InputStreams
As well see with ResultSet later, using InputStreams is the recommended
way to work with long data (blobs). There are two ways to treat
InputStreams when using them as input parameters: Read the entire
InputStream when the parameter is set and treat it as a large data object, or
defer the read until the statement is executed and read it in chunks at a time.
The latter approach is the preferred method because the contents of an
InputStream may be too large to fit into memory. Heres what the
SimpleText driver does with InputStreams:
public
void
setBinaryStream(
int parameterIndex,
java.io.InputStream x,
int length)
throws SQLException
{
// Validate the parameter index
verify(parameterIndex);
// Read in the entire InputStream all at once.
A more optimal
// way of handling this would be to defer the
read until execute
// time, and only read in chunks at a time.
byte b[] = new byte[length];
try {
x.read(b);
}
catch (Exception ex) {
But wait, this isnt the preferred way! You are correct, it isnt. The
SimpleText driver simply reads in the entire InputStream and then sets the
parameter as a byte array. Ill leave it up to you to modify the driver to defer
the read until execute time.
ResultSet
The ResultSet class provides methods to access data generated by a table
query. This includes a series of get methods which retrieve data in any one
of the JDBC SQL type formats, either by column number or by column
name. When the issue of providing get methods was first introduced by
JavaSoft, some disgruntled programmers argued that they were not
necessary; if an application wanted to get data in this manner, then the
application could provide a routine to cross reference the column name to a
column number. Unfortunately (in my opinion), JavaSoft chose to keep these
methods in the API and provide the implementation of the cross reference
method in an appendix. Because it is part of the API, all drivers must
implement the methods. Implementing the methods is not all that difficult,
but it is tedious and adds overhead to the driver. The driver simply takes the
column name that is given, gets the corresponding column number for the
column name, and invokes the same get method using the column number:
public
String
getString(
String columnName)
throws SQLException
{
return getString(findColumn(columnName));
}
And heres the findColumn routine:
public int findColumn(
String columnName)
throws SQLException
{
if
(md.getColumnName(i).equalsIgnoreCase(columnName))
{
}
throw new SQLException("Column name not found:
" + columnName,
"S0022");
}
This method uses a Hashtable to cache the column number and column
names.
Its Your Way, Right Away
An application can request column data in any one of the supported JDBC
data types. As we have discussed before, the driver should coerce the data
into the proper format. The SimpleText driver accomplishes this by using a
CommonValue object for all data values. Therefore, the data can be served
in any format, stored as a CommonValue object, and the application can
request it in any other supported format. Lets take a look at the getString
method:
public String getString(
int columnIndex)
throws SQLException
{
// Verify the column and get the absolute
column number for the
// table.
int colNo = verify(columnIndex);
String s = null;
if (inMemoryRows != null) {
s
=
(getColumn(rowNum,
columnIndex)).getString();
}
else {
CommonValue value = getValue(colNo);
if (value != null) {
s = value.getString();
}
}
if (s == null) {
lastNull = true;
}
}
return s;
The method starts out by verifying that the given column number is valid. If
it is not, an exception is thrown. Some other types of initialization are also
performed. Remember that all ResultSet objects are provided with a
Hashtable of SimpleTextColumn objects describing each column:
protected int verify(
int column)
throws SQLException
{
clearWarnings();
lastNull = false;
SimpleTextColumn
inMemoryColumns.get(
col
(SimpleTextColumn)
new
Integer(column));
if (col == null) {
throw new SQLException("Invalid column
number: " + column);
}
return col.colNo;
}
Next, if the row data is stored in an in-memory Hashtable (as with the
DatabaseMetaData catalog methods), the data is retrieved from the
Hashtable. Otherwise, the driver gets the data from the data file. In both
instances, the data is retrieved as a CommonValue object, and the getString
method is used to format the data into the requested data type. Null values
are handled specially; the JDBC API has a wasNull method that will return
true if the last column that was retrieved was null:
public boolean wasNull()
throws SQLException
{
return lastNull;
}
The SimpleText driver also supports InputStreams. In our case, the
SimpleTextInputStream class is just a simple wrapper around a
CommonValue object. Thus, if an application requests the data for a column
as an InputStream, the SimpleText driver will get the data as a
CommonValue object (as it always does) and create an InputStream that
fetches the data from the CommonValue.
The getMetaData method returns a ResultSetMetaData object, which is
our last class to cover.
ResultSetMetaData
The ResultSetMetaData class provides methods that describe each one of
the columns in a result set. This includes the column count, column
Chapter
Internet Database Issues: Middleware
11
The JDBC specification says that the JDBC API should serve as a platform
for building so-called three-tier client/server systems, often called
middleware. As you might imagine, these systems have three basic
components: the client, the server, and the application server. Figure 11.1
shows the basic structure of a three-tier system.
Figure 11.1 Three-tier system structure.
In this chapter, Ill provide you with the code necessary to implement a
simple application server of your own. Well also take a look at building a
client for our home-grown application server. But before we get to the
coding, we first need to discuss why we would want to go to such lengths to
build a three-tier system instead of allowing direct database access.
Several middleware solutions based on the JDBC are already available, and
although you may ultimately decide to buy one from a vendor instead of
coding one yourself, I feel that its important to learn the issues involved
with middleware. Knowing the advantages and disadvantages that go along
with inserting a middle tier to a system can help you decide if you need one.
Connectivity Issues Involved With Database Access
Lets begin by examining some issues of database scalabilty that you are
likely to encounter. The Internet and large intranet scenarios pose interesting
dilemmas for databases that serve a large number of users:
ConcurrencySuppose a user receives some data
from the database server, and while the user is
looking at it, the data on the database server is
changed in some way. For the user to see the
updated material, both the database server and the
client need to be able to handle the change. While
some database servers can handle the necessary
coding (and the increased load on the server) for
updating, some cannot.
Legacy
DatabasesSome
legacy
database
systems may not support simultaneous connections,
or even direct connections using TCP/IP.
SecurityMost database servers do not support
encrypted connections, which means that certain
for
in
try {
while(true) {
server_port.accept();
Socket
client_socket
ServerConnection c =
ServerConnection(client_socket,
CurrentConnections, 3, watcher);
// Prevent simultaneous access
synchronized (connections) {
new
connections.addElement(c);
connection_list.addItem(c.getInfo()
);
}
}
}
catch (IOException e) {fail(e, "Exception while
listening for
connections");}
f.dispose();
System.exit(0);
}
// Start the server up, get a port number if
specified
public static void main(String[] args) {
int port = 0;
if (args.length == 1) {
try {port = Integer.parseInt(args[0]);}
catch (NumberFormatException e) {port = 0;}
}
new ApplicationServer(port);
}
}
// This class is the thread that handles all
communication with a client.
// It also notifies the ConnectionWatcher when the
connection is dropped.
class ServerConnection extends Thread {
static int numberOfConnections = 0;
protected Socket client;
protected ConnectionWatcher watcher;
protected DataInputStream in;
protected PrintStream out;
Connection con;
// Initialize the streams and start the thread
public ServerConnection(Socket client_socket,
ThreadGroup
CurrentConnections,
int priority, ConnectionWatcher
watcher) {
// Give the thread a group, a name, and a
priority
super(CurrentConnections, "Connection
number" +
numberOfConnections++);
this.setPriority(priority);
// We'll need this data later, so store it
in local objects
client = client_socket;
this.watcher = watcher;
// Create the streams for talking with
client
try {
in
=
new
DataInputStream(client.getInputStream());
out
=
new
PrintStream(client.getOutputStream());
}
catch (IOException e) {
try {client.close();} catch (IOException
e2) {
System.err.println("Exception while
getting socket streams: "
+ e); return;}
}
// And start the thread up
this.start();
}
// This is where the real "functionality" of the
server takes place.
// This is where the input and output is done to
the client.
public void run() {
String inline;
try {
// Loop forever, or until the connection is
broken!
while(true) {
// Read in a line
inline = in.readLine();
if (inline == null) break;
// If the client has broken
connection, get out of
// the loop
inline=inline.trim();
// Get rid of leading and
trailing whitespace
inline=inline.trim()
out.flush();
}
catch (IOException e) {}
// If the client broke off the connection,
notify the
// ConnectionWatcher
// (watcher) which will close the
connection.
finally {
try {client.close();}
catch (IOException e2) {
synchronized
(watcher)
{watcher.notify();}
}
}
}
// This sends info back to the connection starter
so that it can
connected
the
}
Output+="\n";
}
stmt.close();
//
con.close();
}
catch( Exception e ) {
e.printStackTrace();
Output=e.getMessage();
}
return Output;
}
// End DB specific stuff
} // End class Connection
// This class cleans up closed connections and
updates the displayed
// list of connected clients.
class ConnectionWatcher extends Thread {
protected ApplicationServer server;
protected ConnectionWatcher(ApplicationServer
s) {
super(s.CurrentConnections,
"ConnectionWatcher");
server = s;
this.start();
}
public synchronized void run() {
while(true) {
try {this.wait(10000);}
catch (InterruptedException e){
System.out.println("Caught an
Interrupted Exception");
}
// Prevent simultaneous access
synchronized(server.connections) {
// Loop through the connections
for(int i
server.connections.size(); i++) {
ServerConnection c;
0;
<
c
=
(ServerConnection)server.connections.elementAt(i);
// If the connection thread isn't
alive anymore,
// remove it from the Vector and
List.
if (!c.isAlive()) {
server.connections.removeElem
entAt(i);
server.connection_lis
t.delItem(i);
i--;
}
}
}
}
}
The Client: A Complete Example With Code
Now that we have the server code, lets look at the client class, which is
shown in Listing 11.2. This client class is not self-standing; well need an
applet to call this class and make use of the methods we define in it. The
code for a sample applet that calls this client class is shown in Listing 11.3.
Note that the client is specially coded to communicate with the application
server in Listing 11.1, and that it does not require the Web browser it is run
on to have the JDBC API classes. For our simple example, we dont need to
implement all of the functionality that is demanded of a JDBC driver, so I
didnt write one; a JDBC driver that can talk to our application server would
not be difficult to write at this point, however, because we have a simple
command set and simple functionality. Figure 11.3 shows the client applet
in Listing 11.3, which uses the Dbclient class.
Figure 11.3 Sample applet that uses our client.
Listing 11.2 Client class.
import java.io.*;
import java.net.*;
import java.applet.*;
public
public
public
public
public
class DBClient {
Socket socket;
PrintStream out;
String Name;
Reader reader;
}
}
return(reader.getResult());
} else
{
if (line == null) {
System.out.println("Server closed
connection.");
break;
} // if NOT null
else {Result+=line+"\n";}
System.out.println("Read from server:
"+Result);
} // if NOT done..
} //while loop
}
catch
(IOException
e)
{System.out.println("Reader: " + e);}
finally {
try {if (in != null) in.close();}
catch (IOException e) {
System.exit(0);
}
}
}
public String getResult() {
return (Result);
}
}
The client class needs to be instantiated in a Java program, and the
connection needs to be started before any queries can be made. If you
remember our Interactive Query Applet from Chapter 4, this sample applet
will certainly look familiar to you.
Listing 11.3 Applet to call our client class.
import java.net.URL;
import java.awt.*;
import java.applet.Applet;
import DBClient;
public class IQ extends java.applet.Applet {
Button ConnectBtn = new Button("Connect
Database");
to
protected
DBClient DataConnection;
new
show();
} //init
public boolean handleEvent(Event evt) {
if ((evt.target == QueryField) & (evt.id ==
Event.KEY_PRESS))
{char c=(char)evt.key;
if (c == '\n')
{
// When a user enters q query and hits
"return," we send the
// query to be processed and get the
results to show in the
// OutputField.
DataConnection.ProcessCommand("S");
OutputField.setText(DataConnection.ProcessC
ommand(QueryField.getText()));
return true;
}
}
if ((evt.target == ConnectBtn) & (evt.id ==
Event.ACTION_EVENT))
{
// This is the first command the
application server expects,
// connect to the data source.
OutputField.setText(DataConnection.ProcessC
ommand("L"));
return true;
}
return false;
} // handleEvent()
}
Youll need a Web page to call this applet from:
<HTML>
<HEAD>
<TITLE>
Chapter
The JDBC API
12
This chapter ends our journey through the JDBC. Ive provided a summary
of the class interfaces and exceptions that are available in the JDBC API
version 1.01, which was the most current version at the time of this writing.
Although this chapters primary purpose is to serve as a reference, you
should still read through the sections completely so that you are aware of all
the constructors, variables, and methods available.
Classes
Well begin with the class listings. Each class listing includes a description
and the class constructors, methods, and variables.
public class Date
This class extends the java.util.Date object. But unlike the java util.Date,
which stores time, this class stores the day, year, and month. This is for strict
matching with the SQL date type.
Constructors
Additional Description public
String
toString()
Formats a Date object
as
YYYY-MM-DD
Construct
a
Additional Description
java.sql.Date
object
with
the
appropriate
parameters
MethodsConstructor
Method Name
public static Date valueOf
Converts a String str to an
(String str) Date(int Year,
sql.Date object
int Month, int day)
public class DriverManager
This class is used to load a JDBC driver and establish it as an available
driver. It is usually not instantiated, but is called by the JDBC driver.
Constructors
DriverManager()
Methods
Method Name
Additional Description
public
static
void Drops a driver from
deregisterDriver(Driveravailable drivers list
JDBCdriver)
throws
SQLException
public static synchronized
the
Connection
getConnection(String URL)
throws SQLException
public static synchronized
Connection
getConnection(String URL,
String LoginName, String
LoginPassword)
throws
SQLException
public static synchronized Establishes a connection to
Connection
the given database URL,
getConnection(String URL, with the given parameters
Properties
LoginInfo)
throws SQLException
Finds
a
driver
that
public
static
Driver
understands the JDBC URL
getDriver(String
URL)
from the registered driver
throws SQLException
list
public static
getDrivers()
public
static
getLoginTimeout()
int Indicates
the
maximum
time
(seconds)
that
a
driver
will
wait
when
logging into a database
SQLException
public
static
void Indicates
the
time
(in
setLoginTimeout(int sec)
seconds) that all drivers
will wait when logging into
a database
public
static
void Define
the
PrintStream
setLogStream (PrintStream that logging messages are
log)
sent to via the println
method above
public class DriverPropertyInfo
This class is for developers who want to obtain and set properties for a
loaded JDBC driver. Its not necessary to use this class, but it is useful for
debugging JDBC drivers and advanced development.
Constructors
Constructor
Additional Description
public
DriverPropertyInfo The propName is the name
(String propName, String of
the
property,
and
propValue)
propValue is the current
value; if its not been set,
it may be null
Variables
Variable Name
Additional Description
choices
description
name
required
value
public final class Numeric
This special fixed-point, high precision number class is used to store the
SQL data types NUMERIC and DECIMAL.
Constructors
Constructor
public
strNum)
Additional Description
public
Numeric(String Produces a Numeric, and
strNum, int scale)
scale is the number of
digits right of the decimal
public Numeric(int intNum) Produces a Numeric object
from an int Java type
parameter
public Numeric(int intNum, Produces a Numeric object
int scale)
from an int, and scale
gives the desired number
of places right of the
decimal
public Numeric(long x)
public
Numeric(Numeric Produces a Numeric object
num, int scale)
from a Numeric, and scale
gives the desired number
of places right of the
decimal
Methods
Method Name
public
n)
Numeric
Additional
Description
add(Numeric Performs
arithmetic
addition
on
the
reference
Numeric
object and the Numeric
argument
public
static
Numeric Produces
a
Numeric
createFromByteArray(byte
object from the byte
byteArray[])
array parameter
public
static
Numeric Produces
a
Numeric
createFromIntegerArray(int
object from the int
intArray[])
array parameter
public static Numeric createFromRadixString(String
str, int radix)
public
static
Numeric Produces
a
Numeric
createFromScaled(long
object by taking the
longNum, int power) Produces longNum
to
the
a Numeric object from the 10^power
String
and
int
radix
parameters
public Numeric divide(Numeric Divides the Numeric by
q)
the Numeric parameter
double doubleValue()
boolean equals(Object Returns
true
if
the
Returns the Numeric as Numeric object equals
type double
the objct parameter
Returns
an
integer
hashcode
for
the
Numeric object
public Numeric[] integerDivide(Numeric x)
public int intValue() Returns
Returns the Numeric as
an array with two Numeric
a Java type int, digits
objects: the first one is the
after the decimal are
quotient, the second is the
dropped
remainder
public
boolean Returns
true
if
the
isProbablePrime()
number is prime; it
divides
the
Numeric
object by several small
primes, and then uses
the Rabin probabilistic
primality test to test if
the number is prime
the failure rate is less
than (1/(4^N))
public boolean lessThan(Numeric num)
public
boolean
Returns
true
if
the
lessThanOrEquals(Numeric
Numeric object is less
num) Returns true if the
than or equal to the
Numeric object is less than
Numeric num argument
the Numeric num argument
Returns the Numeric as
a Java type long
public Numeric modExp (Numeric numExp, Numeric
numMod)
public
Numeric
modInverse(Numeric
The
modular
numMod) The two parameters
multiplicative inverse is
are used to do a numMod
returned using numMod
modulus
to
the
numExp
as the modulus
exponent calculation; returns
the result as a Numeric
public long longValue()
public
Numeric Returns the product of
multiply(Numeric num)
the Numeric object and
the
Numeric
num
parameter
public static Numeric pi(int places)
public Numeric pow(int exp) Returns
a
Numeric
Returns pi to the number of object
using
the
decimal places
current Numeric object
taken to the power of
Numeric Returns
a
Numeric
Random object that is a random
number using randSeed
as a seed, having size
in bits equal to the bits
parameter
public
Numeric Returns the remainder
remainder(Numeric num)
resulting from dividing
this Numeric object by
the
Numeric
num
parameter
public
static
void Sets the rounding value
setRoundingValue(int val)
used
in
rounding
operations
for
the
Numeric object
public
scale)
Numeric
setScale(int Returns
a
Numeric
object from the current
object
with
the
specified
scale
parameter
public Numeric
numberOfBits)
public Numeric
numberOfBits)
public
subtract(Numeric num)
public
radix)
String
Variables
Variable Name
Additional Description
to
to
Additional Description
hour,
Methods
Method Name
Additional Description
public
static
Time Returns a Numeric object
valueOf(String numStr)
from the String numStr
parameter that is in the
format: HH:MM:SS
public class TimeStamp
This class is used to map the SQL data type TIMESTAMP. It extends
java.util.Date, and has nanosecond precision for time-stamping purposes.
Constructors
Constructor
Additional Description
Additional Description
public
boolean Compares the Timestamp
equals(Timestamp tstamp) object with the Timestamp
parameter tstamp; returns
true if they match
public int getNanos()
Returns
the
Timestamp
objects nanoseconds
Sets
the
Timestamp
objects nanosecond value
public
static
Timestamp Returns
a
Timestamp
valueOf(String strts)
object converted from the
strts parameter that is in
the previous format
Additional Description
public Types()
Variables
BIGINT
BINARY
BIT
CHAR
DATE
DECIMAL
DOUBLE
FLOAT
INTEGER
LONGVARBINARY
LONGVARCHAR
NULL
NUMERIC
OTHER (for a database specific data type, not a
standard SQL-92 data type)
REAL
SMALLINT
TIME
TIMESTAMP
TINYINT
VARBINARY
VARCHAR
Interfaces
Next are the interface listings. As with the class listings, each interface
listing includes a description and the interfaces methods and variables.
public interface CallableStatement
This is the primary interface to access stored procedures on a database. If
OUT parameters are specified and a query is executed via this class, its
results are fetched from this class and not the ResultSet class. This class
extends the PreparedStatement class, thus inheriting many of its methods.
The first 15 methods (the get methods) are identical in functionality to those
in the ResultSet class, but they are necessary if OUT parameters are used.
See the ResultSet class for a description of the methods.
Methods
Method Name
public
abstract
getBoolean(int
parameterIndex)
SQLException
public
abstract
getByte(int
parameterIndex)
SQLException
public
abstract
getBytes(int
parameterIndex)
SQLException
public
abstract
getDate(int
parameterIndex)
SQLException
public
abstract
getDouble(int
parameterIndex)
SQLException
public
abstract
Additional Description
boolean
throws
byte
throws
byte[]
throws
Date
throws
double
throws
float
getFloat(int
parameterIndex)
SQLException
throws
public
abstract
int
getInt(int parameterIndex)
throws SQLException
public
abstract
getLong(int
parameterIndex)
SQLException
long
throws
public
abstract
Numeric
getNumeric(int
parameterIndex, int scale)
throws SQLException
public
abstract
getObject(int
parameterIndex)
SQLException
public
abstract
getShort(int
parameterIndex)
SQLException
public
abstract
getString(int
parameterIndex)
SQLException
public
abstract
getTime(int
parameterIndex)
SQLException
Object
throws
short
throws
String
throws
Time
throws
abstract
void Each
parameter
of
the
Methods
Method Name
public
abstract
clearWarnings()
Additional Description
void Clears the warnings for the
throws connection
SQLException
public abstract void close() throws SQLException
public
abstract
void Functions
as
the
JDBC
commit()
throws equivalent of the standard
SQLException Closes the database commit command;
connection to the database it applies all commands and
changes made since the last
commit
or
rollback,
including
releasing
database
locks;
results
from queries are closed
when commit is invoked
public abstract Statement Returns a Statement object,
createStatement() throws which can then be used to
SQLException
perform actual queries
public
abstract
boolean
getAutoClose()
throws
SQLException
public
abstract
boolean Returns true if automatic
getAutoCommit()
throws committing
of
the
SQLException Returns true connection is on; automatic
if automatic closing of the commit is on by default and
connection
is
enabled; means that the connection
automatic closing results is committed on individual
in
the
closing
of
the transactions;
the
actual
connection when commit commit occurs when the
or rollback is performed
last row of a result set is
fetched,
or
when
the
ResultSet is closed
public
abstract
String Returns the current catalog
getCatalog()
throws name for the connection
SQLException
public
abstract
DatabaseMetaData
getMetaData()
throws SQLException
public
abstract
int Returns
the
transaction
getTransactionIsolation() isolation
mode
of
the
throws
SQLException connection
Returns
a
DatabaseMetaData
object
for the current connection
public abstract SQLWarning Returns
the
SQLWarning
getWarnings()
throws object with the warnings for
SQLException
the connection
public
abstract
boolean
isClosed()
throws
SQLException
public
abstract
boolean Returns
true
if
the
isReadOnly()
throws connection is a read only
SQLException Returns true connection
if the connection has been
closed
public
abstract
nativeSQL(String
SQLException
public
abstract Returns
a
CallableStatement
CallableStatement
object
prepareCall(String
used
to
perform
stored
sqlQuery)
throws procedures; note that the
SQLException
SQL query must be passed
in
as
the
sqlQuery
parameter here
public
abstract Returns
a
PreparedStatement
PreparedStatement
object
prepareStatement(String
used
to
perform
the
sqlQuery)
throws specified
sqlQuery;
this
SQLException
query
can
be
executed
repeatedly if desired by
using
the
PreparedStatement.execute
method
public
abstract
rollback()
SQLException
public
abstract
void Sets the connection to auto
setAutoClose
(boolean close mode if the auto)
throws SQLException
auto parameter is true
public abstract void throws Sets the connection to auto
SQLException
commit
mode
if
setAutoCommit(boolean
auto) the auto parameter is
true
public
abstract
void The
catalog
may
be
setCatalog (String catalog) changed by specifying the
throws SQLException
catalog
public
abstract
void Sets the connection to read
setReadOnly(boolean
only mode
readOnly)
throws
SQLException
public
abstract
void Sets translation isolation to
setTransactionIsolation(int the specified level
level) throws SQLException
Variables
The following constants are used in the setTransactionIsolation method as
the level parameter:
TRANSACTION_NONE
TRANSACTION_READ_COMMITTED
TRANSACTION_READ_UNCOMMITTED
TRANSACTION_REPEATABLE_READ
TRANSACTION_SERIALIZABLE
public interface DatabaseMetaData
This class contains useful information about the open connection to the
database. The Connection.getMetaData method returns a DatabaseMetaData object that is specific to the opened connection.
Methods
Method Name
Additional Descriptio
public
abstract
boolean Returns true if all
allProceduresAreCallable()
throwsk procedures available
SQLException
the user are callable
public
abstract
bool
allTablesAreSelectable() throws SQLException
public
abstract
boolean Returns
true
if
d
dataDefinitionCausesTransactionCommit() defintion
causes
throws SQLException Returns true if all of transaction to commit
the tables are accessible to the user on
the open connection
public
abstract
boolean Returns
true
if
d
dataDefinitionIgnoredInTransactions()
defintion is ignored in
throws SQLException
transaction
public
abstract
doesMaxRowSizeIncludeBlobs()
SQLException
boolean Returns
true
if
throws getMaxSize method d
not account for the siz
LONGVARCHAR
LONGVARBINARY
data types
public
abstract
ResultSet Returns
a
Result
getBestRowIdentifier(String
catalog, object for the speci
String
parameters that gets
schema, String table, int scope, boolean specified tables key
nullok) throws SQLException
the attributes that can
used to uniquely iden
a row, which may
composite;
the
sc
parameter is one of
constants:
bestRowTemporary,
bestRowTransaction,
betRowSession; the nu
parameter allows colum
that may be null;
ResultSet is composed
the
following
colum
scope (of the same ty
as
above
sc
parameter),
colu
name, SQL data ty
name of the data t
dependent
on
database,
precis
buffer length, signific
places if a Numeric ty
and pseudo column (
of
the
consta
bestRowUnknown,
bestRowNotPseudo,
bestRowPseudo)
public abstract ResultSet
throws SQLException
getCatalogs() Returns
a
Result
object that contains
column for the cata
names that are in
database
public
String
getCatalogSeparator()
SQLException
public abstract String
throws SQLException
abstract Returns
the
separa
throws between
the
cata
String and the table na
getCatalogTerm() Returns
the
databa
specific term for catal
public
abstract
ResultSet Returns
a
Result
getColumnPrivileges(String
catalog, object
that
conta
String
schemaString
table,
String information
about
columnNamePattern) throws SQLException specified tables match
columnNamePattern;
returned ResultSet ob
contains
the
follow
columns:
the
cata
name that the table is
the schema the table
in, the table name,
column name, owner
the table, grantee, t
of
access
(SELE
UPDATE, etc.), and if
grantee can grant acc
to others, YES, NO,
null (if unknown)
public
abstract
getColumns(String
String
schemaPattern,
tableNamePattern,
String
columnNamePattern)
SQLException
ResultSet Returns
a
Result
catalog, object
that
conta
String information
about
matching columns for
throws matching
tables
schemas; the Result
contains
the
follow
columns: catalog na
schema
name,
ta
name, column name,
data type, name of
type
specific
to
database, the maxim
number of characters
precision depending
the
data
type,
bu
length (not used),
number
of
digits
applicable),
radix
applicable),
null-ab
(one of the consta
columnNoNulls,
columnNullable,
columnNullableUnknow
comments for the colu
default value (if it exi
else null), empty colu
empty column, maxim
number of bytes in
column of type CHAR
applicable), index num
of
column;
the
column is set to YES
it can contain NULLS
not NO else its empt
the status is unknown
public
abstract
ResultSet
get Returns
a
Result
CrossReference(String
primaryCatalog, object that describes
String
primarySchema, way
a
table
imp
String
primaryTable,
String foreign
keys;
foreignCatalog,
ResultSet object retur
String
foreignSchema,
String by this method conta
foreignTable)
these columns: prim
throws SQLException
keys
table
cata
primary
keys
ta
schema,
primary
k
table,
primary
k
column
name,
fore
keys
table
cata
foreign
keys
ta
schema,
foreign
k
table,
foreign
k
column name, seque
number
within
fore
key, action to foreign
when
primary
key
updated
(one
of
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
action
to
foreign
when
primary
key
deleted
(one
of
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
foreign key identifier,
primary key indentifier
public
abstract
getDatabaseProductName()
SQLException
String Returns
the
throws product name
datab
public
abstract
getDatabaseProductVersion()
SQLException
String Returns
the
throws product number
datab
public
abstract
getDefaultTransactionIsolation()
SQLException
int Returns
the
def
throws transaction isolation le
as
defined
by
applicable
constants
the Connection class
public
abstract
getDriverMajorVersion()
drivers
ma
public
abstract
getDriverMinorVersion()
drivers
public
abstract
ResultSet Returns
a
Result
getExportedKeys(String
catalog,
String object that describes
schema,
String
table)
throws foreign key attributes t
SQLException
reference
the
speci
tables primary key;
ResultSet object retu
the
following
colum
primary
keys
ta
catalog,
primary
k
table
schema,
prim
keys table, primary k
column
name,
fore
keys
table
cata
foreign
keys
ta
schema,
foreign
k
table,
foreign
k
column name, seque
number
within
fore
key, action to foreign
when
primary
key
updated
(one
of
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
action
to
foreign
when
primary
key
deleted
(one
of
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
foreign key identifier,
primary key indentifier
public
abstract
getExtraNameCharacters()
SQLException
public
abstract
getIdentifierQuoteString()
SQLException
public
abstract
ResultSet Returns
a
Result
getImportedKeys(String String schema, object that describes
String table) throws SQLException
primary
key
attribu
that are referenced by
specified tables fore
key
attributes;
ResultSet object conta
the
following
colum
primary
keys
ta
catalog,
primary
k
table
schema,
prim
keys table, primary k
column
name,
fore
keys
table
cata
foreign
keys
ta
schema,
foreign
k
table,
foreign
k
column name, seque
number
within
fore
key, action to foreign
when
primary
key
updated
(one
of
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
action
to
foreign
when
primary
key
deleted
(one
of
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
foreign key identifier,
primary key indentifier
public
abstract
ResultSet Returns
a
Result
getIndexInfo(String
catalog,
String object that describes
public
abstract
getMaxBinaryLiteralLength()
SQLException
public
abstract
getMaxCatalogNameLength()
SQLException
public
abstract
getMaxCharLiteralLength()
SQLException
int Returns
the
throws length for a
literal
public
abstract
getMaxColumnNameLength()
SQLException
public
abstract
int Indicates
the
maxim
chara
maxim
getMaxColumnsInGroupBy()
SQLException
public
abstract
getMaxColumnsInIndex()
SQLException
public
abstract
getMaxColumnsInOrderBy()
SQLException
public
abstract
getMaxColumnsInSelect()
SQLException
public
abstract
getMaxColumnsInTable()
SQLException
int Returns
the
maxim
throws allowed length of a cur
name
public
abstract
getMaxIndexLength()
SQLException
int Returns
the
throws length of an
bytes
public
abstract
getMaxProcedureNameLength()
SQLException
int Returns
the
maxim
throws allowed
length
of
procedure name
public
abstract
int
throws SQLException
getMaxRowSize() Indicates
row size
public
abstract
getMaxSchemaNameLength()
SQLException
public
abstract
the
maxim
index
maxim
int Returns
the
maxim
throws allowed
length
of
schema name
int Returns
the
maxim
getMaxStatementLength()
SQLException
Returns
the
maxim
public abstract int getMaxStatements()
number
of
stateme
throws SQLException
allowed at one time
public
abstract
getMaxTableNameLength()
SQLException
int Returns
the
maxim
throws allowed length of a ta
name
int Returns
the
maxim
throws allowed length of a u
name
public
abstract
getNumericFunctions()
SQLException
String Returns
a
com
throws separated list of the m
functions available
public
abstract Returns
a
Result
ResultSet getPrimaryKeys(String catalog, object that contains
String schema, String table) throws primary keys descrip
SQLException
for the specified ta
the
ResultSet
ob
contains
the
follow
columns: catalog na
schema
name,
ta
name,
column
na
sequence number, prim
key name, and, possi
NULL
public
abstract
ResultSet Returns
a
Result
getProcedureColumns(String
catalog, object that describes
String
schemaPattern,
String catalogs
sto
procedureNamePattern,
String procedures
and
re
columnNamePattern) throws SQLException columns
matching
specified
procedureNamePatten
columnNamePattern;
ResultSet object conta
the
following
colum
catalog
name,
sche
name, procedure na
column
or
parame
name, column type, d
type,
data
na
precision, length in by
scale, radix, nullabi
and comments
public
abstract
ResultSet Returns
a
Result
getProcedures(String catalogString String object that describes
procedureNamePattern)
throws catalogs procedures;
SQLException
ResultSet object conta
the
following
colum
catalog
name,
sche
name, procedure na
empty
column,
em
column, empty colu
comments
about
procedure, and kind
procedure
public
abstract
String Return
the
databa
getProcedureTerm() throws SQLException specific
term
procedure
public abstract ResultSet
throws SQLException
public
abstract
getSchemaTerm()
getSchemas() Returns
a
Result
object that describes
schemas in a databa
the
ResultSet
ob
contains one column t
contains
the
sche
names
String Returns
the
databa
throws specific term for schem
SQLException
public
abstract
getSearchStringEscape()
SQLException
String Returns
throws characters
searching
the
for
esc
patt
String Returns
a
com
throws separated list of sys
functions in the databa
public
abstract
ResultSet Returns
a
Result
getTablePrivileges(String catalog, String object that describes
schemaPattern
schemaPattern,
String privileges
for
tableNamePattern)
matching
throws SQLException
tableNamePattern;
ResultSet object conta
the
following
colum
catalog
name,
sche
name,
table
na
grantor, grantee, type
access, and YES i
grantee can grant ot
access
the
following
colum
catalog
name,
sche
name, table name, ta
type, and comments
getTypeInfo() Returns
a
Result
object that describes
SQL data types suppor
by
the
database;
ResultSet object conta
the columns: type na
SQL data type consta
in
the
Types
cla
maximum
precis
prefix used to quote
literal,
suffix
used
quote
a
lite
parameters used to cre
the type, nullability, c
sensitivity, searchabi
signed
or
unsig
(boolean),
is
it
currency,
a
incrementable
or
n
local version of data ty
minimum scale, maxim
scale,
empty
colu
String
getURL()
public
abstract
String
throws SQLException
public
abstract
ResultSet Returns
a
Result
getVersionColumns(String
catalog, object that describes
String String table) throws SQLException specified tables colum
that are updated w
any column is updated
the table; the Result
object
contains
following columns: em
columns, column na
SQL datatype, type na
precision, column va
length in bytes, sc
and pseudoColumn or n
isReadOnly() Returns
true
if
database is in read o
mode
public
abstract
nullPlusNonNullIsNull()
SQLException
boolean Returns
true
if
throws concatenation between
NULL and non-NULL
NULL
public
abstract
nullsAreSortedAtEnd()
throws SQLException
boolean
public
abstract
nullsAreSortedAtStart()
throws SQLException
boolean
public
boolean
abstract
nullsAreSortedHigh()
throws SQLException
public
abstract
nullsAreSortedLow()
throws SQLException
boolean
public
abstract
storesLowerCaseIdentifiers()
throws SQLException
boolean
public
abstract
boolean
storesLowerCaseQuotedIdentifiers()
throws SQLException
public
abstract
storesMixedCaseIdentifiers()
SQLException
boolean
throws
public
abstract
boolean
storesMixedCaseQuotedIdentifiers()
throws SQLException
public
abstract
storesUpperCaseIdentifiers()
throws SQLException
boolean
public
abstract
boolean
storesUpperCaseQuotedIdentifiers()
throws SQLException
public
abstract
boolean
supportsAlterTableWithAddColumn()
throws SQLException
public
abstract
boolean
supportsAlterTableWithDropColumn()
throws SQLException
public
abstract
boolean
supportsAlterTableWithDropColumn()
throws SQLException
public
abstract
supportsANSI92EntryLevelSQL()
boolean
throws
SQLException
public
abstract
supportsANSI92FullSQL()
SQLException
boolean
throws
public
abstract
boolean
supportsANSI92IntermediateSQL() throws
SQLException
public
abstract
supportsANSI92FullSQL()
SQLException
boolean
throws
public
abstract
boolean
supportsCatalogsInDataManipulation()
throws SQLException
public
abstract
boolean
supportsCatalogsInIndexDefinitions()
throws SQLException
public
abstract
boolean
supportsCatalogsInPrivilegeDefinitions()
throws SQLException
public
abstract
boolean
supportsCatalogsInProcedureCalls()
throws SQLException
public
abstract
boolean
supportsCatalogsInTableDefinitions()
throws SQLException
public
abstract
supportsColumnAliasing()
SQLException
boolean
throws
public
abstract
boolean
supportsConvert() throws SQLException
public
abstract
boolean
supportsConvert(int fromType, int toType)
throws SQLException
public
abstract
boolean
supportsCoreSQLGrammar()
SQLException
throws
public
abstract
supportsCorrelatedSubqueries()
SQLException
boolean
throws
public
abstract
supportsDataDefinitionAnd
DataManipulationTransactions()
SQLException
boolean
throws
public
abstract
boolean
supportsDataManipulation
TransactionsOnly() throws SQLException
public
abstract
boolean
supportsDifferentTableCorrelationNames()
throws SQLException
public
abstract
supportsExpressionsInOrderBy()
SQLException
boolean
throws
public
abstract
supportsExtendedSQLGrammar()
SQLException
boolean
throws
public
abstract
supportsFullOuterJoins()
SQLException
boolean
throws
public
abstract
boolean
supportsGroupBy() throws SQLException
public
abstract
supportsGroupByBeyondSelect()
SQLException
boolean
throws
public
abstract
supportsGroupByUnrelated()
SQLException
boolean
throws
public
abstract
boolean
supportsIntegrityEnhancementFacility()
throws SQLException
public
abstract
supportsLikeEscapeClause()
SQLException
boolean
throws
public
abstract
supportsLimitedOuterJoins()
SQLException
boolean
throws
public
abstract
supportsMinimumSQLGrammar()
SQLException
boolean
throws
public
abstract
supportsMixedCaseIdentifiers()
SQLException
boolean
throws
public
abstract
boolean
supportsMixedCaseQuotedIdentifiers()
throws SQLException
public
abstract
supportsMultipleResultSets()
SQLException
boolean
throws
public
abstract
supportsMultipleTransactions()
SQLException
boolean
throws
public
abstract
supportsNonNullableColumns()
SQLException
boolean
throws
public
abstract
boolean
supportsOpenCursorsAcrossCommit()
throws SQLException
public
abstract
boolean
supportsOpenCursorsAcrossRollback()
throws SQLException
public
abstract
boolean
supportsOpenStatementsAcrossCommit()
throws SQLException
public
abstract
boolean
supportsOpenStatementsAcrossRollback()
throws SQLException
public
abstract
supportsOrderByUnrelated()
throws SQLException
boolean
public
abstract
supportsOuterJoins()
throws SQLException
boolean
public
abstract
supportsPositionedDelete()
throws SQLException
boolean
public
abstract
supportsPositionedUpdate()
throws SQLException
boolean
public
abstract
boolean
supportsSchemasInDataManipulation()
throws SQLException
public
abstract
boolean
supportsSchemasInProcedureCalls()
throws SQLException
public
abstract
boolean
supportsSchemasInProcedureCalls()
throws SQLException
public
abstract
boolean
supportsSchemasInTableDefinitions()
throws SQLException
public
abstract
supportsSelectForUpdate()
throws SQLException
boolean
public
abstract
supportsStoredProcedures()
throws SQLException
boolean
public
boolean
abstract
supportsSubqueriesInComparisons()
throws SQLException
public
abstract
supportsSubqueriesInExists()
throws SQLException
boolean
public
abstract
supportsSubqueriesInIns()
throws SQLException
boolean
public
abstract
boolean
supportsSubqueriesInQuantifieds()
throws SQLException
public
abstract
supportsTableCorrelationNames()
SQLException
boolean
throws
public
abstract
boolean
supportsTransactionIsolationLevel(int
level) throws SQLException
public
abstract
supportsTransactions()
SQLException
boolean
throws
public
abstract
boolean
supportsUnion() throws SQLException
public
abstract
boolean
supportsUnionAll() throws SQLException
public
abstract
usesLocalFilePerTable()
SQLException
boolean
throws
public
abstract
boolean
usesLocalFiles() throws SQLException
Variables
public final static int bestRowNotPseudo
public final static int bestRowPseudo
public final static int versionColumnUnknown
Additional Description
public
abstract
getMinorVersion()
public
abstract Returns
an
array
of
DriverPropertyInfo[]
DriverPropertyInfo
that
getPropertyInfo(String
contains
possible
URL,
Properties
props) properties based on the
throws SQLException
supplied URL and props
public
abstract
jdbcCompliant()
Method Name
Additional Description
public
abstract
clearParameters()
SQLException
public
abstract
execute()
SQLException
public abstract
executeQuery()
SQLException
void Resets
all
of
throws PreparedStatments
parameters
the
query
the
prepared
public
abstract
int Executes
the
prepared
executeUpdate()
throws query; this method is used
SQLException
public
abstract
void
setAsciiStream(int
paramIndex, InputStream
paramType,
int
length)
throws SQLException
public
abstract
void
setBinaryStream(int
paramIndex, InputStream
paramType,
int
length)
throws SQLException
public
abstract
void
setBoolean(int
paramIndex,
boolean
paramType)
throws
SQLException
public
abstract
void
setByte(int
paramIndex,
byte paramType) throws
SQLException
public
abstract
void
setBytes(int
paramIndex,
byte paramType[]) throws
SQLException
public
abstract
void
setDate(int
paramIndex,
Date paramType) throws
SQLException
public
abstract
setDouble(int
void
double
paramType)
SQLException
throws
public
abstract
void
setFloat(int
paramIndex,
float paramType) throws
SQLException
public
abstract
void
setInt(int paramIndex, int
paramType)
throws
SQLException
public
abstract
void
setLong(int
paramIndex,
long paramType) throws
SQLException
public
abstract
void
setNull(int paramIndex, int
sqlType)
throws
SQLException
public
abstract
void
setNumeric(int
paramIndex,
Numeric
paramType)
throws
SQLException
public
abstract
void
setObject(int paramIndex,
Object paramType) throws
SQLException
public
abstract
void
setObject(int paramIndex,
Object
paramType,
int
targetSqlType)
throws
SQLException
public
abstract
void
setObject(int paramIndex,
Object
paramType,
int
Methods
Method Name
Additional Description
public
abstract
clearWarnings()
SQLException
columnName)
SQLException
public
abstract
getBoolean(int
columnIndex)
SQLException
throws
column
name
columnName)
in
resulting table
the
public
abstract
boolean Fetches the result from the
getBoolean(String
current
row
in
the
columnName)
throws specified
column
(the
SQLException
column
name
columnName)
in
the
resulting table
public
abstract
byte Fetches the result from the
getByte(int columnIndex) current
row
in
the
throws SQLException
specified
column
(the
column
number
columnIndex)
in
the
resulting table
public
abstract
getByte(String
columnName)
SQLException
public
abstract
byte[] Fetches the result from the
getBytes(int columnIndex) current
row
in
the
throws SQLException
specified
column
(the
column
number
columnIndex)
in
the
resulting table
public
abstract
getBytes(String
columnName)
SQLException
throws specified
column
column
name
columnName)
in
resulting table
(the
the
public
abstract
getCursorName()
SQLException
public
abstract
Date Fetches the result from the
getDate(int columnIndex) current
row
in
the
throws SQLException
specified
column
(the
column
number
columnIndex)
in
the
resulting table
public
abstract
getDate(String
columnName)
SQLException
public
abstract
getDouble(int
columnIndex)
SQLException
public
abstract
getDouble(String
columnName)
SQLException
public
abstract
float Fetches the result from the
getFloat(int columnIndex) current
row
in
the
throws SQLException
specified
column
(the
column
number
-
columnIndex)
resulting table
public
abstract
getFloat(String
columnName)
SQLException
in
the
public
abstract
int Fetches the result from the
getInt(int
columnIndex) current
row
in
the
throws SQLException
specified
column
(the
column
number
columnIndex)
in
the
resulting table
public
abstract
int Fetches the result from the
getInt(String
current
row
in
the
columnName)
throws specified
column
(the
SQLException
column
name
columnName)
in
the
resulting table
public
abstract
long Fetches the result from the
getLong(int columnIndex) current
row
in
the
throws SQLException
specified
column
(the
column
number
columnIndex)
in
the
resulting table
public
abstract
getLong(String
columnName)
SQLException
public
abstract
Numeric Fetches the result from the
getNumeric(int
current
row
in
the
columnIndex,
int
scale) specified
column
(the
throws SQLException
column
number
columnIndex)
in
resulting table
the
public
abstract
getObject(String
columnName)
SQLException
public
abstract
short Fetches the result from the
getShort(int columnIndex) current
row
in
the
throws SQLException
specified
column
(the
column
number
columnIndex)
in
the
resulting table
public
abstract
getShort(String
columnName)
SQLException
public
abstract
String Fetches the result from the
getString(int columnIndex) current
row
in
the
throws SQLException
public
abstract
getString(String
columnName)
SQLException
specified
column
column
number
columnIndex)
in
resulting table
(the
the
public
abstract
Time Fetches the result from the
getTime(int columnIndex) current
row
in
the
throws SQLException
specified
column
(the
column
number
columnIndex)
in
the
resulting table
public
abstract
getTime(String
columnName)
SQLException
current
row
in
InputStream
specified
column
getUnicodeStream(int
column
number
columnIndex)
throws
columnIndex)
in
SQLException
resulting table
the
(the
the
public
abstract
wasNull()
SQLException
Additional Description
public
abstract
String
getCatalogName(int
Returns the name of the
column)
throws catalog hit by the query
SQLException
public
abstract
int Returns
getColumnCount() throws columns
the number of
in the resulting
SQLException
table
public
abstract
int Returns
the
specified
getColumnDisplaySize(int columns maximum size
column)
throws
SQLException
public
abstract
String
Gets a label, if it exists,
getColumnLabel(int
for the specified column in
column)
throws
the result set
SQLException
public
abstract
String
Gets
a
name
for
the
getColumnName(int
specific column number in
column)
throws
the resulting table
SQLException
public
abstract
int Returns a constant in the
getColumnType(int
Type class that is the JDBC
column)
throws type
of
the
specified
SQLException
column in the result set
public
abstract
String Gets the name of the type
getColumnTypeName(int
of the specified column in
column)
throws the result set
SQLException
public
abstract
int Returns the precision of
getPrecision(int
column) the data in the specified
throws SQLException
column, if applicable
public
abstract
int Returns the scale of the
getScale(int
column) data
in
the
specified
throws SQLException
column, if applicable
public
abstract
String Returns the name of the
getSchemaName(int
schema that was accessed
column)
throws in the query to produce
SQLException
the result set for the
specific column
public
abstract
String Returns the name of the
getTableName(int column) table
from
which
the
throws SQLException
specified column in the
boolean
Returns
true
if
the
(int
specified
column
is
throws
automatically numbered
public
abstract
isCaseSensitive
column)
SQLException
boolean Returns
true
if
the
(int specified
columns
throws contents
are
case
sensitive, if applicable
true
if
column is
the
read
public
abstract
boolean Returns true if the WHERE
isSearchable(int
column) clause can be a part of the
throws SQLException
SQL query performed on
the specified column
public
abstract
boolean Returns true if the data
isSigned(int
column) contained in the specified
throws SQLException
column in the result set is
signed, if applicable
public
abstract
boolean Returns true if a write on
isWritable(int
column) the specified column is
throws SQLException
possible
Variables
Variable Name
Additional Description
public
final
static
columnNoNulls
int
public
final
static
columnNullable
int
public
final
static
int NULL values may or may
columnNullableUnknown
not be allowed, uncertain
public interface Statement
This class is used to execute a SQL query against the database via the
Connection object. The Connection.createStatement returns a Statement
object. Methods in the Statement class produce ResultSet objects which are
used to fetch the result of a query executed in this class.
Methods
Method Name
Additional Description
Closes
the
Statement
public abstract void close() and frees its associated
throws SQLException
resources, including any
ResultSets
public abstract boolean execute(String sql) throws
SQLException
public
abstract
ResultSet Executes a query that
executeQuery(String
sql) returns
a
ResultSet
throws
SQLException
object (produces some
Executes the parameter sql,
results) using the sql
which is an SQL query; this
parameter as the SQL
method accounts for multiple
query
ResultSets
public
abstract
executeUpdate(String
throws SQLException
public
abstract
int
SQLException
public
abstract
int
getMaxRows()
throws
SQLException
Returns
the
maximum amount of data
returned
for
a
resulting Returns the maximum
column; applies only to the number
of
rows
a
following
SQL
datatypes: ResultSet can contain
BINARY,
VARBINARY,
LONGVARBINARY,
CHAR,
VARCHAR,
and
LONGVARCHAR
public
abstract
getMoreResults()
SQLException
public
abstract
SQLException
public
abstract
ResultSet Returns
a
ResultSet
getResultSet()
throws object
that
is
the
SQLException
Returns
the current result of the
number of seconds that the query; only one of these
seconds)
SQLException
throws
execute
Exceptions
Finally, we get to the exceptions. As with the other sections, the exception
listings include a description and the class constructors and methods.
public class DataTruncation
This class extends SQLWarning. An exception is produced when data
transfer is prematurely terminated on a write operation, and a warning is
generated when data transfer is prematurely terminated on a read operation.
You can use the methods contained here to provide debugging information
because the JDBC driver should throw this exception when a data transfer
problem is encountered.
Constructors
Constructor
public
DataTruncation(int Builds
a
Throwable
index, boolean parameter, DataTruncation object with
boolean read, int dataSize, the specified properties
int transferSize) Additional
Description
Methods
Method Name
Additional Description
public
getParameter()
boolean Returns
true
if
the
truncated
value
was
a
parameter, or false if it
was a column
String
String
Methods
Method Name
Additional Description
public
SQLException Returns the next exception
getNextException()
as an SQLException object
public
getSQLState()
public synchronized
setNextException
(SQLException excp)
public class SQLWarning
Additional Description
public
SQLWarning Returns an SQLWarning
getNextWarning()
object that contains the
next warning
Sets
the
next
public
void
SQLWarning
warning
setNextWarning(SQLWarning
warn for the SQLWarning
warn)
object