Java Database Programming With JDBC
Java Database Programming With JDBC
S ea rc h thi s book :
Go!
Introduction
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 drivers—hence
databases—without recoding your Java programs.
In the next chapter, we’ll 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 SQL-92, 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 2
SQL 101
SQL—the language of database. This chapter’s primary purpose is to serve as a primer
on this data sublanguage. Although it would be impossible for me to cover the intricacies
of SQL in just one chapter, I do intend to give you a solid introduction that we’ll build on
in the remainder of this book. Because the JDBC requires that drivers support the ANSI
SQL-92 standard to be “JDBC compliant,” I’ll be basing this chapter on that standard.
SQL-92, which I’ll refer to as SQL, is based on the relational model of database
management proposed in 1970 by Dr. E.F. Codd; over time, SQL evolved into the full-
featured language it is today, and it continues to evolve with our ever-changing needs.
A JDBC driver doesn’t absolutely have to be SQL-92 compliant. The JDBC specification
states the following: “In order to pass JDBC compliance tests and to be called ‘JDBC
compliant, we require that a driver support at least ANSI SQL-92 Entry Level.” This
requirement is clearly not possible with drivers for legacy database management systems
(DBMS). The driver in these cases will not implement all of the functions of a
“compliant” driver. In Chapter 10, Writing JDBC Drivers, we develop the basics of a
JDBC driver that implements only some of the features of SQL, but is a JDBC driver
nonetheless.
We’ll start our exploration of SQL by discussing the relational model, the basis for SQL.
Then we’ll cover the essentials of building data tables using SQL. Finally, we’ll go into
the manipulation and extraction of the data from a datasource.
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 employee’s number.
The link—employee number and department number—we 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 key—for example, we might
want to just use the combination of the title and city location of a department to comprise
the primary key. In SQL, columns defined as primary keys must be defined. They cannot
be “undefined” (also known as NULL).
Using Multiple Tables And Foreign Keys
As we have shown, it’s best to split data into tables so that the data contained within a
table is logically associated. Oftentimes, the data will belong logically in more than one
table, as is the case of the employee number in the EMPLOYEE and CONFIDENTIAL
tables. We can further define that if a row in one table exists, a corresponding row must
exist in another table; that is, we can say that if there is an entry in the EMPLOYEE table,
there must be a corresponding entry in the CONFIDENTIAL table. We can solidify this
association with the use of foreign keys, where a specific column in the dependent table
matches a column in a “parent” table. In essence, we are linking a “virtual” column in
one table to a “real” column in another table. In our example database, we link the
CONFIDENTIAL table’s employee number column to the employee number column in
the EMPLOYEE table. We are also specifying that the employee number is a key in the
CONFIDENTIAL table (hence the term foreign key). A composite primary key can
contain a foreign key if necessary.
We can create a logical structure to our data using the concept of a foreign key. However,
in preparation, you’ll have to put quite a bit of thought into creating your set of tables; an
efficient and planned structure to the data by way of the tables and keys requires good
knowledge of the data that is to be modeled. Unfortunately, a full discussion on the
techniques of the subject is beyond the scope of this book. There are several different
ways to efficiently model data; Figure 2.4 shows one visualization of the database we
have created. The SQL queries we perform in the examples of this book are not very
complex, so the information outlined in this section should suffice to convey a basic
understanding of the example databases created throughout the following chapters.
Figure 2.4 E-R diagram of relationships between 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. You’ll 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 doesn’t, performing a check of that data will alert us to the error. These
checks can exist in the actual table definition, but it’s efficient to localize a check in a
domain. Hence, we can add a check to our employee number domain:
CREATE DOMAIN EMP_NUMBER AS CHAR(5) CHECK (VALUE IS NOT NULL);
Now our domain automatically checks for any null entries in columns defined as
EMP_NUMBER. This statement avoids problems that crop up from non-existent entries,
as well as allowing us to catch any rogue SQL queries that add an incorrect (those that do
not set the employee number) entry to the table.
Creating Tables
Creating a table in SQL is really pretty easy. The one thing you need to keep in mind is
that you should define the referenced table, in this case EMPLOYEE, before defining the
referencing table, CONFIDENTIAL. The following code creates the EMPLOYEE table
shown in Figure 2.2:
CREATE TABLE EMPLOYEE
(
empno CHAR(5) PRIMARY KEY,
lastname VARCHAR(20) NOT NULL,
firstname VARCHAR(20) NOT NULL,
function VARCHAR(20) NOT NULL,
department VARCHAR(20)
);
We also could have easily incorporated the domain that we defined earlier into the
creation of the table, as shown here:
CREATE DOMAIN EMP_NUMBER AS CHAR(5) CHECK (VALUE IS NOT NULL);
Mastering SQL querying is not an easy task, but with the proper mind set, it is intuitive
and efficient, thanks to the relational model upon which SQL is based.
The syntax of the SELECT statement is shown here:
SELECT column_names
FROM table_names
WHERE predicates
Let’s 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;
Here’s 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'
AND c.empno = e.empno;
We have declared something like a variable using the as keyword. We can now reference
the specific fields in the table using a “.”, just like an object. Let’s begin by determining
which people in the entire company are making more than $25,000:
SELECT salary
FROM CONFIDENTIAL
WHERE salary > 25000;
Now let’s see who in the Editorial department is making more than $25,000:
SELECT c.salary
FROM EMPLOYEE as e, CONFIDENTIAL as c
WHERE e.department = 'Editorial'
AND c.empno = e.empno
AND c.salary > 25000;
You can perform a number of other functions in SQL, including averages. Here’s how to
get the average salary of the people in the Editorial department:
SELECT AVG (c.salary)
FROM EMPLOYEE as e, CONFIDENTIAL as c
WHERE e.department = 'Editorial'
AND c.empno = e.empno;
Of course, the possibilities with SQL exceed the relatively few examples shown in this
chapter. Because this book’s goal is to introduce the JDBC specifically, I didn’t use
complex queries in the examples. And now our discussion on SQL is complete. If you are
interested in learning more about SQL, I recommend that you check out our book’s
Website, where I have posted a list of recommended books on the topic of SQL and
distributed databases.
Coming Up Next
The next chapter begins our journey into JDBC. I’ll show you how to use JDBC drivers
for connecting to data sources. Then we’ll cover installing drivers, as well as the proper
way to use drivers that are dynamically fetched with an applet. Finally, we’ll discuss the
security restrictions of using directly downloaded drivers as opposed to locally installed
drivers.
Chapter 3
Using JDBC Drivers
As a developer who’s 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. We’ll be covering the details of
getting JDBC drivers to work, as well as the driver registration process we touched on in
Chapter 1. We’ll also take some time to explore JavaSoft’s 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 book’s 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, don’t forget that you’ll need
ODBC drivers, as well. If you don’t have a database server, but you want to use JDBC,
don’t 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 applet’s class file. I’ll provide a more thorough discussion of this point in Chapter
9.
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()) {
}
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 don’t 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 computer’s hard disk. The method
shown here allows you to compile and run Java applications and applets (using the
Appletviewer) that use the JDBC:
1. Download the JDBC API package from the JavaSoft
Web site or make a copy of the file from the CD-ROM.
2. On your hard drive, locate the directory that stores
the Java API packages. (On my PC, the directory is
C:\JAVA\SRC, and on my Sun box, the directory is
\usr\local\java\src.) You do not need to install the JDBC
API package in the same directory as the rest of the
Java API, but I strongly recommend that you do
because, as I mentioned earlier, the JDBC API will soon
be a standard part of the Java API distribution and will
be packaged in the Java API hierarchy.
3. Unpack the JDBC API classes using one of the
following methods (for Unix-based machines or PCs),
substituting the location where you downloaded the
JDBC class file and the location where you want to
install the JDBC classes.
Unix Procedure:
• To upack the file, enter prompt> uncompress
\home\prpatel\jdbc.100.tar.Z .
• To create a jdbc directory with the classes and their
source in separate directories, enter prompt> tar xvf
\home\prpatel\jdbc.100.tar.Z .
• To install the JDBC classes, enter prompt> cd
\usr\local\java\src , then enter prompt> mv
\home\prpatel\jdbc\classes\java , and finally enter
prompt> mv \home\prpatel\jdbc\src\java .
Windows 95 Procedure:
• Using a Windows 95 ZIP utility such as WinZip,
uncompress and untar the file. Be sure the file name
ends with .tar when you uncompress the file so that
utilities will recognize the file. Untar the file to a
tempory folder. Then do the following:
• Copy the java folder from the JDBC\CLASSES
directory (from the temp directory where you untarred
the downloaded file) to the C:\JAVA\SRC directory.
• Copy the java folder from the JDBC\SRC directory to
C:\JAVA\SRC.
4. Set the CLASSPATH to point to c:/usr/local/java/src
(for Unix-based machines) or C:\JAVA\SRC (for PCs).
Again, remember to substitute your location if this is
not where you installed the downloaded file.
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.
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 4
The Interactive
SQL Applet
Now that you have seen how to use JDBC drivers, it’s time we ante up. In this chapter,
we jump into the JDBC with an example applet that we’ll 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, we’ll be saving
ourselves a lot of trouble later on by figuring out just the right way to get to where we
want to go.
Finishing Up
One of Java’s great strengths lies in its ability to automatically allocate and de-allocate
memory for objects created in the program, so the programmer doesn’t 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.
import java.sql.*;
// These are the packages needed to load the JDBC kernel, known as the
// DriverManager.
import imaginary.sql.*;
// These are the actual driver classes! We are using the msql JDBC
// drivers to access our msql database.
setLayout(gridbag);
// Set the layout of the applet to the gridbag that we created above.
setFont(new Font("Helvetica", Font.PLAIN, 12));
setBackground(Color.gray);
// Set the font and color of the applet.
Con.weightx=1.0;
Con.weighty=0.0;
Con.anchor = GridBagConstraints.CENTER;
Con.fill = GridBagConstraints.NONE;
Con.gridwidth = GridBagConstraints.REMAINDER;
This code requires some explanation. The weightx and weighty properties determine
how the space in the respective direction is distributed. If either weight property is set to
0, the default, any extra space is distributed around the outside of the components in the
corresponding direction. The components are also centered automatically in that
direction. If either weight property is set to 1, any extra space is distributed within the
spaces between components in the corresponding direction. Hence, in setting the
weightx=1, we have told the GridBagLayout layout manager to position the
components on each row so that extra space is added equally between the components on
that row. However, the rows of components are vertically “clumped” together because
the weighty property is set to 0.0. Later on, we’ll change weighty to 1 so that the large
TextArea (the OutputField) takes up extra space equal to all the components added
before it. Take a look at Figure 4.3, shown at the end of the chapter, to see what I mean.
We also set the anchor property to tell the GridBagLayout to position the components
on the center, relative to each other. The fill property is set to NONE so that the
components are not stretched to fill empty space. You will find this technique to be useful
when you want a large graphics area (Canvas) to take up any empty space that is
available around it, respective to the other components. The gridwidth is set to
REMAINDER to signal that any component assigned the GridBagContstraint Con
takes up the rest of the space on a row. Similarly, we can set gridheight to
REMAINDER so that a component assigned this constraint takes up the remaining
vertical space. The last detail associated with GridBagLayout involves assigning the
properties to the component. This is done via the setConstraints method in
GridBagLayout.
Listing 4.4 shows how we do this. Notice that we assign properties for the TextArea, but
not for the Labels. Because we’re positioning the Labels on the “right” side of the screen
(the default), there is no need to assign constraints. There are more properties you can set
with GridBagLayout, but it’s beyond the scope of this book.
Listing 4.4 Assigning properties to components.
add(new Label("Name"));
gridbag.setConstraints(NameField, Con);
add(NameField);
// Note that we did not setConstraints for the Label. The GridbagLayout
// manager assumes they carry the default constraints. The NameField is
// assigned to be the last component on its row via the constraints Con,
// then added to the user interface.
gridbag.setConstraints(ConnectBtn, Con);
add(ConnectBtn);
// Here, we only want the ConnectBtn button on a row, by itself, so we
// set the constraints, and add 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 let’s show it! We also don’t 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
// all the properties are set.
if (evt.target == NameField)
{char c=(char)evt.key;
// Look for the Enter key pressed in the NameField.
if (c == '\n')
{ Name=NameField.getText();
// Set the global Name variable to the contents in the NameField.
return true;
}
else { return false; }
}
if (evt.target == DBurl)
{char c=(char)evt.key;
// Look for the enter key pressed in the DBurl TextArea.
if (c == '\n')
{ url=DBurl.getText();
// Set the global url variable to the contents of the DBurl TextArea.
return true;
}
else { return false; }
}
if (evt.target == QueryField)
{char c=(char)evt.key;
// Look for the Enter key pressed in the QueryField.
if (c == '\n')
{
OutputField.setText(Select(QueryField.getText()));
// Get the contents of the QueryField, and pass them to the Select
// method that is defined in Listing 4.7. The Select method executes the
// entered query, and returns the results. These results are shown in
the
// OutputField using the setText method.
return true;
}
else { return false; }
}
url=DBurl.getText();
Name=NameField.getText();
try {
new imaginary.sql.iMsqlDriver();
// This creates a new instance of the Driver we want to use. There are a
// number of ways to specify which driver you want to use, and there is
// even a way to let the JDBC DriverManager choose which driver it
thinks
// it needs to connect to the data source.
return true;
}
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), it’s 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 String—however, 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,
I’ll 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.
public String Select(String QueryLine) {
// This is the method we called above in Listing 4.5.
// We return a String, and use a String parameter for the entered query.
String Output="";
int columns;
int pos;
try {
// Several of the following methods can throw exceptions if there was a
// problem with the query, or if the connection breaks, or if
// we improperly try to retrieve results.
ResultSet rs = stmt.executeQuery(QueryLine);
// The ResultSet in turn is linked to the connection to the data source
// via the Statement class. The Statement class contains the
executeQuery
// method, which returns a ResultSet class. This is analagous to a
// pointer that can be used to retrieve the results from the JDBC
// connection.
columns=(rs.getMetaData()).getColumnCount();
// Here we use the getMetaData method in the result set to return a
// Metadata object. The MetaData object contains a getColumnCount
// method which we use to determine how many columns of data
// are present in the result. We set this equal to an integer
// variable.
Listing 4.8 Getting the Result and MetaData Information.
while(rs.next()) {
// Now, we use the next method of the ResultSet instance rs to fetch
// each row, one by one. There are more optimized ways of doing
// this--namely using the inputStream feature of the JDBC driver.
// I show you an example of this in Chapter 9.
Output+=rs.getObject(pos)+" ";
// Here we've used the general method for getting a result. The
// getObject method will attempt to caste the result in the form
// of its assignee, in this case the String variable Output.
// We simply get each "cell" and add a space to it, then append it onto
// the Output variable.
}
// End for loop (end looping through the columns for a specific row ).
Output+="\n";
// For each row that we fetch, we need to add a carriage return so that
// the next fetched row starts on the next line.
}
// End while loop ( end fetching rows when no more rows are left ).
stmt.close();
// Clean up, close the stmt, in effect, close the input-output query
// connection streams, but stay connected to the data source.
}
catch( Exception e ) {
e.printStackTrace();
Output=e.getMessage();
}
// We have to catch any exceptions that were thrown while we were
// querying or retrieving the data. Print the exception
// to the console and return it so it can be shown to the user
// in the applet.
return Output;
// Before exiting, return the result that we got.
}
Wrapping It Up
The last part of the applet, shown in Listing 4.9, involves terminating the connection to
the data source. This is done in the destroy method of the applet. We have to catch an
exception, if one occurs, while the close method is called on the connection.
Listing 4.9 Terminating the connection.
public void destroy() {
try {con.close();}
catch( Exception e ) {
e.printStackTrace();
System.out.println(e.getMessage());
}
} // end destroy
} // end applet
<hr>
</BODY>
</HTML>
Coming Up Next
In the next chapter, we’ll explore the bridge between ODBC and JDBC. You’ll see how
easy it is to use existing ODBC drivers with JDBC, and learn some of the fine points of
the relation, similarity, and difference between the two database connectivity standards.
You won’t want to miss this one; the author, Karl Moss, is also the author of the
Sun/Intersolv ODBC-JDBC bridge included in the JDBC package.
Chapter 5
Accessing ODBC Services Using JDBC
One of JavaSoft’s 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 driver—the Bridge—is now included with the Java Developer’s kit.
The Bridge works great, but there are some things you need to understand before you can
implement it properly. In this chapter, we’ll 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. I’ll 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 library’s 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 Java’s major advantages—
portability. 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. Here’s
a short checklist of required components:
• The Java Developer’s 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 ODBCTE32.EXE)
which can fully exercise ODBC data sources on Win95/NT.
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 can’t support it. The subname specifies the
ODBC data source name to use, followed by any additional connection string attributes.
Here’s 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
// DriverManager. We can now let the DriverManager choose the right
// driver to connect to the given URL.
prop.put("UID", "dept12");
prop.put("PWD", "Julie");
Table 5.3
DatabaseMetaData
ODBC calls. JDBC
Interface
Method
ODBC Call Comments
allProceduresAreCallabl SQLGetInf fInfoType =
e o SQL_ACCESSABLE_
PROCEDURES
allTablesAreSelectable SQLGetInf fInfoType =
o SQL_ACCESSABLE_
TABLES
SQLGetInf fInfoType =
getUserName
o SQL_USER_NAME
isReadOnly SQLGetInf fInfoType = SQL_DATA_
o SOURCE_READ_ONLY
nullsAreSortedHigh SQLGetInf fInfoType =
o SQL_NULL_COLLATION;
result must be
SQL_NC_HIGH
nullsAreSortedLow SQLGetInf fInfoType =
o SQL_NULL_COLLATION;
result must be
SQL_NC_LOW
nullsAreSortedAtStart SQLGetInf fInfoType =
o SQL_NULL_COLLATION;
result must be
SQL_NC_START
nullsAreSortedAtEnd SQLGetInf fInfoType =
o SQL_NULL_COLLATION;
result must be
SQL_NC_END
getDatabaseProductNa SQLGetInf fInfoType =
me o SQL_DBMS_NAME
getDatabaseProductVer SQLGetInf fInfoType =
sion o SQL_DBMS_VER
usesLocalFiles SQLGetInf fInfoType =
o SQL_FILE_USAGE; the
result must be
SQL_FILE_QUALIFIER
usesLocalFilePerTable SQLGetInf fInfoType =
o SQL_FILE_USAGE; the
result must be
SQL_FILE_TABLE
supportsMixedCaseIden SQLGetInf fInfoType =
tifiers o SQL_IDENTIFIER_CASE;
the result must be
SQL_IC_UPPER,
SQL_IC_LOWER or
SQL_IC_MIXED
storesUpperCaseIdentifi SQLGetInf fInfoType =
ers o SQL_IDENTIFIER_CASE,
the result must be
SQL_IC_UPPER
storesLowerCaseIdentifi SQLGetInf fInfoType =
ers o SQL_IDENTIFIER_CASE;
the result must be
SQL_IC_LOWER
storesMixedCaseIdentifi SQLGetInf fInfoType =
ers o SQL_IDENTIFIER_CASE;
the result must be
SQL_IC_MIXED
supportsMixedCaseQuot SQLGetInf fInfoType =
ed o SQL_QUOTED_IDENTIFIER
Identifiers _CASE; the result must
be SQL_IC_UPPER,
SQL_IC_LOWER, or
SQL_IC_MIXED
storesUpperCaseQuoted SQLGetInf fInfoType =
Identifiers o SQL_QUOTED_IDENTIFIER
_CASE; the result must
be SQL_IC_UPPER
storesLowerCaseQuoted SQLGetInf fInfoType =
Identifiers o SQL_QUOTED_IDENTIFIER
_CASE; the result must
be SQL_IC_LOWER
storesMixedCaseQuoted SQLGetInf fInfoType =
Identifiers o SQL_QUOTED_IDENTIFIER
_CASE; the result must
be SQL_IC_MIXED
getIdentifierQuoteStrin SQLGetInf fInfoType =
g o SQL_IDENTIFIER_QUOTE_
CHAR
SQLGetInf fInfoType =
getSQLKeywords
o SQL_KEYWORDS
getNumericFunctions SQLGetInf fInfoType =
o SQL_NUMERIC_FUNCTION
S; the result is a bitmask
enumerating the scalar
numeric functions; this
bitmask is used to create
a comma-separated list of
functions
getStringFunctions SQLGetInf fInfoType =
o SQL_STRING_FUNCTIONS
; the result is a bitmask
enumerating the scalar
string functions; this
bitmask is used to create
a comma-separated list of
functions
getSystemFunctions SQLGetInf fInfoType =
o SQL_SYSTEM_
FUNCTIONS; the result is
a bitmask enumerating
the scalar system
functions; this bitmask is
used to create a comma-
separated list of functions
getTimeDateFunctions SQLGetInf fInfoType =
o SQL_TIMEDATE_
FUNCTIONS; the result is
a bitmask enumerating
the scalar date and time
functions; This bitmask is
used to create a comma-
separated list of functions
getSearchStringEscape SQLGetInf fInfoType =
o SQL_SEARCH_PATTERN_
ESCAPE
getExtraNameCharacter SQLGetInf fInfoType =
s o SQL_SPECIAL_
CHARACTERS
supportsAlterTableWith SQLGetInf fInfoType =
Add o SQL_ALTER_TABLE; result
Column must have the
SQL_AT_ADD_COLUMN bit
set
supportsAlterTableWith SQLGetInf fInfoType
Drop o =SQL_ALTER_TABLE; the
Column result must have the
SQL_AT_DROP_
COLUMN bit set
SQLGetInf fInfoType =
supportsColumnAliasing
o SQL_COLUMN_ALIAS
nullPlusNonNullIsNull SQLGetInf fInfoType =
o SQL_CONCAT_NULL_BEHA
VIOR; the result must be
SQL_CB_NULL
supportsConvert SQLGetInf fInfoType =
o SQL_CONVERT_
FUNCTIONS; the result
must be
SQL_FN_CVT_CONVERT
supportsTableCorrelatio SQLGetInf fInfoType =
n o SQL_CORRELATION_
Names NAME; the result must be
SQL_CN_
DIFFERENT or
SQL_CN_ANY
supportsDifferentTable SQLGetInf fInfoType =
CorrelationNames o SQL_CORRELATION_
NAMES; the result must
be SQL_CN_
DIFFERENT
supportsExpressionsIn SQLGetInf fInfoType =
OrderBy o SQL_EXPRESSIONS_
IN_ORDER_BY
supportsOrderByUnrelat SQLGetInf fInfoType =
ed o SQL_ORDER_BY_
COLUMNS_IN_SELECT
supportsGroupBy SQLGetInf fInfoType =
o SQL_GROUP_BY; the
result must not be
SQL_GB_NOT_
SUPPORTED
supportsGroupByUnrela SQLGetInf fInfoType =
ted o SQL_GROUP_BY; the
result must be
SQL_GB_NO_
RELATION
supportsGroupByBeyon SQLGetInf fInfoType =
d o SQL_GROUP_BY; the
Select result must be
SQL_GB_GROUP_BY_
CONTAINS_SELECT
supportsLikeEscapeClau SQLGetInf fInfoType =
se o SQL_LIKE_ESCAPE_
CLAUSE
supportsMultipleResultS SQLGetInf fInfoType =
ets o SQL_MULT_RESULT_
SETS
supportsMultipleTransa SQLGetInf fInfoType =
ctions o SQL_MULTIPLE_
ACTIVE_TXN
supportsNonNullableCol SQLGetInf fInfoType = SQL_NON_
umns o NULLABLE_COLUMNS; the
result must be
SQL_NNC_NON_
NULL
supportsMinimumSQL SQLGetInf fInfoType =
Grammar o SQL_ODBC_SQL_
CONFORMANCE; result
must be
SQL_OSC_MINIMUM,
SQL_OSC_CORE, or
SQL_OSC_EXTENDED
supportsCoreSQLGram SQLGetInf fInfoType = SQL_ODBC_
mar o SQL_CONFORMANCE; the
result must be
SQL_OSC_CORE or
SQL_OSC_EXTENDED
supportsExtendedSQL SQLGetInf fInfoType = SQL_ODBC_
Grammar o SQL_CONFORMANCE; the
result must be SQL_OSC_
EXTENDED
supportsIntegrityEnhan SQLGetInf fInfoType =
cement o SQL_ODBC_SQL_
Facility OPT_IEF
supportsOuterJoins SQLGetInf fInfoType =
o SQL_OUTER_JOINS; the
result must not be “N”
supportsFullOuterJoins SQLGetInf fInfoType =
o SQL_OUTER_JOINS; the
result must be “F”
supportsLimitedOuterJo SQLGetInf fInfoType =
ins o SQL_OUTER_JOINS; the
result must be “P”
SQLGetInf fInfoType =
getSchemaTerm
o SQL_OWNER_TERM
SQLGetInf fInfoType =
getProcedureTerm
o SQL_PROCEDURE_TERM
SQLGetInf fInfoType =
getCatalogTerm
o SQL_QUALIFIER_TERM
isCatalogAtStart SQLGetInf fInfoType =
o SQL_QUALIFIER_
LOCATION; the result
must be SQL_QL_START
getCatalogSeparator SQLGetInf fInfoType =
o SQL_QUALIFIER_NAME_
SEPARATOR
supportsSchemasInDat SQLGetInf fInfoType =
a o SQL_OWNER_USAGE; the
Manipulation result must have the
SQL_OU_DML_
STATEMENTS bit set
supportsSchemasInProc SQLGetInf fInfoType =
edure o SQL_OWNER_USAGE; the
Calls result must have the
SQL_OU_
PROCEDURE_INVOCATION
bit set
supportsSchemasInTabl SQLGetInf fInfoType =
e o SQL_OWNER_USAGE; the
Definitions result must have the
SQL_OU_TABLE_
DEFINITION bit set
supportsSchemasInInde SQLGetInf fInfoType =
x o SQL_OWNER_USAGE; the
Definitions result must have the
SQL_OU_INDEX_
DEFINITION bit set
supportsSchemasInPrivi SQLGetInf fInfoType =
lege o SQL_OWNER_USAGE; the
Definitions result must have the
SQL_OU_
PRIVILEGE_DEFINITION
bit set
supportsCatalogsInData SQLGetInf fInfoType =
Manipulation o SQL_QUALIFIER_USAGE;
the result must have the
SQL_QU_DML_STATEMENT
S bit set
supportsCatalogsInProc SQLGetInf fInfoType =
edure o SQL_QUALIFIER_USAGE;
Calls the result must have the
SQL_QU_
PROCEDURE_INVOCATION
bit set
supportsCatalogsInTabl SQLGetInf fInfoType =
e o SQL_QUALIFIER_
Definitions USAGE; the result must
have the
SQL_QU_TABLE_DEFINITI
ON bit set
supportsCatalogsInInde SQLGetInf fInfoType =
x o SQL_QUALIFIER_USAGE;
Definitions the result must have the
SQL_QU_INDEX_DEFINITI
ON bit set
supportsCatalogsInPrivi SQLGetInf fInfoType =
lege o SQL_QUALIFIER_USAGE;
Definitions the result must have the
SQL_QU_
PRIVILEGE_DEFINITION
bit set
supportsPositionedDelet SQLGetInf fInfoType =
e o SQL_POSITIONED_
STATEMENTS; the result
must have the
SQL_PS_POSITIONED_DEL
ETE bit set
supportsPositionedUpda SQLGetInf fInfoType =
te o SQL_POSITIONED_
STATEMENTS; the result
must have the
SQL_PS_POSITIONED_UP
DATE bit set
supportsSelectForUpdat SQLGetInf fInfoType =
e o SQL_POSITIONED_
STATEMENTS; the result
must have the
SQL_PS_SELECT_FOR_UP
DATE bit set
supportsStoredProcedur SQLGetInf fInfoType =
es o SQL_PROCEDURES
supportsSubqueriesIn SQLGetInf fInfoType =
Comparisons o SQL_SUBQUERIES; the
result must have the
SQL_SQ_
COMPARISON bit set
supportsSubqueriesInE SQLGetInf fInfoType =
xists o SQL_SUBQUERIES; the
result must have the
SQL_SQ_EXISTS bit set
supportsSubqueriesInIn SQLGetInf fInfoType =
s o SQL_SUBQUERIES; the
result must have the
SQL_SQ_IN bit set
supportsSubqueriesIn SQLGetInf fInfoType =
Quantifieds o SQL_SUBQUERIES; the
result must have the
SQL_SQ_
QUANTIFIED bit set
supportsCorrelatedSubq SQLGetInf fInfoType =
ueries o SQL_SUBQUERIES; the
result must have the
SQL_SQ_
CORRELATED_SUBQUERIE
S bit set
supportsUnion SQLGetInf fInfoType = SQL_UNION;
o the result must have the
SQL_U_UNION bit set
supportsUnionAll SQLGetInf fInfoType = SQL_UNION;
o the result must have the
SQL_U_UNION_ALL bit set
supportsOpenCursors SQLGetInf fInfoType =
Across Commit o SQL_CURSOR_COMMIT_
BEHAVIOR; the result
must be
SQL_CB_PRESERVE
supportsOpenCursors SQLGetInf fInfoType =
Across Rollback o SQL_CURSOR_
ROLLBACK_BEHAVIOR;
the result must be
SQL_CB_PRESERVE
supportsOpenStatement SQLGetInf fInfoType =
s o SQL_CURSOR_
Across Commit COMMIT_BEHAVIOR; the
result must be
SQL_CB_PRESERVE or
SQL_CB_CLOSE
supportsOpenStatement SQLGetInf fInfoType =
s o SQL_CURSOR_
Across Rollback ROLLBACK_BEHAVIOR;
the result must be
SQL_CB_PRESERVE or
SQL_CB_CLOSE
getMaxBinaryLiteralLen SQLGetInf fInfoType =
gth o SQL_MAX_BINARY_
LITERAL_LEN
getMaxCharLiteralLengt SQLGetInf fInfoType =
h o SQL_MAX_CHAR_
LITERAL_LEN
getMaxColumnNameLen SQLGetInf fInfoType =
gth o SQL_MAX_COLUMN_
NAME_LEN
getMaxColumnsInGroup SQLGetInf fInfoType =
By o SQL_MAX_COLUMNS_
IN_GROUP_BY
getMaxColumnsInIndex SQLGetInf fInfoType =
o SQL_MAX_COLUMNS_
IN_INDEX
getMaxColumnsInOrder SQLGetInf fInfoType =
By o SQL_MAX_COLUMNS_
IN_ORDER_BY
getMaxColumnsInSelect SQLGetInf fInfoType =
o SQL_MAX_COLUMNS_
IN_SELECT
getMaxColumnsInTable SQLGetInf fInfoType =
o SQL_MAX_COLUMNS_
IN_TABLE
getMaxConnections SQLGetInf fInfoType = SQL_ACTIVE_
o CONNECTIONS
getMaxCursorNameLeng SQLGetInf fInfoType =
th o SQL_MAX_CURSOR_
NAME_LEN
SQLGetInf fInfoType =
getMaxIndexLength
o SQL_MAX_INDEX_SIZE
getMaxSchemaNameLen SQLGetInf fInfoType =
gth o SQL_MAX_OWNER_
NAME_LEN
getMaxProcedureNameL SQLGetInf fInfoType = SQL_MAX_
ength o PROCEDURE_NAME_LEN
getMaxCatalogNameLen SQLGetInf fInfoType = SQL_MAX_
gth o QUALIFIER_NAME_LEN
SQLGetInf fInfoType =
getMaxRowSize
o SQL_MAX_ROW_SIZE
doesMaxRowSizeInclude SQLGetInf fInfoType =
Blobs o SQL_MAX_ROW_SIZE_
INCLUDES_LONG
getMaxStatementLengt SQLGetInf fInfoType = SQL_MAX_
h o STATEMENT_LEN
getMaxStatements SQLGetInf fInfoType = SQL_ACTIVE_
o STATEMENTS
getMaxTableNameLengt SQLGetInf fInfoType =
h o SQL_MAX_TABLE_
NAME_LEN
getMaxTablesInSelect SQLGetInf fInfoType =
o SQL_MAX_TABLES_
IN_SELECT
getMaxUserNameLength SQLGetInf fInfoType =
o SQL_MAX_USER_
NAME_LEN
getDefaultTransactionIs SQLGetInf fInfoType =
olation o SQL_DEFAULT_TXN_
ISOLATION
supportsTransactions SQLGetInf fInfoType =
o SQL_TXN_CAPABLE; the
result must not be
SQL_TC_NONE
supportsTransactionIsol SQLGetInf fInfoType =
ation o SQL_TXN_ISOLATION_
Level OPTION
supportsDataDefinition SQLGetInf fInfoType =
And o SQL_TXN_CAPABLE; the
DataManipulationTransa result must have the
ctions SQL_TC_ALL bit set
supportsDataManipulati SQLGetInf fInfoType =
on o SQL_TXN_CAPABLE; the
TransactionsOnly result must have the
SQL_TC_DML bit set
dataDefinitionCauses SQLGetInf fInfoType =
Transaction Commit o SQL_TXN_CAPABLE; the
result must have the
SQL_TC_DDL_COMMIT bit
set
dataDefinition SQLGetInf fInfoType =
IgnoredIn Transactions o SQL_TXN_CAPABLE; the
result must have the
SQL_TC_DDL_IGNORE bit
set
getProcedures SQL Returns a list of
Procedures procedure names
getProcedureColumns SQLProced Returns a list of input and
ure output parameters used
Columns for procedures
getTables SQLTables Returns a list of tables
getSchemas SQLTables Catalog = “”, Schema =
“%”, Table = “”,
TableType = NULL; only
the TABLE_SCHEM column
is returned
getCatalogs SQLTables Catalog = “%”, Schema =
“”, Table = “”, TableType
= NULL; only the
TABLE_CAT column is
returned
getTableTypes SQLTables Catalog = “”, Schema =
“”, Table = “”, TableType
= “%”
getColumns SQLColum Returns a list of column
ns names in specified tables
getColumnPrivileges SQLColum Returns a list of columns
n and associated privileges
Privileges for the specified table
getTablePrivileges Returns a list of tables
SQLTable and the privileges
Privileges associated with each
table
getBestRowIdentifier SQLSpecial fColType =
Columns SQL_BEST_ROWID
getVersionColumns SQLSpecial fColType = SQL_ROWVER
Columns
getPrimaryKeys SQLPrimar Returns a list of column
y names that comprise the
Keys primary key for a table
getImportedKeys SQLForeig PKTableCatalog = NULL,
n PKTableSchema = NULL,
Keys PKTableName = NULL
getExportedKeys SQLForeig FKTableCatalog = NULL,
n FKTableSchema = NULL,
Keys FKTableName = NULL
getCrossReference SQLForeig
Returns a list of foreign
n
keys in the specified table
Keys
SQLGetTyp
fSqlType =
getTypeInfo e
SQL_ALL_TYPES
Info
ge tInde xInfo
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
fOption =
getMaxFieldSize SQLGetStmtOption
SQL_MAX_LENGTH
fOption =
setMaxFieldSize SQLSetStmtOption
SQL_MAX_LENGTH
fOption =
getMaxRows SQLGetStmtOption
SQL_MAX_ROWS
fOption =
setMaxRows SQLSetStmtOption
SQL_MAX_ROWS
setEscapeProcessin fOption =
SQLSetStmtOption
g SQL_NOSCAN
getQueryTimeout SQLGetStmtOption fOption =
SQL_QUERY_TIMEOUT
setQueryTimeout SQLSetStmtOption fOption =
SQL_QUERY_TIMEOUT
cancel SQLCancel Cancels the
processing on a
statement
setCursorName SQLSetCursorNam Associates a cursor
e name with a
statement
execute SQLExecDirect The Bridge checks for
a SQL statement
containing a ‘FOR
UPDATE’ clause; if
present, the cursor
concurrency level for
the statement is
changed to
SQL_CONCUR_LOCK
getUpdateCount SQLRowCount Returns the number
of rows affected by
an UPDATE, INSERT,
or DELETE statement
ge tMo r eR e sults
SQLMoreResults
Determines whether
there are more
results available on
a statement and, if
so, initializes
processing for those
results
Table 5.5
PreparedStatemen
t ODBC calls.
JDBC Interface
Method
ODBC Call Comments
setNull SQLBindParamete fParamType =
r SQL_PARAM_INPUT;
fSqlType = sqlType
passed as parameter
setBoolean
setByte
setShort
setInt
setLong
setFloat
setDouble
setNumeric
setString
setBytes
setDate
setTime
setTimestamp SQLBindParamete fParamType =
r SQL_PARAM_INPUT;
fSqlType is derived
by the type of get
method
setAsciiStream
setUnicodeStream
setBinaryStream SQLBindParamete fParamType =
r SQL_PARAM_INPUT,
pcbValue =
SQL_DATA_AT_EXEC
SQ
L Ex
e xe cute
ecu
te
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 ODBC Call Comments
CallableStatement
ODBC calls. JDBC
Interface
Method
Table 5.4
Statement ODBC
calls. JDBC
Interface Method
ODBC Call Comments
close SQLFreeStmt fOption = SQL_CLOSE
fOption =
getMaxFieldSize SQLGetStmtOption
SQL_MAX_LENGTH
fOption =
setMaxFieldSize SQLSetStmtOption
SQL_MAX_LENGTH
fOption =
getMaxRows SQLGetStmtOption
SQL_MAX_ROWS
fOption =
setMaxRows SQLSetStmtOption
SQL_MAX_ROWS
setEscapeProcessin fOption =
SQLSetStmtOption
g SQL_NOSCAN
getQueryTimeout SQLGetStmtOption fOption =
SQL_QUERY_TIMEOUT
setQueryTimeout SQLSetStmtOption fOption =
SQL_QUERY_TIMEOUT
cancel SQLCancel Cancels the
processing on a
statement
setCursorName SQLSetCursorNam Associates a cursor
e name with a
statement
execute SQLExecDirect The Bridge checks for
a SQL statement
containing a ‘FOR
UPDATE’ clause; if
present, the cursor
concurrency level for
the statement is
changed to
SQL_CONCUR_LOCK
getUpdateCount SQLRowCount Returns the number
of rows affected by
an UPDATE, INSERT,
or DELETE statement
ge tMo r eR e sults
SQLMoreResults
Determines whether
there are more
results available on
a statement and, if
so, initializes
processing for those
results
Table 5.5
PreparedStatemen
t ODBC calls.
JDBC Interface
Method
ODBC Call Comments
setNull SQLBindParamete fParamType =
r SQL_PARAM_INPUT;
fSqlType = sqlType
passed as parameter
setBoolean
setByte
setShort
setInt
setLong
setFloat
setDouble
setNumeric
setString
setBytes
setDate
setTime
setTimestamp SQLBindParamete fParamType =
r SQL_PARAM_INPUT;
fSqlType is derived
by the type of get
method
setAsciiStream
setUnicodeStream
setBinaryStream SQLBindParamete fParamType =
r SQL_PARAM_INPUT,
pcbValue =
SQL_DATA_AT_EXEC
SQ
L Ex
e xe cute
ecu
te
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 Comments
r e giste r O utPar ame te r
SQLBindParameter
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 5.7
ResultSet ODBC
calls. JDBC
Interface
Method
ODBC Call Comments
next SQLFetch Fetches a row of data
from a ResultSet
close SqlFreeStmt 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
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
ge tC ur so r N ame
SQLGetCursorNam
e Returns the
cursor name
associated with
the statement
Chapter 6
SQL Data Types In Java And ORM
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, we’ll take
a look at the classes in the JDBC that are used to access SQL data types. In addition,
we’ll 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 types—like int, long,
and String—from 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.
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
java.sql.Timestamp
Now that you’ve seen how these data types translate from Java to SQL and vice versa,
let’s look at some of the methods that you’ll 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. You’ll 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
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 query’s 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 Description
class. Method
Summary
As you can see from this brief chapter, mapping SQL data types to Java is truly a snap.
We covered a few of the more important methods you will use to retrieve data from a
database. For a complete reference, see Chapter 12 and have a look at the Date, Time,
TimeStamp, Types, and Numeric classes.
The next chapter steps back from the JDBC to look at ways of presenting your data in
Java. Using Java packages available on the Net, we’ll cover graphs, tables, and more.
We’ll also discuss some nifty methods in the JDBC that will help streamline your code
for retrieving data from your database.
Chapter 7
Working With Query Results
So far, we’ve been concentrating on how to use the classes in the JDBC to perform SQL
queries. That’s great, but now we have to do something with the data we’ve 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, we’ll 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 we’ll 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. We’ll 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, we’ll 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, we’ll 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.
boolean more;
try {
ResultSet rs = stmt.executeQuery(QueryLine);
// Execute the passed in query, and get
// the ResultSet for the query.
columns=(rs.getMetaData()).getColumnCount();
// Get the number of columns in the resulting table so we can
// declare the column String array, and so we can loop
// through the results and retrieve them.
more=rs.next();
// Get the first row of the ResultSet. Loop through the
ResultSet
// and get the data, row-by-row, column-by-column.
while(more) {
more=rs.next();
// Get the next row of the result if it exists.
int i;
ConnectToDB();
// Connect to the database.
add("North", OutputField);
// Add the TextArea for showing the data to the user
String columnData[] = getData("select * from Employee");
// Run a query that goes and gets the complete table listing; we can
put
// any query here and would optimally want to get only the columns we
// need.
ShowFormattedData(columnData);
// Show the data in the TextArea
ShowChartData(columnData[3],columnData[2]);
// Now, pass the two data sets and create a bar chart
add("Center", bar);
// And add the bar chart to the applet's panel
}
int i;
try {
new imaginary.sql.iMsqlDriver();
con = DriverManager.getConnection(url, "prpatel", "");
}
catch( Exception e ) {
e.printStackTrace();
System.out.println(e.getMessage());
}
bar.init();
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 = ('Salary Information');"+
"DataSets = ('Salary', red);"+
"DataSet1 = "+ Data1 + ";"+
"BarLabels = "+ Data2 + ";"+
"GraphLayout= HORIZONTAL;"+
"BottomAxis = (black, 'TimesRoman', 14, 0,
0,100000)"
);
bar.loadParams ("Update");
// Tell the bar chart class we've put
// some new parameters in.
} catch (Exception e) {
System.out.println (e.getMessage());
}
try {
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;
}
That’s it! We’ve 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.
ConnectToDB();
add("North", OutputField);
String columnData[] = getData("select * from Cost");
ShowFormattedData(columnData);
ShowChartData(columnData[1],columnData[0]);
add("Center", pie);
try {
new imaginary.sql.iMsqlDriver();
con = DriverManager.getConnection(url, "prpatel", "");
}
catch( Exception e ) {
e.printStackTrace();
System.out.println(e.getMessage());
}
int i;
System.out.println(SliceData);
if (nData.hasMoreTokens()) {SliceData += ", ";}
}
try {
// We already instantiated the pie chart
// class(NFPieChartAPP) at the top of the applet.
pie.init();
pie.start();
// Initialize and start the pie chart class.
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');");
pie.loadParams ("Update");
// Tell the pie chart class we've 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.
try {
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(QueryLine);
columns=(rs.getMetaData()).getColumnCount();
more=rs.next();
more=rs.next();
for (pos=1; pos<=columns; pos++) {
if(more) {
column[pos-1]+=(",");
}
}
}
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() {
String colors[] =
{"aliceblue","antiquewhite","aqua","aquamarine","azure","beige",
"bisque","black","blanchedalmond","blue","blueviolet","brown","chocolate
",
"cadetblue","chartreuse","cornsilk","crimson","cyan"};
// Increment the color counter, and return a String which contains the
// color at this index.
color_count += 1;
return colors[color_count];
} // end example72.java
Summary
This chapter has shown you how to generate meaningful charts to represent data obtained
from a query. We’ve seen how to create both bar and pie charts. You can use the
properties of the NetCharts package to customize your charts as you wish, and there are
many more options in the package that haven’t been shown in the examples here.
In the next chapter, we will continue to discuss working with database query results, and
we will provide a complete code example for showing SQL BLOB data types. It shows
you how to get an image from the ResultSet, as well as how to add images or binary data
to a table in a database.
Chapter 8
The IconStore Multimedia JDBC Application
In the previous chapter, we learned how to process query results with JDBC. In this
chapter, we’ll take these query results and put them to use in a multimedia application.
The application we’ll be developing, IconStore, will connect to a database, query for
image data stored in database tables, and display the images on a canvas. It’s 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 two database tables: ICONCATEGORY and
ICONSTORE. The ICONCATEGORY table contains information about image
categories, which can be items like printers, sports, and tools. The ICONSTORE table
contains information about each image. Tables 8.1 and 8.2 show the database tables’
underlying 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
Table 8.1 The
ICONCATEGORY
table. Column SQL Type Description
Name
ID INTEGER Image ID
Description of the
DESCRIPTION VARCHAR
image
CATEGORY INTEGER Category ID
IC O N
VARBINARY Binary
image
Now, let’s take a look at what’s 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.
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.SimpleTextDriver").newInstance();
int category;
int id = 1;
//——————————————————————————————————
// 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.
//——————————————————————————————————
protected static boolean addIconRecord(
PreparedStatement ps,
int id,
String desc,
int category,
String filename)
throws SQLException
{
// Create a file object for the icon
File file = new File(filename);
if (!file.exists()) {
return false;
}
// Get the length of the file. This will be used when binding
// the InputStream to the PreparedStatement.
int len = (int) file.length();
FileInputStream inputStream;
try {
// 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. We’ll 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,
isn’t it? Now that we’ve 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 java.awt.*;
import java.io.*;
import java.util.*;
import java.sql.*;
frame.pack();
frame.resize(300, 400);
frame.show();
}
A lot of work is being performed in IconStore.init, such as establishing the database
connection, reading the icon categories, creating the menus, and reading the icon
descriptions. We’ll take a look at each of these in greater detail in the following sections.
Establishing The Database Connection
Listing 8.3 shows the code used by the IconStore application to connect to the
SimpleText JDBC driver.
Listing 8.3 Establishing the database connection.
public Connection establishConnection()
{
"jdbc.SimpleText.SimpleTextDriver").newInstance();
try {
// Create a Statement object
Statement stmt = con.createStatement();
return table;
}
The flow of this routine is very basic, and we’ll 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 easily cross-reference a category description to an ID. We’ll see why this is
necessary a bit later.
Now that all of the category information has been loaded, we can create our menu.
Listing 8.5 shows how this is done.
Listing 8.5 Creating the Icons menu.
// Get a Hashtable containing an entry for each icon category.
// The key is the description and the data value is the
// category number.
categories = getCategories(connection);
// File menu
fileMenu = new Menu("File");
fileMenu.add(new MenuItem("Save As"));
fileMenu.add(new MenuItem("Exit"));
menuBar.add(fileMenu);
// Icons menu
sectionMenu = new Menu("Icons");
Enumeration e = categories.keys();
int listNo = 0;
String desc;
try {
// Create a Statement object
Statement stmt = con.createStatement();
category);
}
// Close the statement
stmt.close();
}
catch (SQLException ex) {
return table;
}
The process we used here is the same as we have seen before—creating a Statement,
executing a query, processing the results, and closing the Statement. Listing 8.7 shows
the entire code for the IconStore.init method. In addition to building the menu, we also
build the CardLayout. It is important to note that the IconStore application is totally
database-driven; no code will have to be modified to add or remove categories or images.
Listing 8.7 IconStore init method.
//————————————————————————————————————
// init
// Initialize the IconStore object. This includes reading the
// IconStore database for the icon descriptions.
//————————————————————————————————————
public void init()
{
// Create our canvas that will be used to display the icons
imageCanvas = new IconCanvas();
// File menu
fileMenu = new Menu("File");
fileMenu.add(new MenuItem("Save As"));
fileMenu.add(new MenuItem("Exit"));
menuBar.add(fileMenu);
// Icons menu
sectionMenu = new Menu("Icons");
iconDesc[listNo] = getIconDesc(connection,
(String) categories.get(desc), lists[listNo]);
listNo++;
}
// Add the Icons menu to the menu bar
menuBar.add(sectionMenu);
if (saveFile == null) {
return true;
}
System.exit(0);
}
case Event.LIST_SELECT:
displayIcon(connection);
break;
}
return false;
}
Most of the code is very straightforward. Of interest here is how the CardLayout is
managed. When a user makes a selection from the Icons menu, the selected item (which
is the category description) is used to change the CardLayout. Remember that when the
CardLayout was created, the title of each list was the category description. Also note
that when the user selects an item from the list box (LIST_SELECT), the corresponding
image can be displayed. Listing 8.9 shows how this is done.
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 ID
String id = (String) iconDesc[n].get(item);
try {
// Create a Statement object
Statement stmt = con.createStatement();
if (inputStream == null) {
stmt.close();
return;
}
while (true) {
// Read from the input. The number of bytes read is
returned.
bytes = inputStream.read(b);
if (bytes == -1) {
break;
}
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 we’ll 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, we’ve 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.
try {
FileInputStream in = new FileInputStream(source);
FileOutputStream out = new FileOutputStream(target);
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;
}
catch (java.lang.Exception ex) {
ex.printStackTrace();
}
return rc;
}
Figure 8.5 shows the IconStore screen after the user has selected the Save As menu
option.
Summary
Let’s 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 GUI
components (menus and lists)
• Handling user events
• Handling JDBC InputStreams
Finding A Solution
So how do we deal with these security holes? The most straightforward way is to use a
database server that implements secure login encryption. Some database servers do this
already, and with the proliferation of “Web databases,” login encryption is likely to be
incorporated into more popular database servers in the future. The other solution, which
is more viable, is to use an application server in a three-tier system. First, the Java
program uses encryption to send login information to the application server. Then, the
application server decodes the information. And finally, the application server sends the
decoded information to the database server, which is either running on the same machine
or on a machine attached to a secure local network. We’ll discuss application servers in
more detail in Chapter 11.
Another solution involves using the Java Security API, currently under development at
Javasoft. This API, which provides classes that perform encryption and authentication,
will be a standard part of the Java API and will allow you to use plug-in classes to
perform encryption on a remote connection.
As a user, how do you know if the Java applet you’re getting is part of a front for an
illegitimate business? The Java Commerce API addresses the security issue of
determining whether an applet is from a legitimate source by using digital signatures,
authorization, and certification. Both the Java Commerce API and Java Security API will
likely be incorporated into Web 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.
Summary
Security in data transactions is a top priority in the Internet community. In this chapter,
we’ve discussed possible security holes and techniques to sew them up. We also took a
look at Javasoft’s 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. We’ll explore the heart of the JDBC’s implementation details, and we’ll
also develop a real JDBC driver that can serve as the basis for drivers you write in the
future.
Chapter 10
Writing Database Drivers
We’ve 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 CD-ROM. 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. We’ll 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.
(column-element [, column-
element]...)
[(column-identifier [, column-
identifier]...)] VALUES
(insert-value [, insert-value]...)
dynamic-parameter ::= ?
The DriverManager
The JDBC DriverManager is a static class that provides services to connect to JDBC
drivers. The DriverManager is provided by JavaSoft and does not require the driver
developer to perform any implementation. Its main purpose is to assist in loading and
initializing a requested JDBC driver. Other than using the DriverManager to register a
JDBC driver (registerDriver) to make itself known and to provide the logging facility
(which is covered in detail later), a driver does not interface with the DriverManager. In
fact, once a JDBC driver is loaded, the DriverManager drops out of the picture all
together, and the application or applet interfaces with the driver directly.
// Find the end of the chain. When the current warning does
// not have a next pointer, it must be the end of the chain.
while (chain.getNextWarning() != null) {
chain = chain.getNextWarning();
}
// 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()
{
// 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):");
class NumericRoundingValueTest {
System.out.println("discounted price="+newPrice.toString());
Timestamp
The Timestamp class is used to represent a combination of date and time values in the
ANSI SQL format YYYY-MM-DD HH:MM:SS.F..., where YYYY is a four-digit year,
MM is a two-digit month, DD is a two-digit day, HH is a two-digit hour, MM is a two-
digit minute, SS is a two-digit second, and F is an optional fractional second up to nine
digits in length. The JDBC Timestamp class extends the existing java.util.Date class
(adding the fraction seconds) and, most importantly, adds two methods to convert Strings
into timestamps, and vice-versa:
// Create a Timestamp object with a date of 1996-06-30 and a time of
// 2:30:08 pm.
Timestamp t = Timestamp.valueOf("1996-06-30 14:30:08");
System.out.println("Timestamp=" + t.toString());
import java.sql.*;
#ifndef _Included_jdbc_test_MyBridge
#define _Included_jdbc_test_MyBridge
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) long jdbc_test_MyBridge_getINTSize(struct
Hjdbc_test_MyBridge *);
__declspec(dllexport) long jdbc_test_MyBridge_getINTValue(struct
Hjdbc_test_MyBridge *,HArrayOfByte *);
struct Hjava_lang_String;
__declspec(dllexport) void jdbc_test_MyBridge_callSomeFunction(struct
Hjdbc_test_MyBridge *,struct Hjava_lang_String *,HArrayOfByte *);
#ifdef __cplusplus
}
#endif
#endif
The generated C file (shown in Listing 10.9) must be compiled and linked with the
bridge.
//----------------------------------------------------------------------
--
// 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,
struct Hjava_lang_String *stringValue,
HArrayOfByte *buf)
{
// This fictitious function will print the string, then return the
// length of the string in the int pointer.
printf("String value=%s\n", pString);
*pInt = strlen(pString);
}
Now, create a library (DLL or Shared Object) by compiling this module and linking it
with the jdbc_test_MyDriver compiled object and the one required Java library, javai.lib.
Here’s the command line I used to build it for Win95/NT:
cl -DWIN32 mybridge.c jdbc_test_mybridge.c -FeMyBridge.dll -MD -LD
javai.lib
Now we can use our native bridge, as shown in Listing 10.11.
Listing 10.11 Implementing the bridge.
import jdbc.test.*;
import java.sql.*;
class Test {
try {
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 isn’t
public, it can’t 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.
package jdbc.test;
class TestInterface {
}
}
As you can see, implementing interfaces is easy. We’ll 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");
DriverManager.println("Trace=" + a + b + c);
In this example, a String message of “Trace=The quick brown fox jumped over the lazy
dog” will be constructed, the message will be provided as a parameter to the
DriverManager.println method, and the message will be written to the OutputStream
being used for tracing (if one has been registered).
Some of the JDBC components are also nice enough to provide tracing information. The
DriverManager object traces most of its method calls. SQLException also sends trace
information whenever an exception is thrown. If you were to use the previous code
example and enable tracing to a file, the following example output will be created when
attempting to connect to the SimpleText driver:
DriverManager.initialize: jdbc.drivers = null
JDBC DriverManager initialized
registerDriver:
driver[className=jdbc.SimpleText.SimpleTextDriver,context=null,
jdbc.SimpleText.SimpleTextDriver@1393860]
DriverManager.getConnection("jdbc:SimpleText")
trying
driver[className=jdbc.SimpleText.SimpleTextDriver,context=null,
jdbc.SimpleText.SimpleTextDriver@1393860]
driver[className=jdbc.SimpleText.SimpleTextDriver,context=null,j
dbc.SimpleText.SimpleTextDriver@1393860]
}
From an application, you can use this method to check if tracing has been previously
enabled before blindly setting it:
// Before setting tracing on, check to make sure that tracing is not
// already turned on. If it is, notify the application.
if (traceOn()) {
// Issue a warning that tracing is already enabled
.
.
.
}
From the driver, I use this method to check for tracing before attempting to send
information to the PrintStream. In the example where we traced the message text of
“Trace=The quick brown fox jumped over the lazy dog,” a lot had to happen before the
message was sent to the DriverManager.println method. All of the given String objects
had to be concatenated, and a new String had to be constructed. That’s a lot of overhead
to go through before even making the println call, especially if tracing is not enabled
(which will probably be the majority of the time). So, for performance reasons, I prefer to
ensure that tracing has been enabled before assembling my trace message:
// Send some information to the JDBC trace OutputStream
String a = "The quick brown fox ";
String b = "jumped over the ";
String c = "lazy dog";
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 about—converting data from one format to another. As Figure 10.1
shows, JDBC specifies the necessary conversions.
import java.sql.*;
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;
}
//----------------------------------------------------------------------
-
// isNull
// returns true if the value is null
//----------------------------------------------------------------------
--
public boolean isNull()
{
return (data == null);
}
//------------------------------------------------------------------
------
// getMethods
//----------------------------------------------------------------------
--
switch(internalType) {
case Types.VARCHAR:
s = (String) data;
break;
case Types.INTEGER:
s = ((Integer) data).toString();
break;
case Types.VARBINARY:
{
// Convert a byte array into a String of hex digits
byte b[] = (byte[]) data;
int len = b.length;
String digits = "0123456789ABCDEF";
char c[] = new char[len * 2];
default:
throw new SQLException("Unable to convert data type to
String: " +
internalType);
}
return s;
}
switch(internalType) {
case Types.VARCHAR:
i = (Integer.valueOf((String) data)).intValue();
break;
case Types.INTEGER:
i = ((Integer) data).intValue();
break;
default:
throw new SQLException("Unable to convert data type to
String: " +
internalType);
}
return i;
}
switch(internalType) {
case Types.VARCHAR:
{
if ((len % 2) != 0) {
throw new SQLException(
"Data must have an even number of hex
digits");
}
if (index < 0) {
throw new SQLException("Invalid hex digit");
}
if (index < 0) {
throw new SQLException("Invalid hex digit");
}
b[i] += (byte) index;
}
}
break;
case Types.VARBINARY:
b = (byte[]) data;
break;
default:
throw new SQLException("Unable to convert data type to
byte[]: " +
internalType);
}
return b;
}
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}
We’ll 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 functions—string, numeric, time and date, system, and data type
conversion—all 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, MINUTE, MONTH, MONTHNAME, NOW, QUARTER,
SECOND, TIMESTAMPADD, TIMESTAMPDIFF, WEEK, and/or YEAR.
To determine what type of explicit data type conversions a JDBC driver supports, an
application can use the DatabaseMetaData method supportsConvert. This method has
two parameters: a from SQL data type and a to SQL data type. If the explicit data
conversion between the two SQL types is supported, the method returns true. The syntax
for the CONVERT function is
{fn CONVERT(value, data_type)}
where value is a column name, the result of another scalar function, or a literal, and
data_type is one of the JDBC SQL types listed in the Types class.
LIKE Predicate Escape Characters
In a LIKE predicate, the “%” (percent character) matches zero or more of any character,
and the “_” (underscore character) matches any one character. In some instances, an SQL
query may have the need to search for one of these special matching characters. In such
cases, you can use the “%” and “_” characters as literals in a LIKE predicate by
preceding them with an escape character. The DatabaseMetaData method getSearch-
StringEscape returns the default escape character (which for most DBMSes will be the
backslash character “ \”). To override the escape character, use the following syntax:
{escape 'escape-character'}
The following SQL statement uses the LIKE predicate escape clause to search for any
columns that start with the “%” character:
SELECT * FROM EMPLOYEE WHERE NAME LIKE '\%' {escape '\'}
Outer Joins
JDBC supports the ANSI SQL-92 LEFT OUTER JOIN syntax. The escape clause syntax
is
{oj outer-join}
where outer-join is the table-reference LEFT OUTER JOIN {table-reference | outer-join}
ON search-condition.
Procedures
A JDBC application can call a procedure in place of an SQL statement. The escape clause
used for calling a procedure is
{[?=] call procedure-name[(param[, param]...)]}
where procedure-name specifies the name of a procedure stored on the data source, and
param specifies procedure parameters. A procedure can have zero or more parameters,
and may return a value.
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 pre-registered 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();
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 JDBC-ODBC 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";
Driver Properties
Connecting to a JDBC driver with only a URL specification is great, but the vast majority
of the time, a driver will require additional information in order to properly connect to a
database. The JDBC specification has addressed this issue with the getPropertyInfo
method. Once a Driver has been instantiated, an application can use this method to find
out what required and optional properties can be used to connect to the database. You
may be tempted to require the application to embed properties within the URL subname,
but by returning them from the getPropertyInfo method, you can identify the properties
at runtime, giving a much more robust solution. Listing 10.15 shows an application that
loads the SimpleText driver and gets the property information.
class PropertyTest {
}
catch (SQLException ex) {
System.out.println ("\nSQLException(s) caught\n");
Property 1
Name: Directory
Description: Initial text file directory
Required: false
Value: null
Choices: null
It doesn’t take a lot of imagination to envision an application or applet that gathers the
property information and prompts the user in order to connect to the database. The actual
code to implement the getPropertyInfo method for the SimpleText driver is very simple,
as shown in Listing 10.16.
Listing 10.16 Implementing the getPropertyInfo method.
//----------------------------------------------------------------------
--
// getPropertyInfo - JDBC API
//
// The getPropertyInfo method is intended to allow a generic GUI tool to
// discover what properties it should prompt a human for in order to get
// enough information to connect to a database. Note that depending on
// the values the human has supplied so far, additional values may
become
// necessary, so it may be necessary to iterate though several calls.
// to getPropertyInfo.
//
// url The URL of the database to connect to.
//
// info A proposed list of tag/value pairs that will be sent on
// connect open.
//
// Returns an array of DriverPropertyInfo objects describing possible
// properties. This array may be an empty array if no
// properties are required.
//----------------------------------------------------------------------
--
}
else {
// Create an empty list
prop = new DriverPropertyInfo[0];
}
return prop;
return con;
}
As you can see, there isn’t 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 database connection resides in the
Connection class, which we’ll discuss next.
Connection
The Connection class represents a session with the data source. From here, you can
create Statement objects to execute SQL statements and gather database statistics.
Depending upon the database that you are using, multiple connections may be allowed
for each driver.
For the SimpleText driver, we don’t need to do anything more than actually connect to
the database. In fact, there really isn’t a database at all—just a bunch of text files. For
typical database drivers, some type of connection context will be established, and default
information will be set and gathered. During the SimpleText connection initialization, all
that we need to do is check for a read-only condition (which can only occur within
untrusted applets) and any properties that are supplied by the application, as shown in
Listing 10.18.
Listing 10.18 SimpleText connection initialization.
public void initialize(
Driver driver,
java.util.Properties info)
throws SQLException
{
// Save the owning driver object
ownerDriver = driver;
if (securityManager != null) {
try {
// Use some arbitrary file to check for file write
privileges
securityManager.checkWrite ("SimpleText_Foo");
// Flag is set if no exception is thrown
canWrite = true;
}
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");
return stmt;
}
Now consider the corresponding initialize method in the Statement class:
public void initialize(
SimpleTextConnection con)
throws SQLException
{
// Save the owning connection object
ownerConnection = con;
}
Which module will you compile first? You can’t compile the Connection class until the
Statement class has been compiled, and you can’t compile the 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. Here’s 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 statement, an exception should be raised, and the Statement object
should not be created.
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, we’ll just take a look
at how the SimpleText driver implements the getTables catalog method. But first, let’s
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 catalog
information. You may need to filter the information in
the JDBC driver if the database system doesn’t.
4. Sort the result data per the JDBC API specification.
If you are lucky, the database you are using will sort
the data in the proper sequence. Most likely, it will not.
In this case, you will need to ensure that the data is
returned in the proper order.
5. Return a ResultSet containing the requested
information.
The SimpleText getTables method will return a list of all of the text files in the catalog
(directory) given. If no catalog is supplied, the default directory is used. Note that the
SimpleText driver does not perform all of the steps shown previously; it does not provide
any filtering, nor does it sort the data in the proper sequence. You are more than welcome
to add this functionality. In fact, I encourage it. One note about column information: I
prefer to use a Hashtable containing the column number as the key, and a class
containing all of the information about the column as the data value. So, for all
ResultSets that are generated, I create a Hashtable of column information that is then
used by the ResultSet object and the ResultSetMetaData object to describe each
column. Listing 10.19 shows the SimpleTextColumn class that is used to hold this
information for each column.
Listing 10.19 The SimpleTextColumn class.
package jdbc.SimpleText;
public class SimpleTextColumn
extends Object
{
//----------------------------------------------------------------------
--
// Constructor
//----------------------------------------------------------------------
--
public SimpleTextColumn(
String name,
int type,
int precision)
{
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;
}
if (types != null) {
willBeEmpty = true;
for (int ii = 0; ii < types.length; ii++) {
if (types[ii].equalsIgnoreCase("TABLE")) {
willBeEmpty = false;
break;
}
}
}
if (!willBeEmpty) {
// Get a Hashtable with all tables
Hashtable tables = ownerConnection.getTables(
ownerConnection.getDirectory(catalog),
tableNamePattern);
Hashtable singleRow;
SimpleTextTable table;
return rs;
}
Let’s take a closer look at what’s going on here. The first thing we do is create a
Statement object to “fake out” the ResultSet object that we will be creating to return
back to the application. The ResultSet object is dependent upon a Statement object, so
we’ll give it one. The next thing we do is create all of the column information. Note that
all of the required columns are given in the JDBC API specification. The add method
simply adds a SimpleTextColumn object to the Hashtable of columns:
protected void add(
Hashtable h,
int col,
String name,
int type)
{
h.put(new Integer(col), new SimpleTextColumn(name,type));
}
Next, we create another Hashtable to hold all of the data for all of the catalog rows. The
Hashtable contains an entry for each row of data. The entry contains the key, which is
the row number, and the data value, which is yet another Hashtable whose key is the
column number and whose data value is a CommonValue object containing the actual
data. Remember that the CommonValue class provides us with the mechanism to store
data and coerce it as requested by the application. If a column is null, we simply cannot
store any information in the Hashtable for that column number.
After some sanity checking to ensure that we really need to look for the catalog
information, we get a list of all of the tables. The getTables method in the Connection
class provides us with a list of all of the SimpleText data files:
public Hashtable getTables(
String dir,
String table)
{
Hashtable list = new Hashtable();
if (file.isDirectory()) {
}
}
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;
java.sql.ResultSet rs = null;
//----------------------------------------------------------------------
--
// executeUpdate - JDBC API
// Execute an SQL INSERT, UPDATE, or DELETE statement. In addition,
// SQL statements that return nothing, such as SQL DDL statements,
// can be executed.
//
// sql an SQL INSERT, UPDATE, or DELETE statement, or an SQL
// statement that returns nothing.
//
// Returns either the row count for INSERT, UPDATE, or DELETE; or 0
// for SQL statements that return nothing.
//----------------------------------------------------------------------
--
public int executeUpdate(
String sql)
throws SQLException
{
if (traceOn()) {
trace("@executeUpdate(" + sql + ")");
}
int count = -1;
return count;
}
As you can see, executeQuery and executeUpdate are simply helper methods for an
application; they are built completely upon other methods contained within the class. The
execute method accepts an SQL statement as its only parameter, and will be implemented
differently, depending upon the underlying database system. For the SimpleText driver,
the SQL statement will be parsed, prepared, and executed. Note that parameter markers
are not allowed when executing an SQL statement directly. If the SQL statement created
results containing columnar data, execute will return true; if the statement created a count
of rows affected, execute will return false. If execute returns true, the application then
uses getResultSet to return the current result information; otherwise, getUpdateCount
will return the number of rows affected.
Warnings
As opposed to SQLException, which indicates a critical error, an SQLWarning can be
issued to provide additional information to the application. Even though SQLWarning is
derived from SQLException, warnings are not thrown. Instead, if a warning is issued, it
is placed on a warning stack with the Statement object (the same holds true for the
Connection and ResultSet objects). The application must then check for warnings after
every operation using the getWarnings method. At first, this may seem a bit
cumbersome, but when you consider the alternative of wrapping try...catch statements
around each operation, this seems like a better solution. Note also that warnings can be
chained together, just like SQLExceptions (for more information on chaining, see the
JDBC Exception Types section earlier in this chapter).
clearWarnings();
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
{
try {
if ((Integer) x != null) {
return Types.INTEGER;
}
}
catch (Exception ex) {
}
try {
if ((byte[]) x != null) {
return Types.VARBINARY;
}
}
catch (Exception ex) {
}
Setting InputStreams
As we’ll 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. Here’s what the SimpleText driver
does with InputStreams:
public void setBinaryStream(
int parameterIndex,
java.io.InputStream x,
int length)
throws SQLException
{
try {
x.read(b);
}
catch (Exception ex) {
throw new SQLException("Unable to read InputStream: " +
ex.getMessage());
}
if (x != null) {
return (x.intValue());
}
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;
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 attributes, and the column name.
ResultSetMetaData will typically be the smallest class in a JDBC driver, and is usually
very straightforward to implement. For the SimpleText driver, all of the necessary
information is retrieved from the Hashtable of column information that is required for all
result sets. Thus, to retrieve the column name:
public String getColumnLabel(
int column)
throws SQLException
{
// Use the column name
return getColumnName(column);
}
if (column == null) {
throw new SQLException("Invalid column number: " + col);
}
return column;
}
Summary
We have covered a lot of material in this chapter, including the JDBC DriverManager
and the services that it provides, implementing Java interfaces, creating native JDBC
drivers, tracing, data coercion, escape sequence processing, and each one of the major
JDBC interfaces. This information, in conjunction with the SimpleText driver, should
help you to create your own JDBC driver without too much difficulty.
Chapter 11
Internet Database Issues: Middleware
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.
inline=inline.trim();
// Get rid of leading and trailing whitespace
out.flush();
}
}
catch (IOException e) {}
while(rs.next()) {
Output+=rs.getObject(pos)+" ";
}
Output+="\n";
}
stmt.close();
// con.close();
}
catch( Exception e ) {
e.printStackTrace();
Output=e.getMessage();
}
return Output;
}
// End DB specific stuff
out.flush();
// tell the reader we've sent some data/command
synchronized(reader){reader.notify();reader.notifyOn=false;}
while(true) {
// We have to wait until the Reader has finished reading, so we
set
// this notifyOn flag in the reader when it has finished reading.
if (reader.notifyOn) {break;}
}
public Reader(DBClient c) {
super("DBclient Reader");
this.client = c;
this.start();
}
DataInputStream in=null;
try {
in = new DataInputStream(client.socket.getInputStream());
while(true) {
// We start reading when we are notified from the main thread
// and we stop when we have finished reading the stream for
// this command/query.
try {if (notifyOn) {this.wait(); notifyOn=false; Result="";}}
catch (InterruptedException e){
System.out.println("Caught an Interrupted Exception");
}
// Prevent simultaneous access
line = in.readLine();
if (line.equalsIgnoreCase("DONE")) {
notifyOn=true;
} 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;
gridbag.setConstraints(OutputField, Con);
OutputField.setForeground(Color.white);
OutputField.setBackground(Color.black);
add(OutputField);
show();
} //init
OutputField.setText(DataConnection.ProcessCommand(QueryField.getText()))
;
return true;
}
}
Summary
In this chapter, we took a brief look at middleware. You saw the advantages and
disadvantages of implementing a three-tier system, and we created a simple application
server and a client server which you can easily extend to fit your needs.
We’re almost at the end of this journey through the JDBC. The next chapter is a reference
chapter of the JDBC API. It contains documentation on the JDBC methods used in the
writing of this book, as well as methods that we didn’t explicitly cover. You may want to
browse through the package tree to get an idea of how the various classes and methods fit
together, as well as their relation to one another.
Chapter 12
The JDBC API
This chapter ends our journey through the JDBC. I’ve 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 chapter’s 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
We’ll 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
Constructor Additional Description
Date(int Year, int Month, int Construct a java.sql.Date
day) object with the appropriate
parameters
Methods
Method Name Additional Description
public String toString() Formats a Date object as
YYYY-MM-DD
public static Date valueOf Converts a String str to an
(String str) sql.Date object
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 the
deregisterDriver(Driver- available drivers list
JDBCdriver) throws
SQLException
public static synchronized
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, with
getConnection(String URL, the given parameters
Properties LoginInfo) throws
SQLException
public static Driver Finds a driver that
getDriver(String URL) throws understands the JDBC URL
SQLException from the registered driver list
public static Enumeration Gets an Enumeration of the
getDrivers() available JDBC drivers
public static int Indicates the maximum time
getLoginTimeout() (seconds) that a driver will
wait when logging into a
database
public static PrintStream Gets the logging PrintStream
getLogStream() used by the DriverManager
and JDBC drivers
public static void Sends msg to the current
println(String msg) JDBC logging stream (fetched
from above method)
Specifies that a new driver
public static synchronized
class should call
void register Driver(Driver
registerDriver when loading to
JDBCdriver) throws
“register” with the
SQLException
DriverManager
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 that
setLogStream (PrintStream logging messages are sent to
log) 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. It’s 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 of
(String propName, String the property, and propValue is
propValue) the current value; if it’s not
been set, it may be null
Variables
Variable Name Additional Description
choices If the property value is part of
a set of values, then choices
is an array of the possible
values
description The property’s description
name The property’s name
required This is true if this property is
required to be set during
Driver.connect
The current value of the
value
property
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 Additional Description
public Numeric(String strNum) Produces a Numeric object
from a string; strNum can be
in one of two formats:
“1234.32” or “3.1E8”
public Numeric(String strNum, Produces a Numeric, and scale
int 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, int Produces a Numeric object
scale) from an int, and scale gives
the desired number of places
right of the decimal
public Numeric(long x) Produces a Numeric object
from a long Java type
parameter
public Numeric(long x, int Produces a Numeric object
scale) from a long parameter, and
scale gives the desired
number of places right of the
decimal
public Numeric(double x, int Produces a Numeric object
scale) from a double Java type
parameter, and scale gives
the desired number of places
right of the decimal
public Numeric(Numeric num) Produces a Numeric object
from a Numeric
public Numeric(Numeric num, Produces a Numeric object
int scale) from a Numeric, and scale
gives the desired number of
places right of the decimal
Methods
Method Name Additional Description
public Numeric add(Numeric Performs arithmetic addition
n) on the reference Numeric
object and the Numeric
argument
public static Numeric
Produces a Numeric object
createFromByteArray(byte
from the byte array parameter
byteArray[])
public static Numeric
Produces a Numeric object
createFromIntegerArray(int
from the int array parameter
intArray[])
public static Numeric Produces a Numeric object
createFromRadixString(String from the String and int radix
str, int radix) parameters
public static Numeric Produces a Numeric object by
createFromScaled(long taking the longNum to the
longNum, int power) 10^power
public Numeric divide(Numeric Divides the Numeric by the
q) Numeric parameter q and
returns the result
public double doubleValue() Returns the Numeric as a Java
type double
public boolean equals(Object Returns true if the Numeric
objct) object equals the objct
parameter
Returns the Numeric as a Java
public float floatValue()
type float
public static int Returns the roundingValue
getRoundingValue() used in rounding operations in
the Numeric object
public int getScale() Returns the number of places
to the right of the decimal
public long getScaled() Returns the Numeric object as
a long, but removes the
decimal (1234.567 ->
1234567); precision may be
lost
public boolean Returns true if the Numeric
greaterThan(Numeric num) object is greater than the
Numeric num argument
public boolean Returns true if the Numeric
greaterThanOrEquals(Numeric object is greater than or equal
num) to the Numeric num argument
public int hashCode() Returns an integer hashcode
for the Numeric object
public Numeric[] Returns an array with two
integerDivide(Numeric x) Numeric objects: the first one
is the quotient, the second is
the remainder
public int intValue() Returns the Numeric as a Java
type int, digits after the
decimal are dropped
public boolean Returns true if the number is
isProbablePrime() 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 Returns true if the Numeric
lessThan(Numeric num) object is less than the
Numeric num argument
public boolean Returns true if the Numeric
lessThanOrEquals(Numeric object is less than or equal to
num) the Numeric num argument
Returns the Numeric as a Java
public long longValue()
type long
public Numeric modExp The two parameters are used
(Numeric numExp, Numeric to do a numMod modulus to
numMod) the numExp exponent
calculation; returns the result
as a Numeric
The modular multiplicative
public Numeric
inverse is returned using
modInverse(Numeric numMod)
numMod as the modulus
public Numeric Returns the product of the
multiply(Numeric num) Numeric object and the
Numeric num parameter
public static Numeric pi(int Returns pi to the number of
places) decimal places
public Numeric pow(int exp) Returns a Numeric object
using the current Numeric
object taken to the power of
the given exponent exp
public static Numeric Returns a Numeric object that
random(int bits, Random is a random number using
randSeed) 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 used
setRoundingValue(int val) in rounding operations for the
Numeric object
public Numeric setScale(int Returns a Numeric object from
scale) the current object with the
specified scale parameter
public Numeric shiftLeft(int Returns the Numeric object
numberOfBits) with the specified
numberOfBits shifted left
public Numeric shiftRight(int Returns the Numeric object
numberOfBits) with the specified
numberOfBits shifted right
public int significantBits() Returns the number of
significant bits in the Numeric
object
public Numeric sqrt() Returns the square root of
this Numeric object
public Numeric Returns the difference
subtract(Numeric num) between the Numeric object
and the Numeric num
parameter
public String toString() Returns a String type that is
the String representation of
the Numeric object
public String toString(int Returns a String type that is
radix) the String representation of
the Numeric object, in the
specified radix
Variables
Variable Name Additional Description
public final static Numeric A Numeric equivalent to the
ZERO value of 0
public final static Numeric A Numeric equivalent to the
ONE value of 1
public class Time
The public class Time is another SQL-JDBC data coversion class. This class extends
java.util.Date, and basically implements the time-storing functions that are not present in
the java.sql.Date class shown earlier.
Constructors
Constructor Additional Description
public Time(int hour, int Makes a Time object with the
minute, specified hour, minute, and
int second) second
Methods
Method Name Additional Description
public String toString() Returns a String with the Time
formatted this way:
HH:MM:SS
public static Time Returns a Numeric object from
valueOf(String numStr) 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
Builds a Timestamp object
public Timestamp(int year, int
using the int parameters:
month, int date, int hour, int
year, month, date, hour,
minute, int second, int nano)
minute, second, and nano
Methods
Method Name Additional Description
public boolean Compares the Timestamp
equals(Timestamp tstamp) object with the Timestamp
parameter tstamp; returns
true if they match
Returns the Timestamp
public int getNanos()
object’s nanoseconds
Sets the Timestamp object’s
public void setNanos(int n)
nanosecond value
public String toString() Returns a formatted String
object with the value of the
Timestamp object in the
format: YYYY-MM-DD
HH:MM:SS.F
public static Timestamp Returns a Timestamp object
valueOf(String strts) converted from the strts
parameter that is in the
previous format
public class Types
This class contains the SQL data types as constants. It is used by other classes as the
standard constant for the data types.
Constructors
Constructor Additional Description
public Types() Builds a Types object; not
usually necessary as they can
be accessed as so:
Types.BIGINT
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 interface’s 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 Additional Description
public abstract boolean
getBoolean(int
parameterIndex) throws
SQLException
public abstract byte
getByte(int parameterIndex)
throws SQLException
public abstract byte[]
getBytes(int parameterIndex)
throws SQLException
public abstract Date
getDate(int parameterIndex)
throws SQLException
public abstract double
getDouble(int
parameterIndex) throws
SQLException
public abstract float
getFloat(int parameterIndex)
throws SQLException
public abstract int getInt(int
parameterIndex) throws
SQLException
public abstract long
getLong(int parameterIndex)
throws SQLException
public abstract Numeric
getNumeric(int
parameterIndex, int scale)
throws SQLException
public abstract Object
getObject(int
parameterIndex) throws
SQLException
public abstract short
getShort(int parameterIndex)
throws SQLException
public abstract String
getString(int parameterIndex)
throws SQLException
public abstract Time
getTime(int parameterIndex)
throws SQLException
public abstract Timestamp
getTimestamp(int
parameterIndex) throws
SQLException
public abstract void Each parameter of the stored
registerOutParameter(int procedure must be registered
paramIndex, int sqlDataType) before the query is run;
throws SQLException paramIndex is the stored
proc’s parameter location in
the output sequence, and
sqlDataType is the data type
of the parameter at the
specified location
(sqlDataType should be set
from the Type class using one
of its variables, for example,
Types.BIGINT)
public abstract void Specifies the number of places
registerOutParameter(int to the right of the decimal
parameterIndex, int desired when getting Numeric
sqlDataType, int scale) throws data objects
SQLException
public abstract boolean
Returns true if the stored proc
wasNull() throws
parameter was value NULL
SQLException
public interface Connection
This is the high-level class used to interact with a database. The object is established from
the DriverManager.getConnection method, which returns this object (Connection).
This class obtains information about the specific database connection via the instantiated
JDBC driver, and its primary use is to perform queries via the createStatement,
prepareCall, and prepareStatement methods, which return Statement, PreparedCall,
and PreparedStatement objects, respectively.
Methods
Method Name Additional Description
public abstract void Clears the warnings for the
clearWarnings() throws connection
SQLException
public abstract void close() Closes the connection to the
throws SQLException database
public abstract void commit() Functions as the JDBC
throws SQLException equivalent of the standard
database commit command; 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 Returns true if automatic
getAutoClose() throws closing of the connection is
SQLException enabled; automatic closing
results in the closing of the
connection when commit or
rollback is performed
public abstract boolean Returns true if automatic
getAutoCommit() throws committing of the connection
SQLException is on; automatic commit is on
by default and means that the
connection is committed on
individual transactions; the
actual commit occurs when
the 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 Returns a DatabaseMetaData
DatabaseMetaData object for the current
getMetaData() throws connection
SQLException
public abstract int Returns the transaction
getTransactionIsolation() isolation mode of the
throws SQLException connection
public abstract SQLWarning Returns the SQLWarning
getWarnings() throws object with the warnings for
SQLException the connection
public abstract boolean Returns true if the connection
isClosed() throws has been closed
SQLException
public abstract boolean Returns true if the connection
isReadOnly() throws is a read only connection
SQLException
public abstract String Returns the native SQL that
nativeSQL(String throws the JDBC driver sqlQuery)
SQLException would send to the database
for the specified sqlQuery
parameter
public abstract Returns a CallableStatement
CallableStatement object used to perform stored
prepareCall(String sqlQuery) procedures; note that the SQL
throws SQLException query must be passed in as
the sqlQuery parameter here
public abstract Returns a PreparedStatement
PreparedStatement object used to perform the
prepareStatement(String specified sqlQuery; this query
sqlQuery) throws can be executed repeatedly if
SQLException desired by using the
PreparedStatement.execute
method
public abstract void rollback() Drops changes made since the
throws SQLException last commit or rollback, and
closes respective results;
database locks are also
released
public abstract void Sets the connection to auto
setAutoClose (boolean throws close mode if the auto) auto
SQLException 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 changed
setCatalog (String catalog) by specifying the catalog
throws SQLException
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 Database-MetaData object that is specific
to the opened connection.
Methods
Method Name Additional Description
public abstract boolean Returns true if all the
allProceduresAreCallable() throwsk procedures available to
SQLException the user are callable
public abstract boolean Returns true if all of the
allTablesAreSelectable() throws tables are accessible to
SQLException the user on the open
connection
public abstract boolean Returns true if data
dataDefinitionCausesTransactionCommit() defintion causes the
throws SQLException transaction to commit
public abstract boolean Returns true if data
dataDefinitionIgnoredInTransactions() defintion is ignored in the
throws SQLException transaction
public abstract boolean Returns true if the
doesMaxRowSizeIncludeBlobs() throws getMaxSize method does
SQLException not account for the size
of LONGVARCHAR and
LONGVARBINARY SQL
data types
public abstract ResultSet Returns a ResultSet
getBestRowIdentifier(String catalog, object for the specified
String parameters that gets the
schema, String table, int scope, boolean specified table’s key or
nullok) throws SQLException the attributes that can be
used to uniquely identify
a row, which may be
composite; the scope
parameter is one of the
constants:
bestRowTemporary,
bestRowTransaction, or
betRowSession; the
nullok parameter allows
columns that may be
null; the ResultSet is
composed of the
following columns: scope
(of the same types as
above scope parameter),
column name, SQL data
type, name of the data
type dependent on the
database, precision,
buffer length, significant
places if a Numeric type,
and pseudo column (one
of the constants
bestRowUnknown,
bestRowNotPseudo, or
bestRowPseudo)
public abstract ResultSet getCatalogs() Returns a ResultSet
throws SQLException object that contains a
column for the catalog
names that are in the
database
public abstract Returns the separator
String getCatalogSeparator() throws between the catalog
SQLException String and the table
name
public abstract String getCatalogTerm() Returns the database-
throws SQLException specific term for
“catalog”
public abstract ResultSet Returns a ResultSet
getColumnPrivileges(String catalog, object that contains
String schemaString table, String information about the
columnNamePattern) throws specified table’s matching
SQLException columnNamePattern; the
returned ResultSet object
contains the following
columns: the catalog
name that the table is in,
the schema the table is
in, the table name, the
column name, owner of
the table, grantee, type
of access (SELECT,
UPDATE, etc.), and if the
grantee can grant access
to others, “YES,” “NO,” or
null (if unknown)
public abstract ResultSet Returns a ResultSet
getColumns(String catalog, object that contains
String schemaPattern, String information about the
tableNamePattern, matching columns for the
String columnNamePattern) throws matching tables and
SQLException schemas; the ResultSet
contains the following
columns: catalog name,
schema name, table
name, column name, SQL
data type, name of the
type specific to the
database, the maximum
number of characters or
precision depending on
the data type, buffer
length (not used), the
number of digits (if
applicable), radix (if
applicable), null-ability
(one of the constants
columnNoNulls,
columnNullable,
columnNullableUnknown),
comments for the
column, default value (if
it exists, else null),
empty column, empty
column, maximum
number of bytes in the
column of type CHAR (if
applicable), index number
of column; the last
column is set to “YES” if
it can contain NULLS if
not “NO” else it’s empty
if the status is unknown
public abstract ResultSet get Returns a ResultSet
CrossReference(String primaryCatalog, object that describes the
String primarySchema, way a table imports
String primaryTable, String foreign keys; the
foreignCatalog, ResultSet object returned
String foreignSchema, String by this method contains
foreignTable) these columns: primary
throws SQLException key’s table catalog,
primary key’s table
schema, primary key’s
table, primary key’s
column name, foreign
key’s table catalog,
foreign key’s table
schema, foreign key’s
table, foreign key’s
column name, sequence
number within foreign
key, action to foreign key
when primary key is
updated (one of the
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
action to foreign key
when primary key is
deleted (one of the
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
foreign key identifier,
and primary key
indentifier
public abstract String Returns the database
getDatabaseProductName() throws product name
SQLException
public abstract String Returns the database
getDatabaseProductVersion() throws product number
SQLException
public abstract int Returns the default
getDefaultTransactionIsolation() throws transaction isolation level
SQLException as defined by the
applicable constants in
the Connection class
public abstract int Gets the driver’s major
getDriverMajorVersion() version
public abstract int Gets the driver’s minor
getDriverMinorVersion() version
public abstract String getDriverName() Returns the name of the
throws SQLException JDBC driver
public abstract String getDriverVersion() Returns the version of
throws SQLException the JDBC driver
public abstract ResultSet Returns a ResultSet
getExportedKeys(String catalog, String object that describes the
schema, String table) throws foreign key attributes
SQLException that reference the
specified table’s primary
key; the ResultSet object
returns the following
columns: primary key’s
table catalog, primary
key’s table schema,
primary key’s table,
primary key’s column
name, foreign key’s table
catalog, foreign key’s
table schema, foreign
key’s table, foreign key’s
column name, sequence
number within foreign
key, action to foreign key
when primary key is
updated (one of the
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
action to foreign key
when primary key is
deleted (one of the
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
foreign key identifier,
and primary key
indentifier
public abstract String Returns characters that
getExtraNameCharacters() throws can be used in unquoted
SQLException identifier names besides
the standard A through Z,
0 through 9, and _
public abstract String Returns the String used
getIdentifierQuoteString() throws to quote SQL identifiers
SQLException
public abstract ResultSet Returns a ResultSet
getImportedKeys(String String schema, object that describes the
String table) throws SQLException primary key attributes
that are referenced by
the specified table’s
foreign key attributes;
the ResultSet object
contains the following
columns: primary key’s
table catalog, primary
key’s table schema,
primary key’s table,
primary key’s column
name, foreign key’s table
catalog, foreign key’s
table schema, foreign
key’s table, foreign key’s
column name, sequence
number within foreign
key, action to foreign key
when primary key is
updated (one of the
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
action to foreign key
when primary key is
deleted (one of the
constants
importedKeyCascade,
importedKeyRestrict,
importedKeySetNull),
foreign key identifier,
and primary key
indentifier
public abstract ResultSet Returns a ResultSet
getIndexInfo(String catalog, String object that describes the
schema, String table, boolean unique, specified table’s indices
boolean approximate) throws and statistics; the
SQLException ResultSet object contains
the following columns:
catalog name, schema
name, table name, “false”
boolean (if
tableIndexStatic is the
type), index catalog (or
null if type is
tableIndexStatic), index
type, sequence number,
column name, column
sort sequence, number of
unique values in the table
or number of rows (if
tableIndexStatic),
number of pages used for
the index (or the number
of pages used for the
table if tableIndexStatic),
and filter condition (if it
exists)
public abstract int Returns the number of
getMaxBinaryLiteralLength() throws hex characters allowed in
SQLException an inline binary literal
public abstract int The maximum length for
getMaxCatalogNameLength() throws a catalog name
SQLException
public abstract int Returns the maximum
getMaxCharLiteralLength() throws length for a character
SQLException literal
public abstract int Indicates the maximum
getMaxColumnNameLength() throws length for a column name
SQLException
public abstract int Indicates the maximum
getMaxColumnsInGroupBy() throws number of columns in a
SQLException GROUP BY clause
public abstract int Indicates the maximum
getMaxColumnsInIndex() throws number of columns in an
SQLException index
public abstract int Indicates the maximum
getMaxColumnsInOrderBy() throws number of columns
SQLException allowed in a ORDER BY
clause
public abstract int Indicates the maximum
getMaxColumnsInSelect() throws number of columns in a
SQLException SELECT statement
public abstract int Indicates the maximum
getMaxColumnsInTable() throws number of columns
SQLException allowed in a table
public abstract int getMaxConnections() Indicates the maximum
throws SQLException number of simultaneous
connections allowed to
the database
public abstract int Returns the maximum
getMaxCursorNameLength() throws allowed length of a cursor
SQLException name
public abstract int Returns the maximum
getMaxIndexLength() throws length of an index in
SQLException bytes
public abstract int Returns the maximum
getMaxProcedureNameLength() throws allowed length of a
SQLException procedure name
public abstract int getMaxRowSize() Indicates the maximum
throws SQLException row size
public abstract int Returns the maximum
getMaxSchemaNameLength() throws allowed length of a
SQLException schema name
public abstract int Returns the maximum
getMaxStatementLength() throws allowed length of a SQL
SQLException statement
Returns the maximum
public abstract int getMaxStatements()
number of statements
throws SQLException
allowed at one time
public abstract int Returns the maximum
getMaxTableNameLength() throws allowed length of a table
SQLException name
public abstract int Indicates the maximum
getMaxTablesInSelect() number of tables allowed
throws SQLException in a SELECT statement
public abstract int Returns the maximum
getMaxUserNameLength() throws allowed length of a user
SQLException name
public abstract String Returns a comma-
getNumericFunctions() throws separated list of the math
SQLException functions available
public abstract Returns a ResultSet
ResultSet getPrimaryKeys(String catalog, object that contains the
String schema, String table) throws primary key’s description
SQLException for the specified table;
the ResultSet object
contains the following
columns: catalog name,
schema name, table
name, column name,
sequence number,
primary key name, and,
possibly, NULL
public abstract ResultSet Returns a ResultSet
getProcedureColumns(String catalog, object that describes the
String schemaPattern, String catalog’s stored
procedureNamePattern, String procedures and result
columnNamePattern) throws columns matching the
SQLException specified
procedureNamePatten
and columnNamePattern;
the ResultSet object
contains the following
columns: catalog name,
schema name, procedure
name, column or
parameter name, column
type, data type, data
name, precision, length
in bytes, scale, radix,
nullability, and comments
public abstract ResultSet Returns a ResultSet
getProcedures(String catalogString String object that describes the
procedureNamePattern) throws catalog’s procedures; the
SQLException ResultSet object contains
the following columns:
catalog name, schema
name, procedure name,
empty column, empty
column, empty column,
comments about the
procedure, and kind of
procedure
public abstract String Return the database-
getProcedureTerm() throws SQLException specific term for
procedure
public abstract ResultSet getSchemas() Returns a ResultSet
throws SQLException object that describes the
schemas in a database;
the ResultSet object
contains one column that
contains the schema
names
public abstract String Returns the database-
getSchemaTerm() throws specific term for schema
SQLException
public abstract String Returns the escape
getSearchStringEscape() throws characters for pattern
SQLException searching
public abstract String getSQLKeywords() Returns a comma-
throws SQLException separated list of
keywords that the
database recognizes, but
the keywords are not
SQL-92 keywords
public abstract String Returns a comma-
getStringFunctions() separated list of string
throws SQLException functions in the database
public abstract String Returns a comma-
getSystemFunctions() throws separated list of system
SQLException functions in the database
public abstract ResultSet Returns a ResultSet
getTablePrivileges(String catalog, String object that describes the
schemaPattern schemaPattern, String privileges for the
tableNamePattern) matching and
throws SQLException tableNamePattern; the
ResultSet object contains
the following columns:
catalog name, schema
name, table name,
grantor, grantee, type of
access, and “YES” if a
grantee can grant other
access
public abstract ResultSet Returns a ResultSet
getTables(String object that describes
catalog, String schemaPattern, String tables matching the
tableNamePattern, String types[]) schemaPattern and
throws SQLException tableNamePattern; the
ResultSet object contains
the following columns:
catalog name, schema
name, table name, table
type, and comments
public abstract ResultSet getTableTypes() Returns a ResultSet
throws SQLException object that describes the
table types available in
the database; the
ResultSet object contains
the column that is a list
of the table types
public abstract String Returns the date and
getTimeDateFunctions() throws time functions for the
SQLException database
public abstract ResultSet getTypeInfo() Returns a ResultSet
throws SQLException object that describes the
SQL data types supported
by the database; the
ResultSet object contains
the columns: type name,
SQL data type constants
in the Types class,
maximum precision,
prefix used to quote a
literal, suffix used to
quote a literal,
parameters used to
create the type,
nullability, case
sensitivity, searchability,
signed or unsigned
(boolean), is it a
currency, auto
incrementable or not,
local version of data
type, minimum scale,
maximum scale, empty
column, empty column,
and radix
public abstract String getURL() throws The URL for the database
SQLException
public abstract String getUserName() Returns the user name as
throws SQLException known by the database
public abstract ResultSet Returns a ResultSet
getVersionColumns(String catalog, object that describes the
String String table) throws SQLException specified table’s columns
that are updated when
any column is updated in
the table; the ResultSet
object contains the
following columns: empty
columns, column name,
SQL datatype, type name,
precision, column value
length in bytes, scale,
and pseudoColumn or not
public abstract boolean isCatalogAtStart() Returns true if the
throws SQLException catalog name appears at
the start of a qualified
table name
public abstract boolean isReadOnly() Returns true if the
throws SQLException database is in read only
mode
public abstract boolean Returns true if a
nullPlusNonNullIsNull() throws concatenation between a
SQLException NULL and non-NULL is
NULL
public abstract boolean
nullsAreSortedAtEnd()
throws SQLException
public abstract boolean
nullsAreSortedAtStart()
throws SQLException
public abstract boolean
nullsAreSortedHigh()
throws SQLException
public abstract boolean
nullsAreSortedLow()
throws SQLException
public abstract boolean
storesLowerCaseIdentifiers()
throws SQLException
public abstract boolean
storesLowerCaseQuotedIdentifiers()
throws SQLException
public abstract boolean
storesMixedCaseIdentifiers() throws
SQLException
public abstract boolean
storesMixedCaseQuotedIdentifiers()
throws SQLException
public abstract boolean
storesUpperCaseIdentifiers()
throws SQLException
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 boolean
supportsANSI92EntryLevelSQL() throws
SQLException
public abstract boolean
supportsANSI92FullSQL() throws
SQLException
public abstract boolean
supportsANSI92IntermediateSQL() throws
SQLException
public abstract boolean
supportsANSI92FullSQL() throws
SQLException
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 boolean
supportsColumnAliasing() throws
SQLException
public abstract boolean
supportsConvert() throws SQLException
public abstract boolean
supportsConvert(int fromType, int
toType) throws SQLException
public abstract boolean
supportsCoreSQLGrammar() throws
SQLException
public abstract boolean
supportsCorrelatedSubqueries() throws
SQLException
public abstract boolean
supportsDataDefinitionAnd
DataManipulationTransactions() throws
SQLException
public abstract boolean
supportsDataManipulation
TransactionsOnly() throws SQLException
public abstract boolean
supportsDifferentTableCorrelationNames(
) throws SQLException
public abstract boolean
supportsExpressionsInOrderBy() throws
SQLException
public abstract boolean
supportsExtendedSQLGrammar() throws
SQLException
public abstract boolean
supportsFullOuterJoins() throws
SQLException
public abstract boolean
supportsGroupBy() throws SQLException
public abstract boolean
supportsGroupByBeyondSelect() throws
SQLException
public abstract boolean
supportsGroupByUnrelated() throws
SQLException
public abstract boolean
supportsIntegrityEnhancementFacility()
throws SQLException
public abstract boolean
supportsLikeEscapeClause() throws
SQLException
public abstract boolean
supportsLimitedOuterJoins() throws
SQLException
public abstract boolean
supportsMinimumSQLGrammar() throws
SQLException
public abstract boolean
supportsMixedCaseIdentifiers() throws
SQLException
public abstract boolean
supportsMixedCaseQuotedIdentifiers()
throws SQLException
public abstract boolean
supportsMultipleResultSets() throws
SQLException
public abstract boolean
supportsMultipleTransactions() throws
SQLException
public abstract boolean
supportsNonNullableColumns() throws
SQLException
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 boolean
supportsOrderByUnrelated()
throws SQLException
public abstract boolean
supportsOuterJoins()
throws SQLException
public abstract boolean
supportsPositionedDelete()
throws SQLException
public abstract boolean
supportsPositionedUpdate()
throws SQLException
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 boolean
supportsSelectForUpdate()
throws SQLException
public abstract boolean
supportsStoredProcedures()
throws SQLException
public abstract boolean
supportsSubqueriesInComparisons()
throws SQLException
public abstract boolean
supportsSubqueriesInExists()
throws SQLException
public abstract boolean
supportsSubqueriesInIns()
throws SQLException
public abstract boolean
supportsSubqueriesInQuantifieds()
throws SQLException
public abstract boolean
supportsTableCorrelationNames() throws
SQLException
public abstract boolean
supportsTransactionIsolationLevel(int
level) throws SQLException
public abstract boolean
supportsTransactions() throws
SQLException
public abstract boolean
supportsUnion() throws SQLException
public abstract boolean
supportsUnionAll() throws SQLException
public abstract boolean
usesLocalFilePerTable() throws
SQLException
public abstract boolean
usesLocalFiles() throws SQLException
Variables
public final static int bestRowNotPseudo
public final static int bestRowPseudo
public final static int versionColumnUnknown
public final static int versionColumnNotPseudo
public final static int versionColumnPseudo
public final static int importedKeyCascade
public final static int importedKeyRestrict
public final static int importedKeySetNull
primary key has been updated or deleted
public final static int typeNoNulls
public final static int typeNullable
public final static int typeNullableUnknown
public final static int typePredNone
public final static int typePredChar
public final static int typePredBasic
public final static int typeSearchable
public final static short tableIndexStatistic
public final static short tableIndexClustered
public final static short tableIndexHashed
public final static short tableIndexOther
public interface Driver
The JDBC driver implements this interface. The JDBC driver must create an instance of
itself and then register with the DriverManager.
Methods
Method Name Additional Description
public abstract boolean Returns true if the driver can
acceptsURL(String URL) connect to the specified
throws SQLException database in the URL
public abstract Connection Connects to the database
connect(String url, Properties specified in the URL with the
props) throws SQLException specified Properties props
public abstract int Returns the JDBC driver’s
getMajorVersion() major version number
public abstract int Returns the JDBC driver’s
getMinorVersion() minor version number
public abstract Returns an array of
DriverPropertyInfo[] DriverPropertyInfo that
getPropertyInfo(String URL, contains possible properties
Properties props) throws based on the supplied URL
SQLException and props
public abstract boolean Returns true if the JDBC
jdbcCompliant() driver can pass the JDBC
compliance suite
public interface PreparedStatement
This object extends Statement, and it is used to perform queries that will be repeated.
This class exists primarily to optimize queries that will be executed repeatedly.
Methods
Methods
Method Name Additional Description
public abstract String
Returns the name of the
getCatalogName(int column)
catalog hit by the query
throws SQLException
public abstract int
Returns the number of
getColumnCount() throws
columns in the resulting table
SQLException
public abstract int Returns the specified column’s
getColumnDisplaySize(int maximum size
column) throws SQLException
public abstract String Gets a label, if it exists, for
getColumnLabel(int column) the specified column in the
throws SQLException result set
public abstract String Gets a name for the specific
getColumnName(int column) column number in the
throws SQLException resulting table
public abstract int Returns a constant in the
getColumnType(int column) Type class that is the JDBC
throws SQLException type of the specified column
in the result set
public abstract String Gets the name of the type of
getColumnTypeName(int the specified column in the
column) throws SQLException result set
public abstract int Returns the precision of the
getPrecision(int column) data in the specified column,
throws SQLException if applicable
public abstract int Returns the scale of the data
getScale(int column) throws in the specified column, if
SQLException applicable
public abstract String Returns the name of the
getSchemaName(int column) schema that was accessed in
throws SQLException the query to produce the
result set for the specific
column
Returns the name of the table
public abstract String
from which the specified
getTableName(int column)
column in the result set came
throws SQLException
from
public abstract boolean Returns true if the specified
isAutoIncrement (int column) column is automatically
throws SQLException numbered
public abstract boolean Returns true if the specified
isCaseSensitive (int column) column’s contents are case
throws SQLException sensitive, if applicable
public abstract boolean Returns true if the content of
isCurrency(int column) throws the specific column in the
SQLException result set was a currency
Returns true if a write
public abstract boolean
operation in the specified
isDefinitelyWritable(int
column can be done for
column) throws SQLException
certain
public abstract int
Returns true if the specified
isNullable(int column) throws
column accepts NULL entries
SQLException
public abstract boolean
Returns true if the specified
isReadOnly(int column)
column is read only
throws SQLException
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) throws contained in the specified
SQLException column in the result set is
signed, if applicable
public abstract boolean
Returns true if a write on the
isWritable(int column) throws
specified column is possible
SQLException
Variables
Variable Name Additional Description
public final static int
NULL values not allowed
columnNoNulls
public final static int
NULL values allowed
columnNullable
public final static int NULL values may or may not
columnNullableUnknown 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
public abstract void cancel() If a query is running in
throws SQLException another thread, a foreign
thread can cancel it by calling
this method on the local
Statement object’s
instantiation
public abstract void Clears the warnings for the
clearWarnings() throws Statement
SQLException
Closes the Statement and
public abstract void close()
frees its associated resources,
throws SQLException
including any ResultSets
public abstract boolean Executes the parameter sql,
execute(String sql) throws which is an SQL query; this
SQLException method accounts for multiple
ResultSets
public abstract ResultSet Executes a query that returns
executeQuery(String sql) a ResultSet object (produces
throws SQLException some results) using the sql
parameter as the SQL query
public abstract int Executes a query that does
executeUpdate(String sql) not produce a resulting table;
throws SQLException the method returns the
number of rows affected or 0
if no result is produced
public abstract int Returns the maximum amount
getMaxFieldSize() throws of data returned for a
SQLException resulting column; applies only
to the following SQL
datatypes: BINARY,
VARBINARY, LONGVARBINARY,
CHAR, VARCHAR, and
LONGVARCHAR
public abstract int Returns the maximum number
getMaxRows() throws of rows a ResultSet can
SQLException contain
public abstract boolean Returns true if the next
getMoreResults() throws ResultSet of the query is
SQLException present, and moves the
ResultSet into the current
result space
Returns the number of
public abstract int
seconds that the JDBC driver
getQueryTimeout() throws
will wait for a query to
SQLException
execute
public abstract ResultSet Returns a ResultSet object
getResultSet() throws that is the current result of
SQLException the query; only one of these
is returned if only one
ResultSet is the result of the
query; if more ResultSets are
present, the getMoreResults
method is used to move to the
next ResultSet
public abstract int Returns the update count; if
getUpdateCount() throws the result is a ResultSet, -1 is
SQLException returned
public abstract SQLWarning Returns the warnings
getWarnings() throws encountered for the query of
SQLException this Statement object
public abstract void Sets the name of a cursor for
setCursorName(String name) future reference, and uses it
throws SQLException in update statements
public abstract void Sets escape substitution
setEscapeProcessing(boolean processing
enable) throws SQLException
public abstract void Sets the maximum amount of
setMaxFieldSize(int max) data that can be returned for
throws SQLException a column of type BINARY,
VARBINARY, LONGVARBINARY,
CHAR, VARCHAR, and
LONGVARCHAR
public abstract void Sets the maximum number of
setMaxRows(int max) throws rows that can be retrieved in
SQLException a ResultSet
public abstract void
Sets the time a driver will
setQueryTimeout(int seconds)
wait for a query to execute
throws SQLException
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 Additional Description
public DataTruncation(int Builds a Throwable
index, boolean parameter, DataTruncation object with
boolean read, int dataSize, int the specified properties
transferSize)
Methods
Method Name Additional Description
public int getDataSize() Returns the number of bytes
that should have been
transferred
public int getIndex() Returns the index of the
column or parameter that was
interrupted
public boolean getParameter() Returns true if the truncated
value was a parameter, or
false if it was a column
public boolean getRead() Returns true if truncation
occurred on a read; false
means truncation occurred on
a write
public int getTransferSize() Returns the number of bytes
actually transferred
public class SQLException
This class extends java.lang.Exception. It is the responsibility of the JDBC driver to
throw this class when a problem occurs during an operation.
Constructors
These constructors are used to create an SQLException with the specified information. It
is normally not necessary to create an exception unless the developer is working on
creating a driver or higher level JDBC interface:
public SQLException()
public SQLException(String problem)
public SQLException(String problem, String SQLState)
public SQLException(String problem, String SQLState,
int vendorCode)
Methods
Method Name Additional Description
public int getErrorCode() Returns the error code that
was part of the thrown
exception
public SQLException Returns the next exception as
getNextException() an SQLException object
public String getSQLState() Returns the SQL state that
was part of the thrown
exception
public synchronized void Sets the next exception as
setNextException excp for the SQLException
(SQLException excp) object
public class SQLWarning
This class extends SQLException. It is the responsibility of the JDBC driver to throw
this class when a problem occurs during an operation.
Constructors
These constructors build an SQLWarning object with the specified information. It is
normally not necessary to create an SQLWarning unless the developer is working on
creating a driver or higher level JDBC interface:
public SQLWarning()
public SQLWarning(String problem)
public SQLWarning(String problem, String SQLstate)
public SQLWarning(String problem, String SQLstate, int
vendorCode)
Methods
Method Name Additional Description
public SQLWarning Returns an SQLWarning object
getNextWarning() that contains the next warning
public void Sets the next SQLWarning
setNextWarning(SQLWarning warning warn for the
warn) SQLWarning object