Chapter 17. Working with Databases

Welcome to Day 17! Today you're going to get more of the inside story on working with databases. You'll learn how to look up data on the server for the user, how to navigate through a database, and more in-depth topics. Here's an overview of today's work:

  • Supporting user data lookup using databases

  • Navigating through a database

  • Getting information about a result set

  • Joining database tables

  • Creating and filling database tables

Today you're going to see some advanced topics, including joining database tables and extracting data from them, as well as creating your own database tables. Yesterday was more discussion-intensive to build your JDBC foundation—today's work is code-intensive, as you learn how to put all this JDBC expertise to work.

Data Lookup

For example, suppose you want to let the user enter values that you will look up in a database. The pubs database you saw yesterday contains a Publishers table that lists publishers, and one of the fields in the Publishers table is pub_id. What if the user wanted to look up a publisher by ID? You can see an HTML page in Listing 17.1 that enables the user to specify what publisher to look up by ID value.

Example 17.1. Implementing Database Lookup (ch17_01.html)

<HTML>
    <HEAD>
        <TITLE>Database Lookup</TITLE>
    </HEAD>

    <BODY>
        <H1>Database Lookup</H1>
        <FORM ACTION="ch17_02.jsp" METHOD="POST">
            Please enter the ID of the publisher you want to find:
            <BR>
            <INPUT TYPE="TEXT" NAME="id">
            <BR>
            <INPUT TYPE="SUBMIT" value="Submit">
        </FORM>
    </BODY>
<HTML>

You can see this HTML page at work in Figure 17.1, where the user has entered the ID of a publisher and what she wants to look up.

Accessing publisher by ID.

Figure 17.1. Accessing publisher by ID.

When the user clicks the Submit button, the ID value is sent to a new JSP page, ch17_02.jsp, which will use that value in a SQL statement to look up the matching publisher. You can see this JSP page in Listing 17.2, but the meat is right here in this code:

String id = request.getParameter("id"); 
        .
        .
        .
ResultSet resultset =
    statement.executeQuery("select * from Publishers where pub_id = '" + id + "'") ;

As you can see, the idea is just to search the database for the right record using SQL.

Example 17.2. Writing to a Log File (ch17_02.jsp)

<%@ page import="java.sql.*" %>
<% Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); %>

<HTML>
    <HEAD>
        <TITLE>Fetching Data From a Database</TITLE>
    </HEAD>

    <BODY>
        <H1>Fetching Data From a Database</H1>

        <%
            Connection connection = DriverManager.getConnection(
                "jdbc:odbc:data", "Steve", "password");

            Statement statement = connection.createStatement();

            String id = request.getParameter("id");

            ResultSet resultset =
                statement.executeQuery("select * from Publishers where pub_id = '" + id +
Writing to a Log File (ch17_02.jsp) "'") ;

            if(!resultset.next()) {
                out.println("Sorry, could not find that publisher. " +
                "Please <A HREF='ch16_04.html'>try again</A>.");
            } else {
        %>

        <TABLE BORDER="1">
            <TR>
               <TH>ID</TH>
               <TH>Name</TH>
               <TH>City</TH>
               <TH>State</TH>
               <TH>Country</TH>
           </TR>
           <TR>
               <TD> <%= resultset.getString(1) %> </TD>
               <TD> <%= resultset.getString(2) %> </TD>
               <TD> <%= resultset.getString(3) %> </TD>
               <TD> <%= resultset.getString(4) %> </TD>
               <TD> <%= resultset.getString(5) %> </TD>
           </TR>
       </TABLE>
       <BR>
       <A HREF='ch16_04.html'>Search again</A>.
       <%
           }
       %>
    </BODY>
</HTML>

You can see the results in Figure 17.2, where the user has found a particular publisher, whose data is displayed. As you can see, searching a database isn't hard when you use SQL and ResultSet objects.

Finding a publisher in a database.

Figure 17.2. Finding a publisher in a database.

Now it's time to take a look at the methods of ResultSet objects directly. These objects let you navigate through the records they contain, as well as perform many other operations, and knowing how to use them is basic to JDBC.

The ResultSet Methods

Objects of the ResultSet interface are at the very center of JDBC; you can see the fields of the java.sql.ResultSet interface in Table 17.1 and the methods of this interface in Table 17.2. Table 17.2 is long, but it's worth going through in some detail—there is a terrific amount of functionality in this interface, such as the deleteRow method, which deletes an entire row; the updateRow method, which updates the contents of a row; or the absolute method, which makes a given row the current record.

Tip

A Blob as referred to in Table 17.2 is a binary large object, which enables you to work with binary data. A Clob in the same table is a character large object, which enables you to store character data.

Table 17.1. Fields of the ResultSet Interface

Field

Means This

static int CLOSE_CURSORS_AT_COMMIT

A constant that specifies that ResultSet objects should be closed when committed.

static int CONCUR_READ_ONLY

A constant that specifies the concurrency mode for a ResultSet object that may not be updated.

