1

I'm working on a Swing app which is connected to MySQL database. It fetches some data from the DB and shows in the table. Besides, there are 3 buttons that suppose to changes table data ( Add row to table ), update the database (Update database ) and to discard the changes ( Discard changes ) as following,

enter image description here

After clicking the Add row to table button, the new entries from the form should be added in the table. Afterwards if the update button is clicked, the data will be updated in the db. Discard changes will delete the data from the last row of the table and will have nothing to do with db. The app has two classes CoffeesFrame.java and CoffeesTableModel.java I provided the sample code from both of the classes below,

public class CoffeesFrame extends JFrame implements RowSetListener {

    private static final long serialVersionUID = 1L;

    private static Connection myConn = null;
    private static String url = "jdbc:mysql://localhost:3306/myDemo";
    private static String user = "student";
    private static String password = "student";

    // initiate a table object 
   JTable table; 

// labels of the table 

// text data fields of the table 

// buttons of the table 


// initiate a table model object
CoffeesTableModel myCoffeesTableModel;

// table constructor 
public CoffeesFrame(Connection myConn) throws SQLException {


    table = new JTable();       
    createNewTableModel(myConn);

    /*
       label, text field and buttons 
    */

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

    /*
    Code for making the GUI
    */



    button_ADD_ROW.addActionListener(new ActionListener() {

        public void actionPerformed(ActionEvent e) {

            // showMessageDialog works 
            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 {

                // Insert row is not adding data to the table 
                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()));

                // table.getModel();
            }

            catch (Exception ex) {

                ex.printStackTrace();
            }
        }

    });


    button_UPDATE_DATABASE.addActionListener(new ActionListener() {

        public void actionPerformed(ActionEvent e) {

            try {

                myCoffeesTableModel.coffeesRowSet.acceptChanges(myConn);
            }

            catch (Exception ex) {

                ex.printStackTrace();
            }


            try {

                createNewTableModel(myConn);
            }

            catch (Exception el) {

                el.printStackTrace();
            }

        }
    });


    button_DISCARD_CHANGES.addActionListener(new ActionListener() {

        public void actionPerformed(ActionEvent e) {

            System.out.println("The changes are DISCARDED");
        }
    });
}



private void createNewTableModel(Connection myConn) throws SQLException {

    myCoffeesTableModel = new CoffeesTableModel(getContentsOfCoffeesTable(myConn));
    myCoffeesTableModel.addEventHandlersToRowSet(this);
    table.setModel(myCoffeesTableModel);
  }

@Override
public void rowSetChanged(RowSetEvent event) {

    // TODO Auto-generated method stub
}

@Override
public void rowChanged(RowSetEvent event) {

    // TODO Auto-generated method stub
}

@Override
public void cursorMoved(RowSetEvent event) {

    // TODO Auto-generated method stub

}

public CachedRowSet getContentsOfCoffeesTable(Connection mycConn)
        throws SQLException {

    CachedRowSet crs = null;
    ResultSet resultSet = null;
    Statement stmt = null;
    String sql = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES";

    try {

        stmt = myConn.createStatement();
        resultSet = stmt.executeQuery(sql);

        crs = new CachedRowSetImpl();
        crs.populate(resultSet);

    }

    catch (Exception e) {

        e.printStackTrace();
    }

    return crs;
}


public static void main(String[] args) throws SQLException {

    try {

        myConn = DriverManager.getConnection(url, user, password);

        if (myConn != null) {

            System.out.println("Connected to the database myDemo");
        }
    }

    catch (SQLException ex) {

        System.out
                .println("An error occurred. Maybe user/password is invalid");
        ex.printStackTrace();
    }

    myConn.setAutoCommit(false);

    CoffeesFrame coffeesFrame = new CoffeesFrame(myConn);
    coffeesFrame.pack();
    coffeesFrame.setVisible(true);

}

}

The CoffeesTableModel.java as follows, //

public class CoffeesTableModel implements TableModel {

CachedRowSet coffeesRowSet; // The ResultSet to interpret
ResultSetMetaData metadata; // Additional information about the results
int numcols, numrows; // How many rows and columns in the table


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();
}

