Documentation

Using JDBC with GUI API
Trail: JDBC(TM) Database Access
Lesson: JDBC Basics

Using JDBC with GUI API

The sample CoffeesFrame.java demonstrates how to integrate JDBC with a GUI API, in particular, the Swing API. It displays the contents of the COFFEES database table in a table and contains fields and buttons that enable you to add rows to the table. The following is a screenshot of this sample:

Screenshot of Sample CoffeeFrames.java

The sample contains five text fields that correspond to each of the columns in the COFFEES table. It also contains three buttons:

This sample (which requires CoffeesTableModel) demonstrates the following general steps to integrate JDBC with the Swing API:

  1. Implementing the TableModel interface
  2. Implementing the RowSetListener interface
  3. Laying out the Swing components
  4. Adding listeners for the buttons in the sample

Implementing javax.swing.event.TableModel

The TableModel interface enables a Java Swing application to manage data in a JTable object. The sample, CoffeesTableModel.java, implements this interface. It specifies how a JTable object should retrieve data from a RowSet object and display it in a table.

Note: Although this sample displays the contents of the COFFEES table in a Swing application, the class CoffeesTableModel should work for any SQL table provided that its data can be represented with String objects. (However, the fields that enable users to add rows to COFFEES, which are specified in the class CoffeesFrame, would have to be modified for other SQL tables.)

Before implementing the methods of the interface TableModel, the constructor of the class CoffeeTableModel initializes various member variables required for these implemented methods as follows:

public CoffeesTableModel(CachedRowSet rowSetArg)
    throws SQLException {

    this.coffeesRowSet = rowSetArg;
    this.metadata = this.coffeesRowSet.getMetaData();
    numcols = metadata.getColumnCount();

    // Retrieve the number of rows.
    this.coffeesRowSet.beforeFirst();
    this.numrows = 0;
    while (this.coffeesRowSet.next()) {
        this.numrows++;
    }
    this.coffeesRowSet.beforeFirst();
}

The following describes the member variables initialized in this constructor:

The CoffeesTableModel.java sample implements the following methods from TableModel interface:

The following methods have not been implemented because this sample does not allow users to directly edit the contents of the table:

Implementing getColumnCount and getRowCount

The methods getColumnCount and getRowCount return the value of the member variables numcols and numrows, respectively:

public int getColumnCount() {
    return numcols;
}

public int getRowCount() {
    return numrows;
}

Implementing getColumnClass

The getColumnClass method returns the data type of the specified column. To keep things simple, this method returns the String class, thereby converting all data in the table into String objects. The JTable class uses this method to determine how to render data in the GUI application.

public Class getColumnClass(int column) {
    return String.class;
}

Implementing getColumnName

The getColumnName method returns the name of the specified column. The JTable class uses this method to label each of its columns.

public String getColumnName(int column) {
    try {
        return this.metadata.getColumnLabel(column + 1);
    } catch (SQLException e) {
        return e.toString();
    }
}

Implementing getColumnAt

The getColumnAt method retrieves the value at the specified row and column in the row set coffeesRowSet. The JTable class uses this method to populate its table. Note that SQL starts numbering its rows and columns at 1, but the TableModel interface starts at 0; this is the reason why the rowIndex and columnIndex values are incremented by 1.

public Object getValueAt(int rowIndex, int columnIndex) {

    try {
        this.coffeesRowSet.absolute(rowIndex + 1);
        Object o = this.coffeesRowSet.getObject(columnIndex + 1);
        if (o == null)
            return null;
        else
            return o.toString();
    } catch (SQLException e) {
        return e.toString();
    }
}

Implementing isCellEditable

Because this sample does not allow users to directly edit the contents of the table (rows are added by another window control), this method returns false regardless of the values of rowIndex and columnIndex:

public boolean isCellEditable(int rowIndex, int columnIndex) {
    return false;
}

Implementing javax.sql.RowSetListener

The class CoffeesFrame implements only one method from the interface RowSetListener, rowChanged. This method is called when a user adds a row to the table.

public void rowChanged(RowSetEvent event) {

    CachedRowSet currentRowSet =
        this.myCoffeesTableModel.coffeesRowSet;

    try {
        currentRowSet.moveToCurrentRow();
        myCoffeesTableModel = new CoffeesTableModel(
            myCoffeesTableModel.getCoffeesRowSet());
        table.setModel(myCoffeesTableModel);

    } catch (SQLException ex) {

        JDBCTutorialUtilities.printSQLException(ex);

        // Display the error in a dialog box.

        JOptionPane.showMessageDialog(
            CoffeesFrame.this,
            new String[] {
                // Display a 2-line message
                ex.getClass().getName() + ": ",
                ex.getMessage()
            }
        );
    }
}

This method updates the table in the GUI application.

Laying Out Swing Components

The constructor of the class CoffeesFrame initializes and lays out the Swing components. The following statement retrieves the contents of the COFFEES table, stores the contents in the CachedRowSet object myCachedRowSet, and initializes the JTable Swing component:

CachedRowSet myCachedRowSet = getContentsOfCoffeesTable();
myCoffeesTableModel = new CoffeesTableModel(myCachedRowSet);
myCoffeesTableModel.addEventHandlersToRowSet(this);

// Displays the table   
table = new JTable(); 
table.setModel(myCoffeesTableModel);

As mentioned previously, instead of a ResultSet object to represent the contents of the COFFEES table, this sample uses a RowSet object, notably a CachedRowSet object.

The method CoffeesFrame.getContentsOfCoffeesTable retrieves the contents of the table COFFEES.

The method CoffeesTableModel.addEventHandlersToRowSet adds the event handler defined in the CoffeesFrame class, which is the method rowChanged, to the row set member variable CoffeesTableModel.coffeesRowSet. This enables the class CoffeesFrame to notify the row set coffeesRowSet of any events, in particular, when a user clicks the button Add row to table, Update database, or Discard changes. When the row set coffeesRowSet is notified of one of these changes, the method CoffeesFrame.rowChanged is called.

The statement table.setModel(myCoffeesTableModel) specifies that it use the CoffeesTableModel object myCoffeesTableModel to populate the JTable Swing component table.

The following statements specify that the CoffeesFrame class use the layout GridBagLayout to lay out its Swing components:

Container contentPane = getContentPane();
contentPane.setComponentOrientation(
    ComponentOrientation.LEFT_TO_RIGHT);
contentPane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();

See How to Use GridBagLayout in the Creating a GUI With JFC/Swing for more information about using the layout GridBagLayout.

See the source code for CoffeesFrame.java to see how the Swing components of this sample are added to the layout GridBagLayout.

Adding Listeners for Buttons

The following statement adds a listener to the button Add row to table:

button_ADD_ROW.addActionListener(
    new ActionListener() {
      
    public void actionPerformed(ActionEvent e) {

        JOptionPane.showMessageDialog(
            CoffeesFrame.this, new String[] {
                "Adding the following row:",
                "Coffee name: [" +
                textField_COF_NAME.getText() +
                "]",
                "Supplier ID: [" +
                textField_SUP_ID.getText() + "]",
                "Price: [" +
                textField_PRICE.getText() + "]",
                "Sales: [" +
                textField_SALES.getText() + "]",
                "Total: [" +
                textField_TOTAL.getText() + "]"
            }
        );

        try {
            myCoffeesTableModel.insertRow(
                textField_COF_NAME.getText(),
                Integer.parseInt(textField_SUP_ID.getText().trim()),
                Float.parseFloat(textField_PRICE.getText().trim()),
                Integer.parseInt(textField_SALES.getText().trim()),
                Integer.parseInt(textField_TOTAL.getText().trim())
            );
        } catch (SQLException sqle) {
            displaySQLExceptionDialog(sqle);
        }
    }
});

When a user clicks this button, it performs the following:

If an SQLException is thrown, then the method CoffeesFrame.displaySQLExceptionDialog creates a message dialog box that displays the content of the SQLException.

The following statement adds a listener to the button Update database:

button_UPDATE_DATABASE.addActionListener(
    new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            try {
                myCoffeesTableModel.coffeesRowSet.acceptChanges();
                msgline.setText("Updated database");
            } catch (SQLException sqle) {
                displaySQLExceptionDialog(sqle);
                // Now revert back changes
                try {
                    createNewTableModel();
                    msgline.setText("Discarded changes");
                } catch (SQLException sqle2) {
                    displaySQLExceptionDialog(sqle2);
                }
            }
        }
    }
);

When a user clicks this button, the table COFFEES is updated with the contents of the row set myCoffeesTableModel.coffeesRowSet.

The following statement adds a listener to the button Discard changes:

button_DISCARD_CHANGES.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        try {
            createNewTableModel();
        } catch (SQLException sqle) {
            displaySQLExceptionDialog(sqle);
        }
    }
});

When a user clicks this button, the method CoffeesFrame.createNewTableModel is called, which repopulates the JTable component with the contents of the COFFEES table.


Previous page: Using Stored Procedures
Next page: End of Trail