static int CONCUR_UPDATABLE

A constant that specifies the concurrency mode for a ResultSet object that may be updated.

static int FETCH_FORWARD

A constant that specifies that the rows of a result set will be processed in a forward direction.

static int FETCH_REVERSE

A constant that specifies that the rows of a result set will be processed in a reversed direction.

static int FETCH_UNKNOWN

A constant that specifies that the order of the rows in a result set will be processed is unknown.

static int HOLD_CURSORS_OVER_COMMIT

A constant that specifies that ResultSet objects should not be closed when committed.

static int TYPE_FORWARD_ONLY

A constant that specifies the type for a ResultSet object whose cursor is forward-only.

static int TYPE_SCROLL_INSENSITIVE

A constant that specifies a ResultSet object that is scrollable but not sensitive to changes made by others.

static int TYPE_SCROLL_SENSITIVE

A constant that specifies a ResultSet object that is scrollable and sensitive to changes made by others.

Table 17.2. Methods of the ResultSet Interface

Method

Does This

void deregisterDriver(Driver driver)

Removes a driver from the DriverManager's internal driver list.

boolean absolute(int row)

Moves the cursor to the given row number in this ResultSet object.

void afterLast()

Moves the cursor to the end of this ResultSet object (note that the “end” here means just after the last row).

void beforeFirst()

Moves the cursor to the beginning of the ResultSet object (note that the “beginning” here means just before the first row).

void cancelRowUpdates()

Cancels the updates made to the current record.

void clearWarnings()

Clears all warnings.

void close()

Releases the ResultSet object's database and JDBC resources immediately.

void deleteRow()

Deletes the current record from the ResultSet object and the database.

int findColumn(String columnName)

Matches the given ResultSet column name to its ResultSet column index.

boolean first()

Moves the cursor to the first row in the ResultSet object.

Array getArray(int i)

Returns the value of the specified column in the current record as an Array object.

Array getArray(String colName)

Returns the value of the specified column in the current record as an Array object.

InputStream getAsciiStream(int columnIndex)

Returns the value of the specified column in the current record as a stream of ASCII characters.

InputStream getAsciiStream(String columnName)

Returns the value of the specified column in the current record as a stream of ASCII characters.

BigDecimal getBigDecimal(int columnIndex)

Returns the value of the specified column in the current record as a java.math.BigDecimal with full precision.

BigDecimal getBigDecimal(String columnName)

Returns the value of the specified column in the current record as a java.math.BigDecimal with full precision.

InputStream getBinaryStream(int columnIndex)

Returns the value of the specified column in the current record as a binary stream of uninterpreted bytes.

InputStream getBinaryStream(String columnName)

Returns the value of the specified column in the current record as a stream of uninterpreted bytes.

Blob getBlob(int i)

Returns the value of the specified column in the current record as a Blob object.

Blob getBlob(String colName)

Returns the value of the specified column in the current record as a Blob object.

boolean getBoolean(int columnIndex)

Returns the value of the specified column in the current record as a boolean.

boolean getBoolean(String columnName)

Returns the value of the specified column in the current record as a boolean.

byte getByte(int columnIndex)

Returns the value of the specified column in the current record as a byte.

byte getByte(String columnName)

Returns the value of the specified column in the current record as a byte.

byte[] getBytes(int columnIndex)

Returns the value of the specified column in the current record as a byte array.

byte[] getBytes(String columnName)

Returns the value of the specified column in the current record as a byte array.

Reader getCharacterStream(int columnIndex)

Returns the value of the specified column in the current record as a java.io.Reader object.

Reader getCharacterStream(String columnName)

Returns the value of the specified column in the current record as a java.io.Reader object.

Clob getClob(int i)

Returns the value of the specified column in the current record as a Clob object.

Clob getClob(String colName)

Returns the value of the specified column in the current record as a Clob object.

int getConcurrency()

Returns the concurrency mode of this ResultSet object.

String getCursorName()

Returns the name of the SQL cursor used by this ResultSet object.

Date getDate(int columnIndex)

Returns the value of the specified column in the current record as a java.sql.Date object.

Date getDate(int columnIndex, Calendar cal)

Returns the value of the specified column in the current record as a java.sql.Date object.

Date getDate(String columnName)

Returns the value of the specified column in the current record as a java.sql.Date object.

Date getDate(String columnName, Calendar cal)

Returns the value of the specified column in the current record as a java.sql.Date object.

double getDouble(int columnIndex)

Returns the value of the specified column in the current record as a double.

double getDouble(String columnName)

Returns the value of the specified column in the current record as a double.

int getFetchDirection()

Returns the fetch direction for this ResultSet object.

int getFetchSize()

Returns the fetch size for this ResultSet object.

float getFloat(int columnIndex)

Returns the value of the specified column in the current record as a float.

float getFloat(String columnName)

Returns the value of the specified column in the current record as a float.

int getInt(int columnIndex)

Returns the value of the specified column in the current record as an int.