public CachedRowSet getCoffeesRowSet() {

    return coffeesRowSet;
}

public void addEventHandlersToRowSet(RowSetListener listener) {

    this.coffeesRowSet.addRowSetListener(listener);
}

public void insertRow(String coffeeName, int supplierID, float price,
        int sales, int total) throws SQLException {

    try {

        this.coffeesRowSet.moveToInsertRow();
        this.coffeesRowSet.updateString("COF_NAME", coffeeName);
        this.coffeesRowSet.updateInt("SUP_ID", supplierID);
        this.coffeesRowSet.updateFloat("PRICE", price);
        this.coffeesRowSet.updateInt("SALES", sales);
        this.coffeesRowSet.updateInt("TOTAL", total);
        this.coffeesRowSet.insertRow();
        this.coffeesRowSet.moveToCurrentRow();
    }

    catch (SQLException e) {

        e.printStackTrace();
        // JDBCTutorialUtilities.printSQLException(e);
    }
}

public void close() {

    try {

        coffeesRowSet.getStatement().close();
    }

    catch (SQLException e) {

        e.printStackTrace();
        // JDBCTutorialUtilities.printSQLException(e);
    }
}

/** Automatically close when we're garbage collected */
protected void finalize() {

    close();
}

/** Method from interface TableModel; returns the number of columns */

public int getColumnCount() {

    return numcols;
}

/** Method from interface TableModel; returns the number of rows */
public int getRowCount() {

    return numrows;
}

/**
 * Method from interface TableModel; returns the column name at columnIndex
 * based on information from ResultSetMetaData
 */

public String getColumnName(int column) {

    try {

        return this.metadata.getColumnLabel(column + 1);
    } 

    catch (SQLException e) {

        return e.toString();
    }
}


/**
 * Method from interface TableModel; returns the most specific superclass
 * for all cell values in the specified column. To keep things simple, all
 * data in the table are converted to String objects; hence, this method
 * returns the String class.
 */

public Class getColumnClass(int column) {

    return String.class;
}

/**
 * Method from interface TableModel; returns the value for the cell
 * specified by columnIndex and rowIndex. TableModel uses this method to
 * populate itself with data from the row set. SQL starts numbering its rows
 * and columns at 1, but TableModel starts at 0.
 */

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();
    }       
}

/**
 * Method from interface TableModel; returns true if the specified cell is
 * editable. This sample does not allow users to edit any cells from the
 * TableModel (rows are added by another window control). Thus, this method
 * returns false.
 */

public boolean isCellEditable(int rowIndex, int columnIndex) {

    return false;
}

// Because the sample does not allow users to edit any cells from the
// TableModel, the following methods, setValueAt, addTableModelListener,
// and removeTableModelListener, do not need to be implemented.

public void setValueAt(Object value, int row, int column) {

    System.out.println("Calling setValueAt row " + row + ", column "
            + column);
}

public void addTableModelListener(TableModelListener l) {

}

public void removeTableModelListener(TableModelListener l) {

}

}

The data shows in the table is grabbed from the db and presented as table. The "Add row to table" shows the pop-up from the form insertion but don't put the data as new row in the end of the table. However, I'm not getting any error. The "Update database" button is not working and provides java.sql.SQLException: Table not specified.. How can I improve the code ?

1 Answer 1

4

Instead of implementing the TableModel interface, you should be extending from the AbstractTableModel, this has default functionality of some of the "normal" functionality you don't want to replicated.

Remove the addTableModelListener and removeTableModelListener, your current implementation has essentially broken the model's contract by not providing the ability to make notifications to other observers (like the JTable)

Your insertRow method MUST call fireTableRowsInserted (from AbstractTableModel) in order to notify the JTable that the model has changed and that it needs to update itself with the new data

Java makes no guarantees that finalize will be called, don't rely on it

getRowCount will be effected by the size of the RowSet, therefore, you need to be able to either keep a running value (ie you need to increment it when you add new rows) or calculate the number of rows from the RowSet itself. I believe you can do this by moving the cursor to the end (or beyond the last position) and using getRow, don't forget though, ResultSet is 1 based, not 0 based which JTable expects

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks a lot for your reply
I will accept the answer for I finish writing the code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.