Chapter 18. Accessing Databases with JDBC

Almost all Java programs deal with data in some way. You have used primitive types, objects, arrays, linked lists, and other data structures up to this point.

Today you work with data in a more sophisticated way by exploring Java Database Connectivity (JDBC), a class library that connects Java programs to relational databases.

Java 6 includes Java DB, a small relational database that’s part of the Java Development Kit, making it easier than ever to incorporate a database into your applications.

Today you explore JDBC and Extensible Markup Language (XML) in the following ways:

  • Using JDBC drivers to work with different relational databases

  • Accessing a database with Structured Query Language (SQL)

  • Reading records from a database using SQL and JDBC

  • Adding records to a database using SQL and JDBC

  • Creating a new Java DB database and reading its records

Java Database Connectivity

Java Database Connectivity (JDBC) is a set of classes that can be used to develop client/server applications that work with databases developed by Microsoft, Sybase, Oracle, Informix, and other sources.

With JDBC, you can use the same methods and classes in Java programs to read and write records and perform other kinds of database access. A class called a driver acts as a bridge to the database source—there are drivers for each of the popular databases.

Client/server software connects a user of information with a provider of that information, and it’s one of the most commonplace forms of programming. You use it every time you surf the Web: A web browser client requests pages, image files, and other documents using a uniform resource locator, or URL. Web servers provide the requested information, if it can be found, for the client.

One of the biggest obstacles faced by database programmers is the wide variety of database formats in use, each with its own proprietary method of accessing data.

To simplify using relational database programs, a standard language called SQL (Structured Query Language) has been introduced. This language supplants the need to learn different database-querying languages for each database format. Java DB, the database included in Java 6, supports SQL.

In database programming, a request for records in a database is called a query. Using SQL, you can send complex queries to a database and get the records you’re looking for in any order you specify.

Consider the example of a database programmer at a student loan company who has been asked to prepare a report on the most delinquent loan recipients. The programmer could use SQL to query a database for all records in which the last payment was more than 180 days ago and the amount due is more than $0.00. SQL also can be used to control the order in which records are returned, so the programmer can get the records in the order of Social Security number, recipient name, amount owed, or another field in the loan database.

All this is possible with SQL—the programmer doesn’t need any of the proprietary languages associated with popular database formats.

Caution

SQL is strongly supported by many database formats, so, in theory, you should be able to use the same SQL commands for each database tool that supports the language. However, you will still need to learn the idiosyncrasies of a specific database format when accessing it through SQL.

SQL is the industry-standard approach to accessing relational databases. JDBC supports SQL, enabling developers to use a wide range of database formats without knowing the specifics of the underlying database. JDBC also supports the use of database queries specific to a database format.

The JDBC class library’s approach to accessing databases with SQL is comparable to existing database-development techniques, so interacting with an SQL database by using JDBC isn’t much different than using traditional database tools. Java programmers who already have some database experience can hit the ground running with JDBC.

The JDBC library includes classes for each of the tasks commonly associated with database usage:

  • Making a connection to a database

  • Creating a statement using SQL

  • Executing that SQL query in the database

  • Viewing the resulting records

These JDBC classes are all part of the java.sql package.

Database Drivers

Java programs that use JDBC classes can follow the familiar programming model of issuing SQL statements and processing the resulting data. The format of the database and the platform it was prepared on don’t matter.

This platform- and database independence is made possible by a driver manager. The classes of the JDBC class library are largely dependent on driver managers, which keep track of the drivers required to access database records. You’ll need a different driver for each database format that’s used in a program, and sometimes you might need several drivers for versions of the same format. Java DB includes its own driver.

JDBC also includes a driver that bridges JDBC and another database-connectivity standard, ODBC.

The JDBC-ODBC Bridge

ODBC, Microsoft’s common interface for accessing SQL databases, is managed on a Windows system by the ODBC Data Source Administrator.

This is run from Control Panel on a Windows system; to get there on most versions of Windows, click Start, Settings, Control Panel, ODBC Data Sources. On Windows XP, choose Start, Control Panel, Performance and Maintenance in Category View or Start, Control Panel in Classic View and then choose Administrative Tools, Data Sources (ODBC).

The administrator adds ODBC drivers, configures drivers to work with specific database files, and logs SQL use. Figure 18.1 shows the ODBC Data Source Administrator on a Windows system.

The ODBC Data Source Administrator on a Windows XP system.

Figure 18.1. The ODBC Data Source Administrator on a Windows XP system.

In Figure 18.1, the Drivers tab of the ODBC Data Source Administrator dialog box lists all the ODBC drivers present on the system. Many of the drivers are specific to a database company’s format, such as the Microsoft Access Driver.

The JDBC-ODBC bridge allows JDBC drivers to be used as ODBC drivers by converting JDBC method calls into ODBC function calls.