int getInt(String columnName)

Returns the value of the specified column in the current record as an int.

long getLong(int columnIndex)

Returns the value of the specified column in the current record as a long.

long getLong(String columnName)

Returns the value of the specified column in the current record as a long.

ResultSetMetaData getMetaData()

Returns the ResultSet object's metadata.

Object getObject(int columnIndex)

Gets the value of the specified column in the current record as an Object.

Object getObject(int i, Map map)

Returns the value of the specified column in the current record as an Object.

Object getObject(String columnName)

Returns the value of the specified column in the current record as an Object.

Object getObject(String colName, Map map)

Returns the value of the specified column in the current record as an Object.

Ref getRef(int i)

Returns the value of the specified column in the current record as a Ref object.

Ref getRef(String colName)

Returns the value of the specified column in the current record as a Ref object.

int getRow()

Returns the current record number.

short getShort(int columnIndex)

Returns the value of the specified column in the current record as a short.

short getShort(String columnName)

Returns the value of the specified column in the current record as a short.

Statement getStatement()

Returns the Statement object that created this ResultSet object.

String getString(int columnIndex)

Returns the value of the specified column in the current record as a String.

String getString(String columnName)

Returns the value of the specified column in the current record as a String.

Time getTime(int columnIndex)

Returns the value of the specified column in the current record as a java.sql.Time object.

Time getTime(int columnIndex, Calendar cal)

Returns the value of the specified column in the current record as a java.sql.Time object.

Time getTime(String columnName)

Returns the value of the specified column in the current record as a java.sql.Time object.

Time getTime(String columnName, Calendar cal)

Returns the value of the specified column in the current record as a java.sql.Time object.

Timestamp getTimestamp(int columnIndex)

Returns the value of the specified column in the current record as a java.sql.Timestamp object.

Timestamp getTimestamp(int columnIndex, Calendar cal)

Returns the value of the specified column in the current record as a java.sql.Timestamp object.

Timestamp getTimestamp(String columnName)

Returns the value of the specified column in the current record as a java.sql.Timestamp object.

Timestamp getTimestamp(String columnName, Calendar cal)

Returns the value of the specified column in the current record as a java.sql.Timestamp object.

int getType()

Returns the type of this ResultSet object.

URL getURL(int columnIndex)

Returns the value of the specified column in the current record as a java.net.URL object.

URL getURL(String columnName)

Returns the value of the specified column in the current record as a java.net.URL object.

SQLWarning getWarnings()

Returns the first warning issued by calls using this ResultSet object.

void insertRow()

Inserts the contents of the insert row into this ResultSet object and into the database.

boolean isAfterLast()

True if the cursor is after the last row.

boolean isBeforeFirst()

True if the cursor is before the first row.

boolean isFirst()

True if the cursor is on the first row.

boolean isLast()

True if the cursor is on the last row.

boolean last()

Moves the cursor to the last row.

void moveToCurrentRow()

Moves the cursor to the remembered cursor position, usually the current record.

void moveToInsertRow()

Moves the cursor to the insert row.

boolean next()

Moves the cursor to the next row from its current position.

boolean previous()

Moves the cursor to the previous row.

void refreshRow()

Refreshes the current record with its most recent value in the database.

boolean relative(int rows)

Moves the cursor a relative number of rows (can be either positive or negative).

boolean rowDeleted()

True if a row has been deleted.

boolean rowInserted()

True if the current record has had an insertion.

boolean rowUpdated()

True if the current record has been updated.

void setFetchDirection(int direction)

Sets the direction that the rows will be processed.

void setFetchSize(int rows)

Sets the number of rows that should be fetched from the database when more rows are needed.

void updateArray(int columnIndex, Array x)

Updates the specified column with a java.sql.Array value.

void updateArray(String columnName, Array x)

Updates the specified column with a java.sql.Array value.

void updateAsciiStream(int columnIndex, InputStream x, int length)

Updates the specified column with an ascii stream value.

void updateAsciiStream(String columnName, InputStream x, int length)

Updates the specified column with an ascii stream value.

void updateBigDecimal(int columnIndex, BigDecimal x)

Updates the specified column with a java.math.BigDecimal value.

void updateBigDecimal(String columnName, BigDecimal x)

Updates the specified column with a java.sql.BigDecimal value.

void updateBinaryStream(int columnIndex, InputStream x, int length)

Updates the specified column with a binary stream value.

void updateBinaryStream(String columnName, InputStream x, int length)

Updates the specified column with a binary stream value.

void updateBlob(int columnIndex, Blob x)

Updates the specified column with a java.sql.Blob value.

void updateBlob(String columnName, Blob x)

Updates the specified column with a java.sql.Blob value.

void updateBoolean(int columnIndex, boolean x)

Updates the specified column with a Boolean value.

void updateBoolean(String columnName, boolean x)

Updates the specified column with a Boolean value.

void updateByte(int columnIndex, byte x)

Updates the specified column with a byte value.

