Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

AhmadLang / Java, How To Program, 2004

.pdf
Скачиваний:
626
Добавлен:
31.05.2015
Размер:
51.82 Mб
Скачать

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";