Using the JDBC-ODBC bridge requires three things:

  • The JDBC-ODBC bridge driver included with Java: sun.jdbc.odbc. JdbcOdbcDriver

  • An ODBC driver

  • An ODBC data source that has been associated with the driver using software such as the ODBC Data Source Administrator

ODBC data sources can be set up from within some database programs. For example, when a new database file is created in Lotus Approach, users have the option of associating it with an ODBC driver.

All ODBC data sources must be given short, descriptive names. The name is used inside Java programs when a connection is made to the database that the source refers to.

On a Windows system, after an ODBC driver is selected and the database is created, they show up in the ODBC Data Source Administrator. Figure 18.2 shows an example of this for a data source named WorldEnergy.

A listing of data sources in the ODBC Data Sources Administrator.

Figure 18.2. A listing of data sources in the ODBC Data Sources Administrator.

The data source WorldEnergy is associated with a Microsoft Access driver, according to Figure 18.2.

Note

Most Windows database programs include one or more ODBC drivers that correspond to the format. Microsoft Access includes ODBC drivers that can be used to connect to an Access database file.

Connecting to an ODBC Data Source

Your first project today is a Java application that uses a JDBC-ODBC bridge to connect to an Access file.

The Access file for this project is world20.mdb, a database of world energy statistics published by the U.S. Energy Information Administration. The Coal table in this database includes three fields you will be using in the project:

  • Country

  • Year

  • Anthracite Production

The database used in this project is included on this book’s official website at http://www.java21days.com.

To use this database, you must have an ODBC driver on your system that supports Access files. Using the ODBC Data Source Administrator (or a similar program if you’re on a non-Windows system), you must create a new ODBC data source associated with world20.mdb.

Other setup work might be needed depending on the ODBC drivers present on your system, if any. Consult the documentation included with the ODBC driver.

After you have downloaded world20.mdb to your computer or found another database that’s compatible with the ODBC drivers on your system, the final step in getting the file ready for JDBC-ODBC is to create a data source associated with it. Unlike other input-output classes in Java, JDBC doesn’t use a filename to identify a data file and use its contents. Instead, a tool such as the ODBC Data Source Administrator is used to name the ODBC source and indicate the file folder where it can be found.

In the ODBC Data Source Administrator, click the User DSN tab to see a list of data sources that are available. To add a new one associated with world20.mdb (or your own database), click the Add button, choose an ODBC driver, and then click the Finish button.

A Setup window opens that you can use to provide a name, short description, and other information about the database. Click the Select button to find and choose the database file.

Figure 18.3 shows the Setup window used to set up world20.mdb as a data source in the ODBC Data Source Administrator.

The ODBC driver Setup window.

Figure 18.3. The ODBC driver Setup window.

After a database has been associated with an ODBC data source, working with it in a Java program is relatively easy if you are conversant with SQL.

The first task in a JDBC program is to load the driver (or drivers) that will be used to connect to a data source. A driver is loaded with the Class.forName(String) method. Class, part of the java.lang package, can be used to load classes into the Java interpreter. The forName(String) method loads the class named by the specified string. A ClassNotFoundException can be thrown by this method.

All programs that use an ODBC data source use sun.jdbc.odbc.JdbcOdbcDriver, the JDBC-ODBC bridge driver included with Java. Loading this class into a Java interpreter requires the following statement:

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

After the driver has been loaded, you can establish a connection to the data source by using the DriverManager class in the java.sql package.

The getConnection(String, String, String) method of DriverManager can be used to set up the connection. It returns a reference to a Connection object representing an active data connection.

The three arguments of this method are as follows:

  • A name identifying the data source and the type of database connectivity used to reach it

  • A username

  • A password

The last two items are needed only if the data source is secured with a username and a password. If not, these arguments can be null strings (“”).

The name of the data source is preceded by the text jdbc:odbc: when using the JDBC-ODBC bridge, which indicates the type of database connectivity in use.

The following statement could be used to connect to a data source called Payroll with a username of “Doc” and a password of “1rover1”:

Connection payday = DriverManager.getConnection(
    "jdbc:odbc:Payroll", "Doc", "1rover1");

After you have a connection, you can reuse it each time you want to retrieve or store information from that connection’s data source.

The getConnection() method and all others called on a data source throw SQLException errors if something goes wrong as the data source is being used. SQL has its own error messages, and they are passed along as part of SQLException objects.

Retrieving Data from a Database Using SQL

An SQL statement is represented in Java by a Statement object. Statement is an interface, so it can’t be instantiated directly. However, an object that implements the interface is returned by the createStatement() method of a Connection object, as in the following example:

Statement lookSee = payday.CreateStatement();

After you have a Statement object, you can use it to conduct an SQL query by calling the object’s executeQuery(String) method. The String argument should be an SQL query that follows the syntax of that language.

Caution

