Professional Documents
Culture Documents
The JDBC DriverManager class defines objects which can connect Java
applications to a JDBC driver. The Standard Extension packages javax.naming
and javax.sql let you use a DataSource object registered with a Java Naming
and Directory Interface™ (JNDI) naming service to establish a connection with
a data source. You can use either connecting mechanism, but using a
DataSource object is recommended whenever possible.
The JDBC driver test suite helps you to determine that JDBC drivers will
run your program
4. JDBC-ODBC Bridge
The Java Software bridge provides JDBC access via ODBC drivers.
JDBC™ application connects to a target data source using one of two mechanisms:
Establishing a connection involves two steps: Loading the driver, and making the
connection.
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Calling the Class.forName automatically creates an instance of a driver and registers
it with the DriverManager, so you don't need to create an instance of the class. If you
were to create your own instance, you would be creating an unnecessary duplicate,
but it would do no harm.
2
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
String url = "jdbc:derby:Fred";
Connection con = DriverManager.getConnection(url, "login name", "password")
url
the JDBC URL; parts one and two are supplied by your driver, and the third part specifies
your data source
myLogin
your login name or user name
myPassword
your password for the DBMS
myDriver.ClassName
the class name supplied with your driver
Class.forName("myDriver.ClassName");
DataSource ds = ic.lookup("java:comp/env/jdbc/myDB");
Connection con = ds.getConnection();
DataSource ds = (DataSource) org.apache.derby.jdbc.ClientDataSource()
ds.setPort(1527);
ds.setHost("localhost");
ds.setUser("APP")
ds.setPassword("APP");
The ResultSet interface provides methods for retrieving and manipulating the
results of executed queries, and ResultSet objects can have different functionality
and characteristics.
The sensitivity of the ResultSet object is determined by one of three different ResultSet types:
1. TYPE_FORWARD_ONLY — The result set is not scrollable; its cursor moves forward only,
from before the first row to after the last row. The rows contained in the result set depend
3
on how the underlying database materializes the results. That is, it contains the rows that
satisfy the query at either the time the query is executed or as the rows are retrieved.
2. TYPE_SCROLL_INSENSITIVE — The result set is scrollable; its cursor can move both
forward and backward relative to the current position, and it can move to an absolute
position.
3. TYPE_SCROLL_SENSITIVE — The result set is scrollable; its cursor can move both
forward and backward relative to the current position, and it can move to an absolute
position.
When a ResultSet object is first created, the cursor is positioned before the first row. To move
the cursor, you can use the following methods:
• next() - moves the cursor forward one row. Returns true if the cursor is now positioned
on a row and false if the cursor is positioned after the last row.
• previous() - moves the cursor backwards one row. Returns true if the cursor is now
positioned on a row and false if the cursor is positioned before the first row.
• first() - moves the cursor to the first row in the ResultSet object. Returns true if the
cursor is now positioned on the first row and false if the ResultSet object does not
contain any rows.
• last() - moves the cursor to the last row in the ResultSet object. Returns true if the
cursor is now positioned on the last row and false if the ResultSet object does not
contain any rows.
• beforeFirst() - positions the cursor at the start of the ResultSet object, before the first
row. If the ResultSet object does not contain any rows, this method has no effect.
• afterLast() - positions the cursor at the end of the ResultSet object, after the last row.
If the ResultSet object does not contain any rows, this method has no effect.
• relative(int rows) - moves the cursor relative to its current position.
• absolute(int row) - positions the cursor on the row-th row of the ResultSet object.
The ResultSet interface declares getter methods (getBoolean, getLong, and so on)
for retrieving column values from the current row. Your application can retrieve
values using either the index number of the column or the name of the column. The
column index is usually more efficient. Columns are numbered from 1. For
maximum portability, result set columns within each row should be read in left-to-
right order, and each column should be read only once.
4
Column names used as input to getter methods are case insensitive. When a getter
method is called with a column name and several columns have the same name,
the value of the first matching column will be returned.
following line of code moves the cursor to the fourth row of srs:
srs.absolute(4);
If srs has 500 rows, the following line of code moves the cursor to row 497:
srs.absolute(-4);
srs.absolute(4); // cursor is on the fourth row
. . .
srs.relative(-3); // cursor is on the first row
. . .
srs.relative(2); // cursor is on the third row
The method getRow lets you check the number of the row where the cursor is
positioned.
If the method isAfterLast returns false, the cursor is not after the last row, so the
method afterLast is invoked.
if (srs.isAfterLast() == false) {
srs.afterLast();
}
while (srs.previous()) {
String name = srs.getString("COF_NAME");
float price = srs.getFloat("PRICE");
System.out.println(name + " " + price);
}
Updating Tables
5
Updating a row in a ResultSet object is a two-phase process. First, the new value for
each column being updated is set, and then the change is applied to the row. The
row in the underlying data source is not updated until the second phase is
completed.
First, you need to create a ResultSet object that is updatable. To do this, supply the
ResultSet constant CONCUR_UPDATABLE to the createStatement method, Statement
object it creates produces an updatable ResultSet object each time it executes a
query
COF_NAME PRICE
------------------ -----
Colombian 7.99
French_Roast 8.99
Espresso 9.99
Colombian_Decaf 8.99
French_Roast_Decaf 9.99
The method updateRow applies all column changes to the current row. The changes
are not made to the row until updateRow has been called. You can use the
cancelUpdates method to back out changes made to the row before the updateRow
method is called.
An update is the modification of a column value in the current row. Let's suppose that you want
to raise the price of French Roast Decaf coffee to 10.99:
uprs.last();
uprs.updateFloat("PRICE", 10.99);
uprs.updateRow();
Update operations affect column values in the row where the cursor is positioned, so in the first
line the ResultSet uprs calls the method last to move its cursor to the last row (the row where
the column COF_NAME has the value FRENCH_ROAST_DECAF). Once the cursor is on the last row,
6
all of the update methods you call will operate on that row until you move the cursor to another
row. The second line changes the value in the PRICE column to 10.99 by calling the method
updateFloat. This method is used because the column value we want to update is a float in the
Java programming language.
The ResultSet. updateXXX methods take two parameters: the column to update and the new
value to put in that column. As with the ResultSet.getXXX methods, the parameter designating
the column may be either the column name or the column number. There is a different
updateXXX method for updating each datatype ( updateString, updateBigDecimal,
updateInt, and so on) just as there are different getXXX methods for retrieving different
datatypes.
If you had moved the cursor to a different row before calling the method updateRow, the update
would have been lost. If, on the other hand, you realized that the price should really have been
10.79 instead of 10.99, you could have cancelled the update to 10.99 by calling the method
cancelRowUpdates. You have to invoke cancelRowUpdates before invoking the method
updateRow; once updateRow is called, calling the method cancelRowUpdates does nothing.
Note that cancelRowUpdates cancels all of the updates in a row, so if there are many
invocations of the updateXXX methods on the same row, you cannot cancel just one of them. The
following code fragment first cancels updating the price to 10.99 and then updates it to 10.79:
uprs.last();
uprs.updateFloat("PRICE", 10.99);
uprs.cancelRowUpdates();
uprs.updateFloat("PRICE", 10.79);
uprs.updateRow();
Code Fragment 1:
Code Fragment 2:
Note that when the return value for executeUpdate is 0, it can mean one of two things:
• the statement executed was an update statement that affected zero rows
•
• the statement executed was a DDL statement.
Using Transactions
A transaction is a set of one or more statements that are executed together as a
unit, so either all of the statements are executed, or none of the statements is
executed.
When a connection is created, it is in auto-commit mode. This means that each individual SQL
statement is treated as a transaction and is automatically committed right after it is executed.
The way to allow two or more statements to be grouped into a transaction is to disable auto-
commit mode. This is demonstrated in the following line of code, where con is an active
connection:
con.setAutoCommit(false);
con.setAutoCommit(false);
PreparedStatement updateSales = con.prepareStatement(
"UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?");
updateSales.setInt(1, 50);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate();
PreparedStatement updateTotal = con.prepareStatement(
"UPDATE COFFEES SET TOTAL = TOTAL + ? WHERE COF_NAME LIKE ?");
updateTotal.setInt(1, 50);
updateTotal.setString(2, "Colombian");
8
updateTotal.executeUpdate();
con.commit();
con.setAutoCommit(true);
To avoid conflicts during a transaction, a DBMS uses locks, mechanisms for blocking
access by others to the data that is being accessed by the transaction. (Note that in
auto-commit mode, where each statement is a transaction, locks are held for only
one statement.) Once a lock is set, it remains in force until the transaction is
committed or rolled back. For example, a DBMS could lock a row of a table until
updates to it have been committed. The effect of this lock would be to prevent a
user from getting a dirty read, that is, reading a value before it is made permanent.
(Accessing an updated value that has not been committed is considered a dirty read
because it is possible for that value to be rolled back to its previous value. If you
read a value that is later rolled back, you will have read an invalid value.)
Normally, you do not need to do anything about the transaction isolation level; you
can just use the default one for your DBMS. JDBC allows you to find out what
transaction isolation level your DBMS is set to (using the Connection method
getTransactionIsolation) and also allows you to set it to another level (using the
Connection method setTransactionIsolation). Keep in mind, however, that even
though JDBC allows you to set a transaction isolation level, doing so has no effect
unless the driver and DBMS you are using support it.
The JDBC 3.0 API adds the method Connection.setSavepoint, which sets a savepoint within
the current transaction. The Connection.rollback method has been overloaded to take a
savepoint argument.
The example below inserts a row into a table, sets the savepoint svpt1, and then inserts a second
row. When the transaction is later rolled back to svpt1, the second insertion is undone, but the
first insertion remains intact. In other words, when the transaction is committed, only the row
containing ?FIRST? will be added to TAB1:
"(?FIRST?)");
// set savepoint
Savepoint svpt1 = conn.setSavepoint("SAVEPOINT_1");
rows = stmt.executeUpdate("INSERT INTO TAB1 (COL1) " +
"VALUES (?SECOND?)");
...
conn.rollback(svpt1);
...
conn.commit();
Releasing a Savepoint
Once a savepoint has been released, attempting to reference it in a rollback operation causes an
SQLException to be thrown. Any savepoints that have been created in a transaction are
automatically released and become invalid when the transaction is committed, or when the entire
transaction is rolled back. Rolling a transaction back to a savepoint automatically releases and
makes invalid any other savepoints that were created after the savepoint in question.
As mentioned earlier, calling the method rollback aborts a transaction and returns any values
that were modified to their previous values. If you are trying to execute one or more statements
in a transaction and get an SQLException, you should call the method rollback to abort the
transaction and start the transaction all over again. That is the only way to be sure of what has
been committed and what has not been committed. Catching an SQLException tells you that
something is wrong, but it does not tell you what was or was not committed. Since you cannot
count on the fact that nothing was committed, calling the method rollback is the only way to be
sure.
A callableStatement object contains a call to a stored procedure; it does not contain the stored
procedure itself. The first line of code below creates a call to the stored procedure
SHOW_SUPPLIERS using the connection con. The part that is enclosed in curly braces is the escape
syntax for stored procedures. When the driver encounters "{call SHOW_SUPPLIERS}", it will
translate this escape syntax into the native SQL used by the database to call the stored procedure
named SHOW_SUPPLIERS.
10
try {
// Code that could generate an exception goes here.
// If an exception is generated, the catch block below
// will print out information about it.
} catch(SQLException ex) {
System.out.println("\n--- SQLException caught ---\n");
while (ex != null) {
System.out.println("Message: "
+ ex.getMessage ());
System.out.println("SQLState: "
+ ex.getSQLState ());
System.out.println("ErrorCode: "
+ ex.getErrorCode ());
ex = ex.getNextException();
System.out.println("");
}
}
http://java.sun.com/j2se/1.3/docs/guide/jdbc/getstart/GettingStartedTOC.fm.html
The standard syntax for JDBC URLs is shown here. It has three parts, which are separated by
colons.
jdbc:<subprotocol>:<subname>
In this example, the subprotocol is odbc, and the subname fred is a local ODBC data
source.
If one wants to use a network name service (so that the database name in the JDBC URL
does not have to be its actual name), the naming service can be the subprotocol. So, for
example, one might have a URL like:
jdbc:dcenaming:accounts-payable
In this example, the URL specifies that the local DCE naming service should resolve the
database name accounts-payable into a more specific name that can be used to connect
to the real database.
4. <subname>-a way to identify the data source. The subname can vary, depending on the
subprotocol, and it can have any internal syntax the driver writer chooses, including a
subsubname. The point of a subname is to give enough information to locate the data
source. In the previous example, fred is enough because ODBC provides the remainder
of the information. A data source on a remote server requires more information, however.
If the data source is to be accessed over the Internet, for example, the network address
should be included in the JDBC URL as part of the subname and should adhere to the
following standard URL naming convention:
5. //hostname:port/subsubname
Supposing that dbnet is a protocol for connecting to a host on the Internet, a JDBC URL
might look like this:
12
jdbc:dbnet://wombat:356/fred
The DriverManager class maintains a list of Driver classes that have registered themselves by
calling the method DriverManager.registerDriver. All Driver classes should be written with
a static section (a static initializer) that creates an instance of the class and then registers it with
the DriverManager class when it is loaded. Thus, a user would not normally call
DriverManager.registerDriver directly; it should be called automatically by a Driver class
when it is loaded. A Driver class is loaded, and therefore automatically registered with the
DriverManager, in one of two ways:
1. by calling the method Class.forName. This explicitly loads the driver class. Since it
does not depend on any external setup, this way of loading a driver is the recommended
one for using the DriverManager framework. The following code loads the class
acme.db.Driver:
2. Class.forName("acme.db.Driver");
jdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.test.ourDriver
The first call to a DriverManager method will automatically cause these driver classes to be
loaded.
Note that this second way of loading drivers requires a preset environment that is
persistent. If there is any doubt about that being the case, it is safer to call the
method Class.forName to explicitly load each driver. This is also the right method to
use to bring in a particular driver since once the DriverManager class has been
initialized, it will never recheck the jdbc.drivers property list.
Establishing a Connection
13
Once the Driver classes have been loaded and registered with the DriverManager class, they
are available for establishing a connection with a database. When a request for a connection is
made with a call to the DriverManager.getConnection method, the DriverManager tests each
driver in turn to see if it can establish a connection.
It may sometimes be the case that more than one JDBC driver is capable of connecting to a given
URL. For example, when connecting to a given remote database, it might be possible to use a
JDBC-ODBC bridge driver, a JDBC-to-generic-network-protocol driver, or a driver supplied by
the database vendor. In such cases, the order in which the drivers are tested is significant because
the DriverManager will use the first driver it finds that can successfully connect to the given
URL.
First the DriverManager tries to use each driver in the order it was registered. (The drivers listed
in jdbc.drivers are always registered first.) It will skip any drivers that are untrusted code
unless they have been loaded from the same source as the code that is trying to open the
connection.
It tests the drivers by calling the method Driver.connect on each one in turn, passing them the
URL that the user originally passed to the method DriverManager.getConnection. The first
driver that recognizes the URL makes the connection.
The following code is an example of all that is normally needed to set up a connection with a
driver such as a JDBC-ODBC bridge driver.
The variable con represents a connection to the data source "fred" that can be used to create and
execute SQL statements.
All DriverManager methods are declared static, which means that they operate on the class as
a whole and not on particular instances. In fact, the constructor for DriverManager is declared
private to prevent users from instantiating it. Logically, there is one instance of the
DriverManager class.
14
http://www.javacamp.org/moreclasses/jdbc/jdbc.html
To load the driver, you need to load the appropriate class, make a driver instance and register it
with the JDBC driver manager. Use Class.forName(String) method. This method takes a string
representing a fully qualified class name and loads the corresponding class.
try {
Class.forName("connect.microsoft.MicrosoftDriver");
//Class.forName("oracle.jdbc.driver.OracleDriver"); for Oracle driver
//Class.forName("com.sybase.jdbc.SybDriver"); for sybase driver
} catch(ClassNotFoundException e) {
System.err.println("Error loading driver: " + e);
}
Once you have loaded the JDBC driver, you need to specify the location of the database server.
Generally, the url format is: jdbc:vendordb:userinfo plus server host, port number and database
name.
To make the actual network connection, pass the URL, the database username, and the password
to the getConnection method of the DriverManager class, as illustrated in the following example.
A Statement object is used to send queries and commands to the database and is created from the
Connection as follows:
Once you have a Statement object, you can use it to send SQL queries by using the executeQuery
method, which returns an object of type ResultSet. Here is an example:
The simplest way to handle the results is to process them one row at a time, using the ResultSet’s
next method to move through the table a row at a time. Within a row, ResultSet provides various
getXxx methods that take a column index or column name as an argument and return the result
as a variety of different Java types. For instance, use getInt if the value should be an integer,
getString for a String, and so on for most other data types. If you just want to display the results,
you can use getString regardless of the actual column type. However, if you use the version that
takes a column index, note that columns are indexed starting at 1 (following the SQL
16
convention), not at 0 as with arrays, vectors, and most other data structures in the Java
programming language.
while(resultSet.next()) {
System.out.println(results.getString(1) + " " +
results.getString(2) + " " +
results.getString(3));
}
connection.close();
You should postpone this step if you expect to perform additional database operations, since the
overhead of opening a connection is usually large. In fact, reusing existing connections is such an
important optimization.