void updateByte(String columnName, byte x)

Updates the specified column with a byte value.

void updateBytes(int columnIndex, byte[] x)

Updates the specified column with a byte array value.

void updateBytes(String columnName, byte[] x)

Updates the specified column with a byte array value.

void updateCharacterStream(int columnIndex, Reader x, int length)

Updates the specified column with a character stream value.

void updateCharacterStream(String columnName, Reader reader, int length)

Updates the specified column with a character stream value.

void updateClob(int columnIndex, Clob x)

Updates the specified column with a java.sql.Clob value.

void updateClob(String columnName, Clob x)

Updates the specified column with a java.sql.Clob value.

void updateDate(int columnIndex, Date x)

Updates the specified column with a java.sql.Date value.

void updateDate(String columnName, Date x)

Updates the specified column with a java.sql.Date value.

void updateDouble(int columnIndex, double x)

Updates the specified column with a double value.

void updateDouble(String columnName, double x)

Updates the specified column with a double value.

void updateFloat(int columnIndex, float x)

Updates the specified column with a float value.

void updateFloat(String columnName, float x)

Updates the specified column with a float value.

void updateInt(int columnIndex, int x)

Updates the specified column with an int value.

void updateInt(String columnName, int x)

Updates the specified column with an int value.

void updateLong(int columnIndex, long x)

Updates the specified column with a long value.

void updateLong(String columnName, long x)

Updates the specified column with a long value.

void updateNull(int columnIndex)

Gives a nullable column a null value.

void updateNull(String columnName)

Updates the specified column with a null value.

void updateObject(int columnIndex, Object x)

Updates the specified column with an Object value.

void updateObject(int columnIndex, Object x, int scale)

Updates the specified column with an Object value.

void updateObject(String columnName, Object x)

Updates the specified column with an Object value.

void updateObject(String columnName, Object x, int scale)

Updates the specified column with an Object value.

void updateRef(int columnIndex, Ref x)

Updates the specified column with a java.sql.Ref value.

void updateRef(String columnName, Ref x)

Updates the specified column with a java.sql.Ref value.

void updateRow()

Updates the underlying database with the new contents of the current record of this ResultSet object.

void updateShort(int columnIndex, short x)

Updates the specified column with a short value.

void updateShort(String columnName, short x)

Updates the specified column with a short value.

void updateString(int columnIndex, String x)

Updates the specified column with a String value.

void updateString(String columnName, String x)

Updates the specified column with String a value.

void updateTime(int columnIndex, Time x)

Updates the specified column with a java.sql.Time value.

void updateTime(String columnName, Time x)

Updates the specified column with a java.sql.Time value.

void updateTimestamp(int columnIndex, Timestamp x)

Updates the specified column with a java.sql.Timestamp value.

void updateTimestamp(String columnName, Timestamp x)

Updates the specified column with a java.sql.Timestamp value.

boolean wasNull()

True if the last column read returned a value of null.

Note in particular the navigation methods you see in Table 17.2:

  • absolute—. Moves to a record you specify in absolute terms

  • first—. Moves to the first record

  • last—. Moves to the last record

  • next—. Moves to the next record

  • previous—. Moves to the previous record

  • relative—. Moves to a record you specify in relative terms

These navigation methods let you move through a result set record by record. That's one of the fundamental skills of working with result sets, and you'll see more on how to navigate through a result set next.

Navigating in a RecordSet

Suppose you want to let the user move through a table using buttons—the Next button to go to the next record, and the Previous button to move to the previous record. To implement buttons such as these in a Web application, you'll need to keep track of the current record between accesses to your JSP page so you know what record the user is looking at when she clicks the Next or Previous buttons.

You can do that in a number of ways, such as to use a session or hidden controls. This example will use hidden controls, and includes the JavaScript to retrieve and increment the current record number, storing the incremented value in a hidden control named hidden when the user clicks the Next button:

<SCRIPT LANGUAGE="JavaScript"> 
    <!--
        function moveNext()
        {
            var counter = 0
            counter = parseInt(document.form1.hidden.value) + 1
            document.form1.hidden.value = counter
            form1.submit()
        }
    // -->
</SCRIPT>

In your JSP code, you can get the value in the hidden control easily enough:

int current = 1; 
if(request.getParameter("hidden") != null) {
    current = Integer.parseInt(request.getParameter("hidden"));
}
    .
    .
    .
Navigating in a RecordSet
int current = 1; 
if(request.getParameter("hidden") != null) {
    current = Integer.parseInt(request.getParameter("hidden"));
}
    .
    .
    .
for(int loopIndex = 0; loopIndex <= current; loopIndex++){
    resultset.next();
}

You can see the resulting code in Listing 17.3, which implements a Next button that enables the user to move from record to record forward-only in a table.

Example 17.3. Navigating Forward-Only in a Database Table (ch17_03.jsp)

<%@ page import="java.sql.*" %>
<% Class.forName("sun.jdbc.odbc.JdbcOdbcDriver") ; %>

<HTML>
    <HEAD>
        <TITLE>Navigating in a Database Table </TITLE>
    </HEAD>

    <BODY>
        <H1>Navigating in a Database Table </H1>
        <FORM NAME="form1" ACTION="ch17_03.jsp" METHOD="POST">

        <%
            int current = 1;
            if(request.getParameter("hidden") != null) {
                current = Integer.parseInt(request.getParameter("hidden"));
            }

            Connection connection = DriverManager.getConnection(
                "jdbc:odbc:data", "Steve", "password");

            Statement statement = connection.createStatement();
            ResultSet resultset =
                statement.executeQuery("select * from Publishers");

            for(int loopIndex = 0; loopIndex <= current; loopIndex++){
                resultset.next();
            }
        %>

        <TABLE BORDER="1">
            <TR>
               <TH>ID</TH>
               <TH>Name</TH>
               <TH>City</TH>
               <TH>State</TH>
               <TH>Country</TH>
            </TR>
            <TR>
                <TD> <%= resultset.getString(1) %> </TD>
                <TD> <%= resultset.getString(2) %> </TD>
                <TD> <%= resultset.getString(3) %> </TD>
                <TD> <%= resultset.getString(4) %> </TD>
                <TD> <%= resultset.getString(5) %> </TD>
            </TR>
        </TABLE>
        <BR>
        <INPUT TYPE="HIDDEN" NAME="hidden" VALUE="<%= current %>">
        <INPUT TYPE="BUTTON" VALUE="Next Record" ONCLICK="moveNext()">
    </FORM>

    <SCRIPT LANGUAGE="JavaScript">
        <!--
            function moveNext()
            {
                var counter = 0
                counter = parseInt(document.form1.hidden.value) + 1
                document.form1.hidden.value = counter
                form1.submit()
            }
        // -->
    </SCRIPT>
</HTML>

You can see the results in Figure 17.3. Obviously, using a forward-only cursor is not satisfactory for the original task, which was to let the user move both forward and backward in the table. So what can you do? You can use a different type of cursor.

Navigating in a forward-only way.

Figure 17.3. Navigating in a forward-only way.

Setting Cursor Type

You can set the type of database cursor you want to use when you create the SQL statement object you will use to create the ResultSet object. Here are the types of cursors you can use (not all JDBC drivers will support all cursors):

  • TYPE_FORWARD_ONLY—. Creates a ResultSet object whose cursor may move forward-only.

  • TYPE_SCROLL_INSENSITIVE—. Creates a ResultSet object that is moveable at will but not sensitive to changes made by others that might be working with the database simultaneously.

  • TYPE_SCROLL_SENSITIVE—. Creates a ResultSet object that is moveable at will and sensitive to changes made by others who might be working with the database simultaneously.

TYPE_SCROLL_SENSITIVE—
  • CONCUR_READ_ONLY—. The concurrency mode for a ResultSet object that may not be updated.

  • CONCUR_UPDATABLE—. The concurrency mode for a ResultSet object that may be updated.

To create a result set with a cursor that you can use to move around at will, you can use code such as this when creating the Statement object you'll use to create the result set itself:

Statement statement = connection.createStatement( 
    ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_READ_ONLY);

Now you can move wherever you want in the result set. For example, to find out how many records there are in a result set, you can move to the last record and use the getRow method to determine the total number of records:

resultset.last(); 
int rows = resultset.getRow();

You can also use the absolute method to move to any record. Use the following code to move to the record specified by the user, which is in the variable named current:

resultset.last(); 
int rows = resultset.getRow();

if(current <= rows){
    resultset.absolute(current);
}

You can see an example that implements both a Next and Previous button using this new cursor and code in Listing 17.4.

Example 17.4. Navigating in a Database Table (ch17_04.jsp)

<%@ page import="java.sql.*" %>
<% Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); %>