It’s beyond the scope of today’s lesson to teach SQL, a rich, data-retrieval and storage language that has its own book in this series: Sams Teach Yourself SQL in 21 Days, 4th Edition by Ron Plew and Ryan Stephens (ISBN: 0-672-32451-2). Although you need to learn SQL to do any extensive work with it, much of the language is easy to pick up from any examples you can find, such as those you will work with today.

The following is an example of an SQL query that could be used on the Coal table of the world20.mdb database:

SELECT Country, Year, 'Anthracite Production' FROM Coal
    WHERE (Country Is Not Null) ORDER BY Year

This SQL query retrieves several fields for each record in the database for which the Country field is not equal to null. The records returned are sorted according to their Country field, so Afghanistan would precede Burkina Faso.

The following Java statement executes that query on a Statement object named looksee:

ResultSet set = looksee.executeQuery(
    "SELECT Country, Year, 'Anthracite Production' FROM Coal "
    + "WHERE (Country Is Not Null) ORDER BY Year");

If the SQL query has been phrased correctly, the executeQuery() method returns a ResultSet object holding all the records that have been retrieved from the data source.

Note

To add records to a database instead of retrieving them, the statement’s executeUpdate() method should be called. You will work with this later.

When a ResultSet is returned from executeQuery(), it is positioned at the first record that has been retrieved. The following methods of ResultSet can be used to pull information from the current record:

  • getDate(String)—. Returns the Date value stored in the specified field name (using the Date class in the java.sql package, not java.util.Date)

  • getDouble(String)—. Returns the double value stored in the specified field name

  • getFloat(String)—. Returns the float value stored in the specified field name

  • getInt(String)—. Returns the int value stored in the specified field name

  • getLong(String)—. Returns the long value stored in the specified field name

  • getString(String)—. Returns the String stored in the specified field name

These are just the simplest methods available in the ResultSet interface. The methods you should use depend on the form that the field data takes in the database, although methods such as getString() and getInt() can be more flexible in the information they retrieve from a record.

You also can use an integer as the argument to any of these methods, such as getString(5), instead of a string. The integer indicates which field to retrieve (1 for the first field, 2 for the second field, and so on).

An SQLException is thrown if a database error occurs as you try to retrieve information from a resultset. You can call this exception’s getSQLState() and getErrorCode() methods to learn more about the error.

After you have pulled the information you need from a record, you can move to the next record by calling the next() method of the ResultSet object. This method returns a false Boolean value when it tries to move past the end of a resultset.

Normally, you can move through a resultset once from start to finish, after which you can’t retrieve its contents again.

When you’re finished using a connection to a data source, you can close it by calling the connection’s close() method with no arguments.

Listing 18.1 contains the CoalReporter application, which uses the JDBC-ODBC bridge and an SQL statement to retrieve some records from an energy database. Four fields are retrieved from each record indicated by the SQL statement: FIPS, Country, Year, and Anthracite Production. The resultset is sorted according to the Year field, and these fields are displayed to standard output.

Example 18.1. The Full Text of CoalReporter.java

 1: import java.sql.*;
 2:
 3: public class CoalReporter {
 4:     public static void main(String[] arguments) {
 5:        String data = "jdbc:odbc:WorldEnergy";
 6:        try {
 7:            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
 8:            Connection conn = DriverManager.getConnection(
 9:                data, "", "");
10:            Statement st = conn.createStatement();
11:            ResultSet rec = st.executeQuery(
12:                "SELECT * " +
13:                "FROM Coal " +
14:                "WHERE " +
15:                "(Country='" + arguments[0] + "') " +
16:                "ORDER BY Year");
17:            System.out.println("FIPS	COUNTRY		YEAR	" +
18:                "ANTHRACITE PRODUCTION");
19:            while(rec.next()) {
20:                System.out.println(rec.getString(1) +  "	"
21:                        + rec.getString(2) + "		"
22:                        + rec.getString(3) + "	"
23:                        + rec.getString(4));
24:            }
25:            st.close();
26:        } catch (SQLException s) {
27:            System.out.println("SQL Error: " + s.toString() + " "
28:                + s.getErrorCode() + " " + s.getSQLState());
29:       } catch (Exception e) {
30:           System.out.println("Error: " + e.toString()
31:               + e.getMessage());
32:       }
33:    }
34: }

This program must be run with a single argument specifying the Country field in the database from which to pull records, as in this example for the JDK:

java CoalReporter Poland

If the application were run with an argument of Poland, the output from the sample database would be the following:

FIPS   COUNTRY    YEAR    ANTHRACITE PRODUCTION
PL     Poland     1990    0.0
PL     Poland     1991    0.0
PL     Poland     1992    0.0
PL     Poland     1993    174.165194805424
PL     Poland     1994    242.50849909616
PL     Poland     1995    304.237935229728
PL     Poland     1996    308.64718066784
PL     Poland     1997    319.67029426312
PL     Poland     1998    319.67029426312

