AhmadLang / Java, How To Program, 2004
.pdf
48// set query and execute it
49setQuery( query );
50} // end constructor ResultSetTableModel
52// get class that represents column type
53public Class getColumnClass( int column ) throws IllegalStateException
54{
55// ensure database connection is available
56if ( !connectedToDatabase )
57throw new IllegalStateException( "Not Connected to Database" );
59// determine Java class of column
60try
61{
62String className = metaData.getColumnClassName( column + 1 );
64// return Class object that represents className
65return Class.forName( className );
66} // end try
67catch ( Exception exception )
68{
69exception.printStackTrace();
70} // end catch
71
72return Object. class; // if problems occur above, assume type Object
73} // end method getColumnClass
74
75// get number of columns in ResultSet
76public int getColumnCount() throws IllegalStateException
77{
78// ensure database connection is available
79if ( !connectedToDatabase )
80throw new IllegalStateException( "Not Connected to Database" );
82// determine number of columns
83try
84{
85return metaData.getColumnCount();
86} // end try
87catch ( SQLException sqlException )
88{
89sqlException.printStackTrace();
90} // end catch
91 |
|
|
|
92 |
return |
0; // |
if problems occur above, return 0 for number of columns |
93 |
} // end |
method |
getColumnCount |
94 |
|
|
|
95// get name of a particular column in ResultSet
96public String getColumnName( int column ) throws IllegalStateException
97{
98// ensure database connection is available
99if ( !connectedToDatabase )
100throw new IllegalStateException( "Not Connected to Database" );
101
102// determine column name
103try
104{
105return metaData.getColumnName( column + 1 );
106} // end try
107catch ( SQLException sqlException )
108{
109sqlException.printStackTrace();
110} // end catch
111
112return ""; // if problems, return empty string for column name
113} // end method getColumnName
114
115// return number of rows in ResultSet
116public int getRowCount() throws IllegalStateException
117{
118// ensure database connection is available
119if ( !connectedToDatabase )
120throw new IllegalStateException( "Not Connected to Database" );
122return numberOfRows;
123} // end method getRowCount
125// obtain value in particular row and column
126public Object getValueAt( int row, int column )
127throws IllegalStateException
128{
129// ensure database connection is available
130if ( !connectedToDatabase )
131throw new IllegalStateException( "Not Connected to Database" );
133// obtain a value at specified ResultSet row and column
134try
135{
136resultSet.absolute( row + 1 );
137return resultSet.getObject( column + 1 );
138} // end try
139catch ( SQLException sqlException )
140{
141sqlException.printStackTrace();
142} // end catch
143
144return ""; // if problems, return empty string object
145} // end method getValueAt
146
147// set new database query string
148public void setQuery( String query )
149throws SQLException, IllegalStateException
150{
151// ensure database connection is available
152if ( !connectedToDatabase )
153throw new IllegalStateException( "Not Connected to Database" );
155// specify query and execute it
156resultSet = statement.executeQuery(query);
158// obtain meta data for ResultSet
159metaData = resultSet.getMetaData();
161// determine number of rows in ResultSet
162 |
resultSet.last(); |
// |
move |
to |
last row |
163 |
numberOfRows = resultSet.getRow(); |
// |
get |
row |
number |
164 |
|
|
|
|
|
165// notify JTable that model has changed
166fireTableStructureChanged();
167} // end method setQuery
168
169// close Statement and Connection
170public void disconnectFromDatabase()
171{
172if (!connectedToDatabase)
173return;
174
175// close Statement and Connection
176try
177{
178statement.close();
179connection.close();
180} // end try
181catch ( SQLException sqlException )
182{
183sqlException.printStackTrace();
184} // end catch
185finally // update database connection status
186{
187connectedToDatabase = false;
188} // end finally
189} // end method disconnectFromDatabase
190} // end class ResultSetTableModel
Class ResultSetTableModel (Fig. 25.28) extends class AbstractTableModel (package
javax.swing.table), which implements interface TableModel. Class ResultSetTableModel overrides TableModel methods getColumnClass, getColumnCount, getColumnName, getrowCount and getValueAt. The default implementations of TableModel methods isCellEditable and setValueAt (provided by AbstractTableModel) are not overridden, because this example does not support editing the JTable cells. The default implementations of TableModel methods addTableModelListener and removeTableModelListener (provided by AbstractTableModel) are not overridden, because the implementations of these methods in AbstractTableModel properly add and remove event listeners.
The ResultSetTableModel constructor (lines 3050) accepts five String argumentsthe driver class name, the URL of the database, the username, the password and the default query to perform. The constructor throws any
exceptions that occur in its body back to the application that created the ResultSetTableModel object, so that the application can determine how to handle the exception (e.g., report an error and terminate the application). Line 35 loads the database driver. Line 38 establishes a connection to the database. Lines 4143 invoke Connection method createStatement to create a Statement object. This example uses a version of method createStatement that takes two argumentsthe result set type and the result set concurrency. The result set type (Fig. 25.29) specifies whether the ResultSet's cursor is able to scroll in both directions or forward only and whether the ResultSet is sensitive to changes. ResultSets that are sensitive to changes reflect those changes immediately after they are made with methods of interface ResultSet. If a ResultSet is insensitive to changes, the query that produced the ResultSet must be executed again to reflect any changes made. The result set concurrency (Fig. 25.30) specifies whether the ResultSet can be updated with ResultSet's update methods. This example uses a ResultSet that is scrollable, insensitive to changes and read only. Line 49 invokes ResultSetTableModel method setQuery (lines 148167) to perform the default query.
[Page 1217]
[Page 1218]
Portability Tip 25.5
Some JDBC drivers do not support scrollable ResultSets. In such cases, the driver typically returns a ResultSet in which the cursor can move only forward. For more information, see your database driver documentation.
Portability Tip 25.6
Some JDBC drivers do not support updatable ResultSets. In such cases, the driver typically returns a read-only ResultSet. For more information, see your database driver documentation.
Common Programming Error 25.11
Attempting to update a ResultSet when the database driver does not support updatable ResultSets causes SQLExceptions.
Figure 25.29. ResultSet constants for specifying ResultSet type.
ResultSet static type |
|
constant |
Description |
TYPE_FORWARD_ONLY
Specifies that a ResultSet's cursor can move only in the forward direction (i.e., from the first row to the last row in the
ResultSet).
TYPE_SCROLL_INSENSITIVE
Specifies that a ResultSet's cursor can scroll in either direction and that the changes made to the ResultSet during ResultSet processing are not reflected in the ResultSet unless the program queries the database again.
TYPE_SCROLL_SENSITIVE
Specifies that a ResultSet's cursor can scroll in either direction and that the changes made to the ResultSet during ResultSet processing are reflected immediately in the ResultSet.
Figure 25.30. ResultSet constants for specifying result properties.
ResultSet static concurrency |
|
constant |
Description |
|
|
CONCUR_READ_ONLY |
Specifies that a ResultSet cannot be updated (i.e., changes to |
|
the ResultSet contents cannot be reflected in the database with |
|
ResultSet's update methods). |
CONCUR_UPDATABLE |
Specifies that a ResultSet can be updated (i.e., changes to the |
|
ResultSet contents can be reflected in the database with |
|
ResultSet's update methods). |
|
|
[Page 1219]
Common Programming Error 25.12
Attempting to move the cursor backwards through a ResultSet when the database driver does not support backwards scrolling causes a SQLException.
Method getColumnClass (lines 5373) returns a Class object that represents the superclass of all objects in a particular column. The JTable uses this information to configure the default cell renderer and cell editor for that column in the JTable. Line 62 uses ResultSetMetaData method getColumnClassName to obtain the fully qualified class name for the specified column. Line 65 loads the class and returns the corresponding Class object. If an exception occurs, the catch in lines 6770 prints a stack trace and line 72 returns Object.classthe Class instance that represents class Objectas the default type. [Note: Line 62 uses the argument column + 1. Like arrays, JTable row and column numbers are counted from 0. However, ResultSet row and column numbers are counted from 1. Thus, when processing ResultSet rows or columns for use in a JTable, it is necessary to add 1 to the row or column number to manipulate the appropriate ResultSet row or column.]
Method getColumnCount (lines 7693) returns the number of columns in the model's underlying ResultSet. Line 85 uses ResultSetMetaData method getColumnCount to obtain the number of columns in the ResultSet. If an exception occurs, the catch in lines 8790 prints a stack trace and line 92 returns 0 as the default number of columns.
Method getColumnName (lines 96113) returns the name of the column in the model's underlying ResultSet. Line 105 uses ResultSetMetaData method getColumnName to obtain the column name from the ResultSet. If an exception occurs, the catch in lines 107110 prints a stack trace and line 112 returns the empty string as the default column name.
Method getrowCount (lines 116123) returns the number of rows in the model's underlying ResultSet. When method setQuery (lines 148167) performs a query, it stores the number of rows in variable numberOfRows.
Method getValueAt (lines 126145) returns the Object in a particular row and column of the model's underlying ResultSet. Line 136 uses ResultSet method absolute to position the ResultSet cursor at a specific row. Line 137 uses ResultSet method getObject to obtain the Object in a specific column of the current row. If an exception occurs, the catch in lines 139142 prints a stack trace and line 144 returns an empty string as the default value.
Method setQuery (lines 148167) executes the query it receives as an argument to obtain a new ResultSet (line 156). Line 159 gets the ResultSetMetaData for the new ResultSet. Line 162 uses ResultSet method last to position the ResultSet cursor at the last row in the ResultSet. Line 163 uses ResultSet method getrow to obtain the row number for the current row in the ResultSet. Line 166 invokes method
fireTableStructureChanged (inherited from class AbstractTableModel) to notify any JTable using this
ResultSetTableModel object as its model that the structure of the model has changed. This causes the JTable to repopulate its rows and columns with the new ResultSet data. Method setQuery tHRows any exceptions that occur in its body back to the application that invoked setQuery.
Method disconnectFromDatabase (lines 170189) implements an appropriate termination method for class ResultSetTableModel. A class designer should provide a public method that clients of the class must invoke explicitly to free resources that an object has used. In this case, method disconnectFromDatabase closes the database statement and connection (lines 178179), which are considered limited resources. Clients of the ResultSetTableModel class should always invoke this method when the instance of this class is no longer needed. Before releasing resources, line 172 verifies whether the connection is already terminated. If so, the method simply returns. In addition, note that each other method in the class throws an IllegalStateException if the boolean field connectedToDatabase is false. Method disconnectFromDatabase sets connectedToDatabase to false (line 184) to ensure that clients do not use an instance of ResultSetTableModel after that instance has already been terminated. IllegalStateException is an exception from the Java libraries that is appropriate for indicating this error condition.
[Page 1220]
The DisplayQueryResults (Fig. 25.31) constructor (lines 34140) creates a ResultSetTableModel object and the GUI for the application. Lines 2225 and 28 declare the database driver class name, database URL, username, password and default query that are passed to the ResultSetTableModel constructor to make the initial connection to the database and perform the default query. Line 64 creates the JTable object and passes a ResultSetTableModel object to the JTable constructor, which then registers the JTable as a listener for TableModelEvents generated by the ResultSetTableModel. Lines 71110 register an event handler for the submitButton that the user clicks to submit a query to the database. When the user clicks the button, method actionPerformed (lines 76108) invokes ResultSetTableModel method setQuery to execute the new query. If the user's query fails (e.g., because of a syntax error in the user's input), lines 9394 execute the default query. If the default query also fails, there could be a more serious error, so line 103 ensures that the database connection is closed and line 105 exits the program. The screen captures in Fig. 25.31 show the results of two queries. The first screen capture shows the default query that retrieves all the data from table authors of database books. The second screen capture shows a query that selects each author's first name and last name from the authors table and combines that information with the title and edition number from the titles table. Try entering your own queries in the text area and clicking the Submit Query button to execute the query.
[Page 1223]
[Page 1224]
Figure 25.31. DisplayQueryResults for querying database books.
(This item is displayed on pages 1220 - 1223 in the print version)
1 // Fig. 25.31: DisplayQueryResults.java
2 // Display the contents of the Authors table in the
3// Books database.
4import java.awt.BorderLayout;
5import java.awt.event.ActionListener;
6import java.awt.event.ActionEvent;
7import java.awt.event.WindowAdapter;
8import java.awt.event.WindowEvent;
9import java.sql.SQLException;
10import javax.swing.JFrame;
11import javax.swing.JTextArea;
12import javax.swing.JScrollPane;
13import javax.swing.ScrollPaneConstants;
14import javax.swing.JTable;
15import javax.swing.JOptionPane;
16import javax.swing.JButton;
17import javax.swing.Box;
18
19public class DisplayQueryResults extends JFrame
20{
21// JDBC driver and database URL
22static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
23static final String DATABASE_URL = "jdbc:mysql://localhost/books";
24static final String USERNAME= "jhtp6";
25static final String PASSWORD= "jhtp6";
26
27// default query selects all rows from authors table
28static final String DEFAULT_QUERY = "SELECT * FROM authors";
30private ResultSetTableModel tableModel;
31private JTextArea queryArea;
33// create ResultSetTableModel and GUI
34public DisplayQueryResults()
35{
36super ( "Displaying Query Results" );
38// create ResultSetTableModel and display database table
39try
40{
41// create TableModel for results of query SELECT * FROM authors
42tableModel = new ResultSetTableModel( JDBC_DRIVER, DATABASE_URL,
43 |
USERNAME, PASSWORD, DEFAULT_QUERY ); |
44 |
|
45// set up JTextArea in which user types queries
46queryArea = new JTextArea( DEFAULT_QUERY, 3, 100 );
47queryArea.setWrapStyleWord( true );
48queryArea.setLineWrap( true );
49 |
|
50 |
JScrollPane scrollPane = new JScrollPane(queryArea, |
51 |
ScrollPaneConstants. VERTICAL_SCROLLBAR_AS_NEEDED, |
52 |
ScrollPaneConstants. HORIZONTAL_SCROLLBAR_NEVER ); |
53 |
|
54// set up JButton for submitting queries
55JButton submitButton = new JButton( "Submit Query" );
57// create Box to manage placement of queryArea and
58// submitButton in GUI
59Box box = Box.createHorizontalBox();
60box.add( scrollPane );
61box.add( submitButton );
62
63// create JTable delegate for tableModel
64JTable resultTable = new JTable( tableModel );
66// place GUI components on content pane
67add( box, BorderLayout. NORTH );
68add( new JScrollPane( resultTable ), BorderLayout. CENTER );
70// create event listener for submitButton
71submitButton.addActionListener(
72 |
|
|
73 |
new |
ActionListener() |
74 |
{ |
|
75 |
// pass query to table model |
|
76 |
public void actionPerformed( ActionEvent event ) |
|
77 |
{ |
|
78 |
|
// perform a new query |
79 |
|
try |
80 |
|
{ |
81 |
|
tableModel.setQuery(queryArea.getText() ); |
82 |
|
} // end try |
83 |
|
catch ( SQLException sqlException ) |
84 |
|
{ |
85 |
|
JOptionPane.showMessageDialog( null, |
86 |
|
sqlException.getMessage(), "Database error", |
87 |
|
JOptionPane.ERROR_MESSAGE ); |
88 |
|
|
89 |
|
// try to recover from invalid user query |
90 |
|
// by executing default query |
91 |
|
try |
92 |
|
{ |
93 |
|
tableModel.setQuery( DEFAULT_QUERY ); |
94 |
|
queryArea.setText( DEFAULT_QUERY ); |
95 |
|
} // end try |
96 |
|
catch ( SQLException sqlException2 ) |
97 |
|
{ |
98 |
|
JOptionPane.showMessageDialog( null, |
99 |
|
sqlException2.getMessage(), "Database error", |
100 |
|
JOptionPane.ERROR_MESSAGE ); |
101 |
|
|
102 |
|
// ensure database connection is closed |
103 |
|
tableModel.disconnectFromDatabase(); |
104 |
|
|
105 |
|
System.exit( 1 ); // terminate application |
106 |
|
} // end inner catch |
107 |
|
} // end outer catch |
108 |
} |
// end actionPerformed |
109} // end ActionListener inner class
110); // end call to addActionListener
112 |
setSize( 500, 250 ); // set window size |
113setVisible( true ); // display window
114} // end try
115catch ( ClassNotFoundException classNotFound )
116{
117JOptionPane.showMessageDialog( null,
118"MySQL driver not found", "Driver not found",
119JOptionPane.ERROR_MESSAGE );
120
121System.exit( 1 ); // terminate application
122} // end catch
123catch ( SQLException sqlException )
124{
125JOptionPane.showMessageDialog( null, sqlException.getMessage(),
126"Database error", JOptionPane.ERROR_MESSAGE );
127
128// ensure database connection is closed
129tableModel.disconnectFromDatabase();
131 |
System.exit( 1 ); |
// terminate application |
132 |
} // end catch |
|
133 |
|
|
134// dispose of window when user quits application (this overrides
135// the default of HIDE_ON_CLOSE)
136setDefaultCloseOperation( DISPOSE_ON_CLOSE );
137
138// ensure database connection is closed when user quits application
139addWindowListener(
140
141new WindowAdapter()
142{
143// disconnect from database and exit when window has closed
144public void windowClosed( WindowEvent event )
145{
146 |
tableModel.disconnectFromDatabase(); |
147 |
System.exit( 0 ); |
148} // end method windowClosed
149} // end WindowAdapter inner class
150); // end call to addWindowListener
151} // end DisplayQueryResults constructor
153// execute application
154public static void main(String args[])
155{
156new DisplayQueryResults();
157} // end main
158} // end class DisplayQueryResults
[View full size image]
[Page 1224 (continued)]
25.9. Stored Procedures
Many database management systems can store individual SQL statements or sets of SQL statements in a database, so that programs accessing that database can invoke them. Such named collections of SQL are called stored procedures. JDBC enables programs to invoke stored procedures using objects that implement interface CallableStatement. CallableStatements can receive arguments specified with the methods inherited from interface PreparedStatement. In addition, CallableStatements can specify output parameters in which a stored procedure can place return values. Interface CallableStatement includes methods to specify which parameters in a stored procedure are output parameters. The interface also includes methods to obtain the values of output parameters returned from a stored procedure.
Portability Tip 25.7
Although the syntax for creating stored procedures differs across database management systems, interface CallableStatement provides a uniform interface for specifying input and output parameters for stored procedures and for invoking stored procedures.
Portability Tip 25.8
According to the Java API documentation for interface CallableStatement, for maximum portability between database systems, programs should process the update counts or ResultSets returned from a CallableStatement before obtaining the values of any output parameters.
[Page 1224 (continued)]
25.10. RowSet Interface
In the previous examples, you learned how to query a database by explicitly establishing a Connection to the database, preparing a Statement for querying the database and executing the query. In this section, we demonstrate the RowSet interface, which configures the database connection and prepares query statements automatically. Interface RowSet provides several set methods that allow the programmer to specify the properties needed to establish a connection (such as the database URL, user name and password of the database) and create a Statement (such as a query). Interface RowSet also provides several get methods that return these properties.
RowSet is part of the javax.sql package. Although part of Java 2 Standard Edition, the classes and interfaces of package javax.sql are most frequently used in the context of the Java 2 Platform Enterprise Edition (J2EE). J2EE is used in industry to build substantial distributed applications that often process database data. J2EE is beyond the scope of this book. You can learn more about J2EE by visiting java.sun.com/j2ee/.
[Page 1225]
There are two types of RowSet objectsconnected and disconnected. A connected RowSet object connects to the database once and remains connected until the application terminates. A disconnected RowSet object connects to the database, executes a query to retrieve the data from the database and then closes the connection. A program may change the data in a disconnected RowSet while it is disconnected. Modified data can be updated in the database after a disconnected RowSet reestablishes the connection with the database.
J2SE 5.0 package javax.sql.rowset contains two subinterfaces of RowSetJdbcRowSet and CachedRowSet. JdbcRowSet, a connected RowSet, acts as a wrapper around a ResultSet object, and allows programmers to scroll through and update the rows in the ResultSet. Recall that by default, a ResultSet object is non-scrollable and read onlyyou must explicitly set the result-set type constant to TYPE_SCROLL_INSENSITIVE and set the result-set concurrency constant to CONCUR_UPDATABLE to make a ResultSet object scrollable and updatable. A JdbcRowSet object is scrollable and updatable by default. CachedRowSet, a disconnected RowSet, caches the data of a ResultSet in memory and disconnects from the database. Like JdbcRowSet, a CachedRowSet object is scrollable and updatable by default. A CachedRowSet object is also serializable, so it can be passed between Java applications through a network, such as the Internet. However, CachedRowSet has a limitationthe amount of data that can be
stored in memory is limited. Besides JdbcRowSet and CachedRowSet, package javax.sql.rowset contains three other subinterfaces of RowSet. For details of these interfaces, visit java.sun.com/j2se/5.0/docs/guide/jdbc/getstart/rowsetImpl.html.
Figure 25.32 reimplements the example of Fig. 25.25 using a RowSet. Rather than establish the connection and create a Statement explicitly, Fig. 25.32 uses a JdbcRowSet object to create a
Connection and a Statement automatically.
Figure 25.32. Displaying the authors table using JdbcRowSet.
(This item is displayed on pages 1225 - 1226 in the print version)
1// Fig. 25.32: JdbcRowSetTest.java
2// Displaying the contents of the authors table using JdbcRowSet.
3import java.sql.ResultSetMetaData;
4import java.sql.SQLException;
5import javax.sql.rowset.JdbcRowSet;
6import com.sun.rowset.JdbcRowSetImpl; // Sun's JdbcRowSet implementation
8public class JdbcRowSetTest
9{
10// JDBC driver name and database URL
11static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
12static final String DATABASE_URL = "jdbc:mysql://localhost/books";
13static final String USERNAME = "jhtp6";
14static final String PASSWORD = "jhtp6";