<HTML>
    <HEAD>
        <TITLE>Navigating in a Database Table </TITLE>
    </HEAD>

    <BODY>
        <H1>Navigating in a Database Table</H1>
        <FORM NAME="form1" ACTION="ch17_04.jsp" METHOD="POST">

        <%
            int current = 1;
            if(request.getParameter("hidden") != null) {
                current = Integer.parseInt(request.getParameter("hidden"));
            }

            Connection connection = DriverManager.getConnection(
                "jdbc:odbc:data", "Steve", "password");

            Statement statement = connection.createStatement(
                ResultSet.TYPE_SCROLL_INSENSITIVE,
                ResultSet.CONCUR_READ_ONLY);

            ResultSet resultset =
                statement.executeQuery("select * from Publishers");

            if(current < 1){
                current = 1;
            }

            resultset.last();
            int rows = resultset.getRow();
            if(current <= rows){
                resultset.absolute(current);
            }
        %>

            <TABLE BORDER="1">
                <TR>
                    <TH>ID</TH>
                    <TH>Name</TH>
                    <TH>City</TH>
                    <TH>State</TH>
                    <TH>Country</TH>
                </TR>
                <TR>
                    <TD> <%= resultset.getString(1) %> </TD>
                    <TD> <%= resultset.getString(2) %> </TD>
                    <TD> <%= resultset.getString(3) %> </TD>
                    <TD> <%= resultset.getString(4) %> </TD>
                    <TD> <%= resultset.getString(5) %> </TD>
                </TR>
            </TABLE>
            <BR>
            <INPUT TYPE="HIDDEN" NAME="hidden" VALUE="<%= current %>">
            <INPUT TYPE="BUTTON" VALUE="Next Record" ONCLICK="moveNext()">
            <INPUT TYPE="BUTTON" VALUE="Previous Record" ONCLICK="movePrevious()">
        </FORM>

        <SCRIPT LANGUAGE="JavaScript">
            <!--
            function moveNext()
            {
                var counter = 0
                counter = parseInt(document.form1.hidden.value) + 1
                document.form1.hidden.value = counter
                form1.submit()
            }
            function movePrevious()
            {
                var counter = 0
                counter = parseInt(document.form1.hidden.value) - 1
                document.form1.hidden.value = counter
                form1.submit()
            }
            // -->
        </SCRIPT>
    </BODY>
</HTML>

You can see this new JSP page at work in Figure 17.4, where the user can move from record to record using the Next and Previous buttons.

Navigating forward and backward in a table.

Figure 17.4. Navigating forward and backward in a table.

Getting Information About a Result Set

Getting Information About a Result Set

A result set's metadata contains a great deal of information about the result set itself, and to get that metadata, you use the result set's getMetaData method. This method returns an object that implements the java.sql.ResultSetMetaData interface. You can see the fields of the ResultSetMetaData interface in Table 17.3, and its methods in Table 17.4.

Table 17.3. Fields of the java.sql.ResultSetMetaData Interface

Field

Means This

static int columnNoNulls

A constant that specifies that a particular column does not allow null values.

static int columnNullable

A constant that specifies that a particular column enables null values.

static int columnNullableUnknown

A constant that specifies that whether a column allows null values is unknown.

Table 17.4. Methods of the java.sql.ResultSetMetaData Interface

Field

Means This

String getCatalogName(int column)

Gets the specified column's catalog name.

String getColumnClassName(int column)

Returns the name of the Java class whose objects populate the current column.

int getColumnCount()

Returns the number of columns in this ResultSet object.

int getColumnDisplaySize(int column)

Indicates the specified column's maximum width in characters.

String getColumnLabel(int column)

Gets the specified column's title for display use (if such a title exists).

String getColumnName(int column)

Gets the specified column's name.

int getColumnType(int column)

Returns the specified column's SQL type.

String getColumnTypeName(int column)

Returns the specified column's type name.

int getPrecision(int column)

Gets the specified column's number of decimal digits.

int getScale(int column)

Gets the column's number of digits to the right of the decimal point.

String getSchemaName(int column)

Gets the column's database schema.

String getTableName(int column)

Gets the column's table name.

boolean isAutoIncrement(int column)

True if the column is automatically numbered.

boolean isCaseSensitive(int column)

True if a column's case matters.

boolean isCurrency(int column)

True if the column is a currency value.

boolean isDefinitelyWritable

True if a write operation on the column will succeed. (int column)

int isNullable(int column)

True if you can store NULL values in the column.

boolean isReadOnly(int column)

True if the column may not be written to.

boolean isSearchable(int column)

True if the column can be used in a SQL WHERE clause.

boolean isSigned(int column)

True if the values in the column are signed numbers.

boolean isWritable(int column)

True if a write operation in a column can succeed.

For example, what if you wanted to determine the name of the fields in a table on the fly? You can do that with the metadata's getColumnName method, as you see in Listing 17.5, which extracts the data from the Publishers table and displays each column of data with the actual name of the column.

Example 17.5. Writing to a Log File (ch17_05.jsp)

<%@ page import="java.sql.*" %>
<% Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); %>

<HTML>
    <HEAD>
        <TITLE>Using Table Metadata</TITLE>
    </HEAD>

    <BODY>
        <H1>Using Table Metadata</H1>

        <%
            Connection connection = DriverManager.getConnection(
                "jdbc:odbc:data", "Steve", "password");

            Statement statement = connection.createStatement() ;
            ResultSet resultset =
                statement.executeQuery("select * from Publishers") ;
        %>

        <TABLE BORDER="1">
            <TR>
                <TH><%= resultset.getMetaData().getColumnName(1)%></TH>
                <TH><%= resultset.getMetaData().getColumnName(2)%></TH>
                <TH><%= resultset.getMetaData().getColumnName(3)%></TH>
                <TH><%= resultset.getMetaData().getColumnName(4)%></TH>
                <TH><%= resultset.getMetaData().getColumnName(5)%></TH>
            </TR>
            <% while(resultset.next()){ %>
            <TR>
                <TD> <%= resultset.getString(1) %></td>
                <TD> <%= resultset.getString(2) %></TD>
                <TD> <%= resultset.getString(3) %></TD>
                <TD> <%= resultset.getString(4) %></TD>
                <TD> <%= resultset.getString(5) %></TD>
            </TR>
            <% } %>
        </TABLE>
    </BODY>
</HTML>

You can see the results in Figure 17.5, where the actual names of each of the columns are displayed at the top of each column.

Getting the names of columns from a database.

Figure 17.5. Getting the names of columns from a database.

Joining Tables

You can also relate the data of two tables together in a result set. In SQL, you can do inner joins, where records must be in both tables, or outer joins, where records can be in either table. For example, the pubs example database contains a table named authors that stores information about authors, and a table named titleauthor that lists the books each author has written. However, the titleauthor table doesn't list any information about the authors themselves, just an ID value. How can you find, say, the books that each author has written, and display that information with the author's first and last names? It turns out that the authors table also gives each author's ID, so you can tie the two tables together using the ID field.

In this case, you can create a new result set containing each author's first name and last name from the authors table and the ID of the books the author has written from the titleauthor table using this SQL, which creates an inner join between those tables based on the ID field in each table:

String query = "SELECT authors.au_fname, authors.au_lname, titleauthor. title_id " + 
    "FROM authors INNER JOIN titleauthor ON authors.au_id = titleauthor. au_id";

You can see the code that implements this SQL in Listing 17.6.

Example 17.6. Joining Tables (ch17_06.jsp)

<%@ page import="java.sql.*" %>
<% Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); %>

<HTML>
    <HEAD>
        <TITLE>Joining Tables</TITLE>
    </HEAD>

    <BODY>
        <H1>Joining Tables</H1>

        <%
            Connection connection = DriverManager.getConnection(
                "jdbc:odbc:data", "Steve", "password");

            Statement statement = connection.createStatement();
            String query = "SELECT authors.au_fname, authors.au_lname, titleauthor
Joining Tables (ch17_06.jsp).title_id " +
              "FROM authors INNER JOIN titleauthor ON authors.au_id = titleauthor.au_id";

            ResultSet resultset =
                statement.executeQuery(query) ;
        %>

        <TABLE BORDER="1">
            <TR>
                <TH>First Name</TH>
                <TH>Last Name</TH>
                <TH>Book ID</TH>
            </TR>
            <% while(resultset.next()){ %>
            <TR>
                <TD> <%= resultset.getString(1) %></TD>
                <TD> <%= resultset.getString(2) %></TD>
                <TD> <%= resultset.getString(3) %></TD>
            </TR>
            <% } %>
        </TABLE>
    </BODY>
</HTML>

Figure 17.6 shows the results of this code. In that figure, you can see the data from the two tables were joined. In particular, notice the data for Marjorie Green in the figure—this author has two book IDs listed, because she's written two books, and both were put into the result set when the two tables were joined.

Logging user accesses in JSP.

Figure 17.6. Logging user accesses in JSP.

Creating and Filling Tables

Although most JSP work with databases involves reading data from pre-existing database tables and displaying it, you can also create your own database tables on the fly. In this next example, you'll see how to create a table in a database from JSP.

For example, you can add a new table named Employees to the pubs database easily—all you have to do is use the SQL CREATE TABLE command. When you create a new table, you give its name and pass the fields in parentheses, as well as their type. This example will create an integer field named ID, and a character field 50 characters in length called Name:

Statement statement = connection.createStatement(); 
String command = "CREATE TABLE Employees (ID INTEGER, Name CHAR(50));";
statement.executeUpdate(command);

Note that here you must use the executeUpdate method, because you're changing the database. You can see this code at work in Listing 17.7.

Example 17.7. Creating a Database Table (ch17_07.jsp)

<%@ page import="java.sql.*" %>

<HTML>
    <HEAD>
        <TITLE>Creating a Table</TITLE>
    </HEAD>

    <BODY>
        <H1>Creating a Table</H1>

        <%
            Connection connection = null;
            try {
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver").newInstance();
                connection = DriverManager.getConnection("jdbc:odbc:data", "Steve",
Creating a Database Table (ch17_07.jsp) "password");

                Statement statement = connection.createStatement();
                String command = "CREATE TABLE Employees (ID INTEGER, Name CHAR(50));";
                statement.executeUpdate(command);

            } catch (Exception e) {
                out.println("An error occurred.");
            }
        %>
        The Employees table was created.
    </BODY>
</HTML>

You can see the result of this JSP page in Figure 17.7, where it has created the new Employees table.

Creating a new database table.

Figure 17.7. Creating a new database table.

A database table isn't much use without data, and you can add data to a table with the SQL INSERT command. For example, to give employee Tom Thumb the ID 1 and employee Peter Pan the ID 2, you can use this command:

String command = "INSERT INTO Employees (ID, Name) VALUES (1, 'Tom Thumb')"; 
statement.executeUpdate(command);

command = "INSERT INTO Employees (ID, Name) VALUES (2, 'Peter Pan')";
statement.executeUpdate(command);

You can see this code at work in Listing 17.8, which inserts these two employees into the Employees table and displays the results by retrieving their records from that table.

Example 17.8. Writing to a Database Table (ch17_08.jsp)

<%@ page import="java.sql.*" %>
<HTML>
    <HEAD>
        <TITLE>Filling a Table</TITLE>
    </HEAD>

    <BODY>
        <H1>Filling a Table</H1>

        <%
            Connection connection = null;
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
            connection = DriverManager.getConnection("jdbc:odbc:data", "Steve", "password");

            Statement statement = connection.createStatement();

            String command = "INSERT INTO Employees (ID, Name) VALUES (1, 'Tom Thumb')";
            statement.executeUpdate(command);

            command = "INSERT INTO Employees (ID, Name) VALUES (2, 'Peter Pan')";
            statement.executeUpdate(command);

            ResultSet resultset =
                statement.executeQuery("select * from Employees");

            while(resultset.next()){
        %>
            <TABLE BORDER="1">
                <TR>
                    <TH>ID</TH>
                    <TH>Name</TH>
                </TR>
                <TR>
                    <TD> <%= resultset.getString(1) %> </TD>
                    <TD> <%= resultset.getString(2) %> </TD>
                </TR>
            </TABLE>
        <%
            }
        %>
    </BODY>
</HTML>

Figure 17.8 shows that the two records were successfully added to the new table and then retrieved.

Retrieving new data from a table.

Figure 17.8. Retrieving new data from a table.

Updating Data

What if you want to change the value in a field after a table has been created—and have that change made to the database as well? You can use a SQL UPDATE command such as this, which changes the name Tom Thumb to Thomas Thumb:

String command = "UPDATE Employees SET Name = 'Thomas Thumb' WHERE NAME = 'Tom Thumb'"; 
statement.executeUpdate(command);

If you know the ID of the record to change, you can perform the same operation:

String command = "UPDATE Employees SET Name = 'Thomas Thumb' WHERE ID = 1"; 
statement.executeUpdate(command);

You can also use an update method of the ResultSet object. Just as you can get Strings, doubles, and so forth with the getString and getDouble ResultSet methods, you can update the data in a field easily with ResultSet methods such as updateString and updateDouble, which automatically make the change to the database as well. For example, to change the name Tom Thumb to Thomas Thumb, you can use the following when Tom Thumb's record is the current record:

resultset.updateString(2, "Thomas Thumb"); 

Tip

Yesterday and today provided an introduction to the use of JDBC and SQL in JSP. Note, however, that a number of books have been written on JDBC, and many more books on SQL. There's plenty more material that can't be covered here, such as the idea of transactions, which provide a level of security in working with databases. When you perform operations in a transaction, no changes are made to the database until you call the Connection object's commit method, and if you don't want to commit those changes, you use the Connection object's rollback method. For more information, take a look at a book dedicated to JDBC.

Summary

Today you learned about a number of in-depth database topics. You learned how to implement data lookup using databases, enabling the user to send you data, and how to use that data to look up a record in a database and display it.

You also learned how to navigate through a database, which involves learning how to set the cursor type yourself when you create the Java Statement object so that you can move around at will in a result set.

In addition, you learned how to join database tables to work with two tables at once, relating them through a common field and creating a result set that includes data from both tables.

Finally, you took a look at the process of creating your own tables in a database and filling them with data. You learned how to create a table with the SQL CREATE command, and how to place data into the new table with the SQL INSERT command. Tomorrow, you'll see another way of handling data in JSP: by using XML.

Q&A

Q1:

I know I can use the SQL DELETE command to delete a record, but how can I delete a whole table?

A1:

You can use the SQL DROP command. Here's an example: DROP TABLE Employees.

Q2:

Is it possible to store more than a single item in a single field?

A2:

Yes, in fact, you can store an entire table in the field of a record, creating what's called a hierarchical database. Database systems such as SQL Server support hierarchical data, but that's beyond the scope of this book.

Workshop

This workshop tests whether you understand all the concepts you learned today. It's a good idea to master today's concepts by honing your knowledge here before starting tomorrow's material. You can find the answers to the quiz questions in Appendix A.

Quiz

1:

What is the default cursor type in a ResultSet object?

2:

What method will you use to retrieve a float value from a field in a record in a result set?

3:

When do you set the type of cursor you want a result set to use?

4:

What method can you use to move to any record you want to access in a result set?

5:

You're passed an unknown result set object; how do you find how many columns each record in the result set contains?

Exercises

1:

Add First and Last buttons to the database navigation example you see in Listing 17.4 to let the user move to the first and last records. (You don't need to connect to the pubs database if you don't have SQL Server, of course—any database table will do here.)

2:

Modify the example in Listing 17.4 further, adding a text field to let the user move to any record directly by number.

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

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