Try running the program with other countries that produce anthracite, such as France, Swaziland, and New Zealand. For any country that has a space in the name, remember to put quotation marks around the country name when running the program.

Writing Data to a Database Using SQL

In the CoalReporter application, you retrieved data from a database using an SQL statement prepared as a string, like this:

SELECT * FROM Coal WHERE (Country='Swaziland') ORDER BY YEAR

This is a common way to use SQL. You could write a program that asks a user to enter an SQL query and then displays the result (though this isn’t a good idea—SQL queries can be used to delete records, tables, and even entire databases).

The java.sql package also supports another way to create an SQL statement: a prepared statement.

A prepared statement, which is represented by the PreparedStatement class, is an SQL statement that is compiled before it is executed. This enables the statement to return data more quickly and is a better choice if you are executing an SQL statement repeatedly in the same program.

Tip

Prepared statements also have another advantage on Windows systems: They make it possible to write data to an Access database using the JDBC-ODBC driver. I’ve had little luck writing data from Java to Access using statements but can use prepared statements without any trouble.

To create a prepared statement, call a connection’s prepareStatement(String) method with a string that indicates the structure of the SQL statement.

To indicate the structure, you write an SQL statement in which parameters have been replaced with question marks.

Here’s an example for a connection object called cc:

PreparedStatement ps = cc.prepareStatement(
    "SELECT * FROM Coal WHERE (Country='?') ORDER BY YEAR");

Here’s another example with more than one question mark:

PreparedStatement ps = cc.prepareStatement(
    "INSERT INTO BOOKDATA VALUES(?, ?, ?, ?, ?, ?, ?)");

The question marks in these SQL statements are placeholders for data. Before you can execute the statement, you must put data in each of these places using one of the methods of the PreparedStatement class.

To put data into a prepared statement, you must call a method with the position of the placeholder followed by the data to insert.

For example, to put the string “Swaziland” in the first prepared statement, call the setString(int, String) method:

ps.setString(1, "Swaziland");

The first argument indicates the position of the placeholder, numbered from left to right. The first question mark is 1, the second is 2, and so on.

The second argument is the data to put in the statement at that position.

The following methods are available:

  • setAsciiStream(int, InputStream, int)—. At the position indicated by the first argument, inserts the specified InputStream, which represents a stream of ASCII characters. The third argument indicates how many bytes from the input stream to insert.

  • setBinaryStream(int, InputStream, int)—. At the position indicated by the first argument, inserts the specified InputStream, which represents a stream of bytes. The third argument indicates the number of bytes to insert from the stream.

  • setCharacterStream(int, Reader, int)—. At the position indicated by the first argument, inserts the specified Reader, which represents a character stream. The third argument indicates the number of characters to insert from the stream.

  • setBoolean(int, boolean)—. Inserts a boolean value at the position indicated by the integer.

  • setByte(int, byte)—. Inserts a byte value at the indicated position.

  • setBytes(int, byte[])—. Inserts an array of bytes at the indicated position.

  • setDate(int, Date)—. Inserts a Date object (from the java.sql package) at the indicated position.

  • setDouble(int, double)—. Inserts a double value at the indicated position.

  • setFloat(int, float)—. Inserts a float value at the indicated position.

  • setInt(int, int)—. Inserts an int value at the indicated position.

  • setLong(int, long)—. Inserts a long value at the indicated position.

  • setShort(int, short)—. Inserts a short value at the indicated position.

  • setString(int, String)—. Inserts a String value at the indicated position.

There’s also a setNull(int, int) method that stores SQL’s version of a null (empty) value at the position indicated by the first argument.

The second argument to setNull() should be a class variable from the Types class in java.sql to indicate what kind of SQL value belongs in that position.

There are class variables for each of the SQL data types. This list, which is not complete, includes some of the most commonly used variables: BIGINT, BIT, CHAR, DATE, DECIMAL, DOUBLE, FLOAT, INTEGER, SMALLINT, TINYINT, and VARCHAR.

The following code puts a null CHAR value at the fifth position in a prepared statement called ps:

ps.setNull(5, Types.CHAR);

The next project demonstrates the use of a prepared statement to add stock quote data to a database. Quotes are collected from the Yahoo! website.

As a service to people who follow the stock market, Yahoo! offers a Download Spreadsheet link on its main stock quote page for each ticker symbol.

To see this link, look up a stock quote on Yahoo! or go directly to a page such as this one:

http://quote.yahoo.com/q?s=sunw&d=v1

Below the price chart, you can find a Download Data link. Here’s what the link looks like for Sun Microsystems:

http://download.finance.yahoo.com/d/quotes.csv?s=SUNW&f=sl1d1t1c1ohgv&e=.csv

You can click this link to open the file or save it to a folder on your system. The file, which is only one line long, contains the stock’s price and volume data saved at the last market close. Here’s an example of what Sun’s data looked like on Feb. 23, 2007:

"SUNW",6.27,"2/23/2007","4:00pm",0.00,6.30,6.31,6.22,50254356

The fields in this data, in order, are the ticker symbol, closing price, date, time, price change since yesterday’s close, daily low, daily high, daily open, and volume.

The QuoteData application uses each of these fields except one—the time, which isn’t particularly useful because it’s always the time the market closed.

The following takes place in the program:

  • The ticker symbol of a stock is taken as a command-line argument.

  • A QuoteData object is created with the ticker symbol as an instance variable called ticker.

  • The object’s retrieveQuote() method is called to download the stock data from Yahoo! and return it as a String.

  • The object’s storeQuote() method is called with that String as an argument. It saves the stock data to a database using a JDBC-ODBC connection.

The last task requires a stock quote database, which can be reached through JDBC-ODBC, set up to collect this data.

Windows users can download quotedata.mdb, an Access 2000 database created to hold Yahoo!’s stock quote data, from the book’s website. Visit http://www.java21days.com and open the Day 18 page. After you download the database (or create one of your own), use the ODBC Data Source Administrator to create a new data source associated with the database. This application assumes that the name of the source is QuoteData.

Enter the text of Listing 18.2 into your editor and save the file as QuoteData.java.

Example 18.2. The Full Text of QuoteData.java

 1: import java.io.*;
 2: import java.net.*;
 3: import java.sql.*;
 4: import java.util.*;
 5:
 6: public class QuoteData {
 7:     private String ticker;
 8:
 9:     public QuoteData(String inTicker) {
10:        ticker = inTicker;
11:    }
12:
13:    private String retrieveQuote() {
14:        StringBuffer buf = new StringBuffer();
15:        try {
16:            URL page = new URL("http://quote.yahoo.com/d/quotes.csv?s=" +
17:               ticker + "&f=sl1d1t1c1ohgv&e=.csv");
18:            String line;
19:            URLConnection conn = page.openConnection();
20:            conn.connect();
21:            InputStreamReader in= new InputStreamReader(
22:                conn.getInputStream());
23:            BufferedReader data = new BufferedReader(in);
24:            while ((line = data.readLine()) != null) {
25:                buf.append(line + "
");
26:            }
27:        } catch (MalformedURLException mue) {
28:            System.out.println("Bad URL: " + mue.getMessage());
29:        } catch (IOException ioe) {
30:            System.out.println("IO Error:" + ioe.getMessage());
31:        }
32:        return buf.toString();
33:     }
34:
35:     private void storeQuote(String data) {
36:         StringTokenizer tokens = new StringTokenizer(data, ",");
37:         String[] fields = new String[9];
38:         for (int i = 0; i < fields.length; i++) {
39:             fields[i] = stripQuotes(tokens.nextToken());
40:         }
41:         String datasource = "jdbc:odbc:QuoteData";
42:         try {
43:             Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
44:             Connection conn = DriverManager.getConnection(
45:                 datasource, "", "");
46:             PreparedStatement prep2 = conn.prepareStatement(
47:                 "INSERT INTO " +
48:                 "Stocks(ticker, price, quoteDate, change, open, " +
49:                 "high, low, volume) " +
50:                 "VALUES(?, ?, ?, ?, ?, ?, ?, ?)");
51:             prep2.setString(1, fields[0]);
52:             prep2.setString(2, fields[1]);
53:             prep2.setString(3, fields[2]);
54:             prep2.setString(4, fields[4]);
55:             prep2.setString(5, fields[5]);
56:             prep2.setString(6, fields[6]);
57:             prep2.setString(7, fields[7]);
58:             prep2.setString(8, fields[8]);
59:             prep2.executeUpdate();
60:             conn.close();
61:         } catch (SQLException sqe) {
62:             System.out.println("SQL Error: " + sqe.getMessage());
63:         } catch (ClassNotFoundException cnfe) {
64:             System.out.println(cnfe.getMessage());
65:         }
66:     }
67:
68:     private String stripQuotes(String input) {
69:         StringBuffer output = new StringBuffer();
70:         for (int i = 0; i < input.length(); i++) {
71:             if (input.charAt(i) != '"') {
72:                 output.append(input.charAt(i));
73:             }
74:         }
75:         return output.toString();
76:     }
77:
78:     public static void main(String[] arguments) {
79:         if (arguments.length < 1) {
80:             System.out.println("Usage: java QuoteData tickerSymbol");
81:             System.exit(0);
82:         }
83:         QuoteData qd = new QuoteData(arguments[0]);
84:         String data = qd.retrieveQuote();
85:         qd.storeQuote(data);
86:     }
87: }

After you compile the QuoteData application, connect to the Internet and run the program. Remember to specify a valid ticker symbol as a command-line argument. To load the current quote for SUNW (Sun Microsystems):

java QuoteData SUNW

The retrieveQuote() method (lines 13–33) downloads the quote data from Yahoo! and saves it as a string. The techniques used in this method were covered on Day 17, “Communicating Across the Internet.”

The storeQuote() method (lines 35–66) uses the SQL techniques covered in this section.

The method begins by splitting up the quote data into a set of string tokens, using the comma character (“,”) as the delimiter between each token. The tokens are then stored in a String array with nine elements.

The array contains the same fields as the Yahoo! data in the same order: ticker symbol, closing price, date, time, price change, low, high, open, and volume.

Next, a data connection to the QuoteData data source is created using the JDBC-ODBC driver (lines 41–45).

This connection is then used to create a prepared statement (lines 46–50). This statement uses the INSERT INTO SQL statement, which causes data to be stored in a database. In this case, the database is quotedata.mdb, and the INSERT INTO statement refers to the Stocks table in that database.

Eight placeholders are in the prepared statement. Only eight are needed, instead of nine, because the application does not use the time field from the Yahoo! data.

A series of setString() methods puts the elements of the String array into the prepared statement, in the same order that the fields exist in the database: ticker symbol, closing price, date, price change, low, high, open, and volume (lines 51–58).

Some fields in the Yahoo! data are dates, floating-point numbers, and integers, so you might think that it would be better to use setDate(), setFloat(), and setInt() for that data.

Some versions of Access, including Access 2000, do not support some of these methods when you are using SQL to work with the database, even though they exist in Java. If you try to use an unsupported method, such as setFloat(), an SQLException error occurs.

It’s easier to send Access strings and let the database program convert them automatically into the correct format. This is likely to be true when you are working with other databases; the level of SQL support varies based on the product and ODBC driver involved.

After the prepared statement has been prepared and all the placeholders are filled, the statement’s executeUpdate() method is called (line 59). This either adds the quote data to the database or throws an SQL error. The private method stripQuotes() is used to remove quotation marks from Yahoo!’s stock data. This method is called on line 39 to take care of three fields that contain extraneous quotes: the ticker symbol, date, and time.

Moving Through Resultsets

The default behavior of resultsets permits one trip through the set using its next() method to retrieve each record.

By changing how statements and prepared statements are created, you can produce resultsets that support these additional methods:

  • afterLast()—. Moves to a place immediately after the last record in the set

  • beforeFirst()—. Moves to a place immediately before the first record in the set

  • first()—. Moves to the first record in the set

  • last()—. Moves to the last record in the set

  • previous()—. Moves to the previous record in the set

These actions are possible when the resultset’s policies have been specified as arguments to a database connection’s createStatement() and prepareStatement() methods.

Normally, createStatement() takes no arguments, as in this example:

Connection payday = DriverManager.getConnection(
    "jdbc:odbc:Payroll", "Doc", "1rover1");
Statement lookSee = payday.CreateStatement();

For a more flexible resultset, call createStatement() with three integer arguments that set up how it can be used. Here’s a rewrite of the preceding statement:

Statement lookSee = payday.CreateStatement(
    ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_READ_ONLY,
    ResultSet.CLOSE_CURSORS_AT_COMMIT);

The same three arguments can be used in the prepareStatement(String, int, int, int) method after the text of the statement.

The ResultSet class includes other class variables that offer more options in how sets can be read and modified.

JDBC Drivers

Creating a Java program that uses a JDBC driver is similar to creating one that uses the JDBC-ODBC bridge.

Java 6 includes the Java DB relational database, which is built from the open source Apache Derby database, and comes with its own driver. For more sophisticated databases, more than a dozen companies, including Informix, Oracle, Symantec, IBM, and Sybase, sell drivers or package them with commercial products. A database of available JDBC drivers can be found on Sun’s JDBC site at http://developers.sun.com/product/jdbc/drivers.

Note

The developers of the MySQL database offer Connector/J, a free open source JDBC driver developed by Mark Matthews. Some of these drivers are available to download for evaluation.

To download this driver or find out more about it, visit the web page http://dev.mysql.com/downloads/connector/j/5.0.html.

Java DB can be found in a db subfolder of the JDK installation. To develop applications that connect to the database, you must make its driver class library accessible. One way to accomplish this is to edit your Classpath environment variable.

The driver library is found in derby.jar in the db/lib subfolder. If you installed the JDK in C:Program Filesjdk1.6.0, this library is in C:Program Filesjdk1.6.0dblibderby.jar. Add the entire file reference, including the folder and filename, to your Classpath.

The steps for setting up a data source for JDBC are similar to those employed with the JDBC-ODBC bridge:

  1. Create the database.

  2. Associate the database with a JDBC driver.

  3. Establish a data source, which may include selecting a database format, database server, username, and password.

Listing 18.3 is a Java application that can perform two tasks:

  1. Create a Java DB database named Presidents with a database table called contacts that contains four records.

  2. Read the records from this database table.

This database is an Access file with contact information for U.S. presidents.

Example 18.3. The Full Text of Presidents.java

  1: import java.io.*;
  2: import java.sql.*;
  3:
  4: public class Presidents {
  5:     String home, system;
  6:
  7:     public Presidents() {
  8:         // set the database's directory
  9:         home = System.getProperty("user.home", ".");
 10:         system = home + File.separatorChar + ".database";
 11:         System.setProperty("derby.system.home", system);
 12:     }
 13:
 14:     public void createDatabase() {
 15:         // create the database
 16:         String data = "jdbc:derby:presidents;create=true";
 17:         try {
 18:             // load the driver
 19:             Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
 20:             // create the connection
 21:             Connection conn = DriverManager.getConnection(data);
 22:             Statement st = conn.createStatement();
 23:             // create the contacts table
 24:             int result = st.executeUpdate(
 25:                 "CREATE TABLE contacts ("
 26:                     + "dex INTEGER NOT NULL PRIMARY KEY "
 27:                     + "GENERATED ALWAYS AS identity "
 28:                     + "(START WITH 1, INCREMENT BY 1), "
 29:                     + "name VARCHAR(40), "
 30:                     + "address1 VARCHAR(40), "
 31:                     + "address2 VARCHAR(40), "
 32:                     + "phone VARCHAR(20), "
 33:                     + "email VARCHAR(40))");
 34:             // insert four records into the new table
 35:             result = st.executeUpdate(
 36:                 "INSERT INTO contacts (name, address1, address2, "
 37:                     + "phone, email) VALUES("
 38:                     + "'Jimmy Carter', "
 39:                     + "'Carter Presidential Center', "
 40:                     + "'1 Copenhill, Atlanta, GA 30307', "
 41:                     + "'(404) 727-7611',"
 42:                     + "'[email protected]')");
 43:             result = st.executeUpdate(
 44:                 "INSERT INTO contacts (name, address1, address2, "
 45:                     + "phone, email) VALUES("
 46:                     + "'George Bush',"
 47:                     + "'Box 79798',"
 48:                     + "'Houston, TX 77279', "
 49:                     + "'(409) 260-9552',"
 50:                     + "'[email protected]')");
 51:             result = st.executeUpdate(
 52:                 "INSERT INTO contacts (name, address1, address2, "
 53:                     + "phone, email) VALUES("
 54:                     + "'Bill Clinton',"
 55:                     + "'15 Old House Lane', "
 56:                     + "'Chappaqua, NY 10514', "
 57:                     + "'(501) 370-8000', "
 58:                     + "'[email protected]')");
 59:             result = st.executeUpdate(
 60:                 "INSERT INTO contacts (name, address1, address2, "
 61:                     + "phone, email) VALUES("
 62:                     + "'George W. Bush', "
 63:                     + "'White House, 1600 Pennsylvania Ave.',"
 64:                     + "'Washington, DC 20500',"
 65:                     + "'(202) 456-1414', "
 66:                     + "'[email protected]')");
 67:             st.close();
 68:             System.out.println("Database created in " + system);
 69:        }  catch (Exception e) {
 70:             System.out.println("Error - " + e.toString());
 71:        }
 72:    }
 73:
 74:    public void readDatabase() {
 75:        String data = "jdbc:derby:presidents";
 76:        try {
 77:            // load the driver and connect to the database
 78:            Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
 79:            Connection conn = DriverManager.getConnection(
 80:                data, "", "");
 81:            // load all records from the contacts table
 82:            Statement st = conn.createStatement();
 83:            ResultSet rec = st.executeQuery(
 84:                "SELECT * FROM contacts ORDER BY name");
 85:            // loop through each record and display its fields
 86:            while(rec.next()) {
 87:                System.out.println(rec.getString("name") +  "
"
 88:                    + rec.getString("address1") + "
"
 89:                    + rec.getString("address2") + "
"
 90:                    + rec.getString("phone") + "
"
 91:                    + rec.getString("email") + "
");
 92:            }
 93:            st.close();
 94:        } catch (Exception e) {
 95:            System.out.println("Error - " + e.toString());
 96:        }
 97:     }
 98:
 99:     public static void main(String[] arguments) {
100:         Presidents prez = new Presidents();
101:         if (arguments.length < 1) {
102:             System.out.println("Usage: java Presidents [create|read]");
103:             System.exit(-1);
104:         }
105:         if (arguments[0].equals("create")) {
106:             prez.createDatabase();
107:         }
108:         if (arguments[0].equals("read")) {
109:             prez.readDatabase();
110:         }
111:     }
112: }

Using this application with another database and driver would require changes to lines 16, 19, 75, and 77.

Java DB requires a system property, derby.system.home, to be set to the location of the root folder where its databases are located. If this folder does not exist, Java DB will create it.

The Java DB JDBC driver can be loaded with the following statement:

Class.forName("org.apache.derby.jdbc.EmbeddedDriver");

The Presidents application is split into the createDatabase() and readDatabase() methods whose functions are self-explanatory.

Database creation employs the following database connection string for the DriverManager.getConnection(String) method in line 21:

jdbc:derby:presidents;create=true

This string follows the form “jdbc:derby:” followed by the database name, a semicolon, and the parameter “create=true”, which causes the database to be created if necessary.

This string can include user and password parameters for a database that requires logon:

jdbc:derby:presidents;user=dbuser;password=tortuga;create=true

Making a connection to read from the database in line 79 is simpler:

jdbc:derby:presidents

After you’ve made a successful connection to Java DB, reading and writing database records over JDBC follows the same process employed earlier today with JDBC-ODBC. SQL statements are written to create a database table, insert records into the table, and read those records.

The SQL employed by Java DB has different record types than Access, MySQL, and other databases.

Run the Presidents application the first time with “create” as the only argument to create the new database:

java Presidents create

If it is successful, the application outputs a message like the following:

Database created in C:Documents and SettingsRogers.database

Run the application again with “read” as the argument to read and display the contents of the database:

java Presidents read

The application produces the following output:

Bill Clinton
15 Old House Lane
Chappaqua, NY 10514
(501) 370-8000
[email protected]

George Bush
Box 79798
Houston, TX 77279
(409) 260-9552
[email protected]

George W. Bush
White House, 1600 Pennsylvania Ave.
Washington, DC 20500
(202) 456-1414
[email protected]

Jimmy Carter
Carter Presidential Center
1 Copenhill, Atlanta, GA 30307
(404) 727-7611
[email protected]

The presence of Java DB is one of the most noteworthy improvements in Java 6. The availability of a relational database on all Java-equipped computers gives programmers a chance to take advantage of persistent data storage.

For more information on Java DB, visit the Sun Microsystems website at http://developers.sun.com/prodtech/javadb.

Summary

Today you learned about working with data stored in popular database formats such as Microsoft Access and Java DB. Using either Java Database Connectivity (JDBC) or a combination of JDBC and ODBC, you can incorporate existing data-storage solutions into your Java programs.

You can connect to several different relational databases in your Java programs by using JDBC or ODBC and Structured Query Language (SQL), a standard language for reading, writing, and managing a database.

Q&A

Q

Can the JDBC-ODBC bridge driver be used in an applet?

A

The default security in place for applets does not allow the JDBC-ODBC bridge to be used because the ODBC side of the bridge driver employs native code rather than Java. Native code can’t be held to the security restrictions in place for Java, so there’s no way to ensure that this code is secure.

JDBC drivers that are implemented entirely in Java can be used in applets, and they have the advantage of requiring no configuration on the client computer.

Q

What’s the difference between Java DB and more well-known databases such as Access and MySQL? Which should I use?

A

Java DB is intended for database applications that have simpler needs than Access and comparable databases. The entire application takes up 2MB of space, making it easy to bundle with Java applications that require database connectivity.

Sun employs Java DB in several parts of the Java Enterprise Edition, which demonstrates that it’s capable of delivering strong, reliable performance on important tasks.

Quiz

Review today’s material by taking this three-question quiz.

Questions

1.

What does a Statement object represent in a database program?

  1. A connection to a database

  2. A database query written in Structured Query Language

  3. A data source

2.

Which Java class represents SQL statements that are compiled before they are executed?

  1. Statement

  2. PreparedStatement

  3. ResultSet

3.

What does the Class.forName(String) method accomplish?

  1. It provides the name of a class.

  2. It loads a database driver that can be used to access a database.

  3. It deletes an object.

Answers

1.

b. The class, part of the java.sql package, represents an SQL statement.

2.

b. Because it is compiled, PreparedStatement is a better choice when you’re going to execute the same SQL query numerous times.

3.

b. This static method loads a database driver.

Certification Practice

The following question is the kind of thing you could expect to be asked on a Java programming certification test. Answer it without looking at today’s material or using the Java compiler to test the code.

Given:

public class ArrayClass {

    public static ArrayClass newInstance() {
        count++;
        return new ArrayClass();
    }

    public static void main(String arguments[]) {
        new ArrayClass();
    }

    int count = -1;
}

Which line in this program prevents it from compiling successfully?

  1. count++;

  2. return new ArrayClass();

  3. public static void main(String arguments[]) {

  4. int count = -1;

The answer is available on the book’s website at http://www.java21days.com. Visit the Day 18 page and click the Certification Practice link.

Exercises

To extend your knowledge of the subjects covered today, try the following exercises:

  1. Modify the CoalReporter application to pull fields from the Country Oil Totals table instead of the Coal table.

  2. Write an application that stores Yahoo! stock quotes in a Java DB database.

Where applicable, exercise solutions are offered on the book’s website at http://www.java21days.com.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset