0

This is a follow-up question to my post here.

My JTable is populated using abstracttablemodel. I implemented an insertRow method which works perfectly in the database but does not refresh my JTable. As seen in the code below, after the insert, I use the fireTableRowsInserted method to update the model but this doesn't seem to work. The abstracttablemodel with insertRow method is as below:

class ResultSetTableModel extends AbstractTableModel
{
 private final Connection connection;
 private final Statement statement;
 private ResultSet resultSet;
 private ResultSetMetaData resultSetMetaData;
 private int numberOfRows;
 
 private static final String oracleDriver = "oracle.jdbc.driver.OracleDriver";


 //track DB connection status
 private boolean dbConnStatus = false;
 
 //constructor initializes rSet and obtains its
 //metadata object; also determines number of rows
 public ResultSetTableModel(String oracleConnection, String username, String password, String query) throws SQLException
 {
  
  //connect to the database
  connection = getDBConnection(oracleConnection, username, password);
  
  //create statement to query database
  statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
  
  //update database connection status
  dbConnStatus = true;
  
  //set query and execute it
  setQuery(query);
 }
 
 private static Connection getDBConnection(String oraConn, String user, String pwd)
 {
  Connection dbConn = null;
  
  try
  {
   Class.forName(oracleDriver);
  }
  catch (ClassNotFoundException classExcep)
  {
   System.out.println(classExcep.getMessage());
  }
  
  try
  {
   dbConn = DriverManager.getConnection(oraConn, user, pwd);
   
   return dbConn;
  }
  catch (SQLException sqlExcep)
  {
   System.out.println(sqlExcep.getMessage());
  }
  
  return dbConn;
 }
 
 //get class that represents column type
 @SuppressWarnings({ "unchecked", "rawtypes" })
 public Class getColumnClass(int column) throws IllegalStateException
 {
  //ensure database connection is available
  if(!dbConnStatus)
   throw new IllegalStateException("No connection to the Database");
  
  //determine Java class of column
  try
  {
   String className = resultSetMetaData.getColumnClassName(column + 1);
   
   //return Class object that represents class Name
   return Class.forName(className);
  }
  catch (Exception e)
  {
   e.printStackTrace();
  }
  
  return Object.class; //if problems occur above, assume type Object
 }

 //remove row in the ResultSet
 public void removeRow(String empID)
 {
  int rsRow = 0;
  
  try
  {
   //set cursor to beginning of data set (before first row)
   if(!resultSet.isBeforeFirst())
    resultSet.beforeFirst();
   
   //iterate through resultSet to find matching record with
   //correct employee ID. once found delete row
   while(resultSet.next())
   {
    if(resultSet.getString("EMPLOYEENO").equals(empID))
    {
     rsRow = resultSet.getRow();
     
     resultSet.absolute(rsRow);
     resultSet.deleteRow();
     
     break;
    }
   }
 
   resultSet.last();
   numberOfRows = resultSet.getRow();

   fireTableDataChanged();

  }
  catch (SQLException e)
  {
   e.printStackTrace();
  }
 }
 
 //add row into ResultSet
 public void insertRow(String fName, String lName, String userID, String company, String group)
 {
  //get display name of user
  String displayName = fName.substring(0, 1).concat(". ").concat(lName);

  try
  {
   //move cursor to staging row for record insertion
   resultSet.moveToInsertRow();
   
   resultSet.updateString("EMPLOYEENO", userID);
   resultSet.updateString("FIRSTNAME", fName);
   resultSet.updateString("LASTNAME", lName);
   resultSet.updateString("DISPLAYNAME", displayName);
   resultSet.updateString("GROUPNAME", group);
   resultSet.updateString("COMPANYNAME", company);
   
   resultSet.insertRow();
   resultSet.beforeFirst();
   
   //resultSet.last();
   //row = resultSet.getRow();

   //fireTableDataChanged();
   //fireTableStructureChanged();
   fireTableRowsInserted(this.getRowCount() - 1, this.getRowCount() - 1);

  } 
  catch (SQLException e)
  {
   e.printStackTrace();
  } 
 }
 
 //get the number of columns in the ResultSet
 public int getColumnCount() throws IllegalStateException
 {
  //ensure database connection is available
  if(!dbConnStatus)
   throw new IllegalStateException("No connection to the Database");
  
  //determine number of columns
  try
  {
   return resultSetMetaData.getColumnCount();
  }
  catch (SQLException sqlException)
  {
   sqlException.printStackTrace();
  }
  
  return 0; //if problem occur above, return 0 for number of columns
 }
 
 //get name of a particular column in ResultSet
 public String getColumnName(int column) throws IllegalStateException
 {
  //ensure database connection is available
  if(!dbConnStatus)
   throw new IllegalStateException("No connection to the Database");
  
  //determine column name
  try
  {
   return resultSetMetaData.getColumnName(column + 1);
  }
  catch (SQLException sqlException)
  {
   sqlException.printStackTrace();
  }
  
  return ""; //if problems occur above, return empty string for column name 
 }
 
 //return number of rows in ResultSet
 public int getRowCount() throws IllegalStateException
 {
  //ensure database connection is available
  if(!dbConnStatus)
   throw new IllegalStateException("No connection to the Database");
  
  return numberOfRows; 
 }
 
 //obtain value in particular row and column
 public Object getValueAt(int row, int column) throws IllegalStateException
 {
  //ensure database connection is available
  if(!dbConnStatus)
   throw new IllegalStateException("No connection to the Database");
  
  //obtain a value at specified ResultSet row and column
  try
  {
   resultSet.absolute(row + 1);
   return resultSet.getObject(column + 1);
  }
  catch (SQLException sqlException)
  {
   sqlException.printStackTrace();
  }
  
  return ""; //if problems occur above, return empty string object  
 }
 
 //set new database query string
 public void setQuery(String query) throws SQLException, IllegalStateException
 {
  //ensure database connection is available
  if(!dbConnStatus)
   throw new IllegalStateException("No connection to the Database");
  
  //specify query and execute it
  resultSet = statement.executeQuery(query);
  
  //obtain metadata for ResultSet
  resultSetMetaData = resultSet.getMetaData();
  
  //determine number of rows in ResultSet
  resultSet.last(); //move to last row
  numberOfRows = resultSet.getRow(); //get row number
  
  //notify JTable that model has changed
  fireTableStructureChanged();
 }
 
 //close Statement and Connection
 public void disconnectFromDatabase()
 {
  //ensure database connection is available
  if(dbConnStatus);
  
  //determine number of columns
  try
  {
   resultSet.close();
   statement.close();
   connection.close();
  }
  catch (SQLException sqlException)
  {
   sqlException.printStackTrace();
  }
  finally
  {
   dbConnStatus = false;
  }
  
 }
} //end class ResultSetTableModel

My custom JFrame class that includes the button call for the insert:

class AdministrationFrame extends JFrame
{
//set default query to retrieve all non-disabled users
private static final String DEFAULT_QUERY = 
    "SELECT EMPLOYEENO, FIRSTNAME, LASTNAME, DISPLAYNAME, GROUPNAME, COMPANYNAME "
    + "FROM EMPLOYEES "
    + "WHERE DISABLED IS NULL ";

//query to retrieve all groups
private static final String ALL_GROUPS_QUERY = 
    "SELECT DISTINCT GROUPNAME "
    + "FROM EMPLOYEES "
    + "ORDER BY GROUPNAME ";

private static final String ALL_COMPANIES_QUERY = 
    "SELECT DISTINCT COMPANYNAME "
    + "FROM EMPLOYEES ";

private static final String ORACLE_CONNECTION = "jdbc:oracle:thin:@..."; //connection to UAT DB

private static final String USERNAME = "...";
private static final String PASSWORD = "...";

//layout for window
private final BorderLayout layout;

private final GridBagLayout gbLayout;
private final GridBagConstraints c;

//administration window
private final JFrame adminFrame;

private final JPanel tablePanel;
private final JPanel tablePanel2;
private final JPanel modifyPanel;
private final JPanel buttonPanel;

//items for tablePanel
private final JLabel filterLabel;
private final JTextField filterTextField;
private final JButton filterButton;

//items for modifyPanel
private final JLabel firstNameLabel;
private final JLabel lastNameLabel;
private final JLabel userIDLabel;
private final JLabel companyLabel;
private final JLabel groupLabel;

private final JTextField firstNameField;
private final JTextField lastNameField;
private final JTextField userIDField;
private final JTextField companyField;
private final JTextField groupField;

private final JComboBox<String> groupsDropDown;
private final JComboBox<String> companiesDropDown;

//items for buttonPanel
private final JButton updateButton;

private Connection conn;

private JTable resultTable;

private static ResultSetTableModel tblModel;

@SuppressWarnings("unchecked")
public AdministrationFrame()
{
  layout = new BorderLayout(10, 10);
  setLayout(layout);

  gbLayout = new GridBagLayout();
  c = new GridBagConstraints();

  //place GUI components on JFrame's content pane
  adminFrame = new JFrame("Employee Modification Panel");

  //set up JPanels
  tablePanel = new JPanel();
  tablePanel2 = new JPanel();

  String tablePanelTitle = "Employee Details";
  tablePanel.setBorder(BorderFactory.createTitledBorder(null, 
      tablePanelTitle, TitledBorder.CENTER, TitledBorder.TOP,
      new Font("Arial", Font.BOLD + Font.ITALIC, 22), Color.BLACK));

  tablePanel2.setLayout(new BoxLayout(tablePanel2, BoxLayout.Y_AXIS));

  modifyPanel = new JPanel();
  modifyPanel.setLayout(gbLayout);

  buttonPanel = new JPanel();

  //set up items in each JPanel
  filterLabel = new JLabel("Filter:");
  filterLabel.setAlignmentX(LEFT_ALIGNMENT);
  filterTextField = new JTextField();
  filterTextField.setAlignmentX(LEFT_ALIGNMENT);
  filterButton = new JButton("Apply Filter");
  filterButton.setAlignmentX(LEFT_ALIGNMENT);

  firstNameLabel = new JLabel("First Name:");
  lastNameLabel = new JLabel("Last Name:");
  userIDLabel = new JLabel("Employee ID:");
  companyLabel = new JLabel("Company:");
  groupLabel = new JLabel("Group:");

  firstNameField = new JTextField();
  lastNameField = new JTextField();
  userIDField = new JTextField();
  companyField = new JTextField();
  companyField.setEditable(false);
  groupField = new JTextField();
  groupField.setEditable(false);

  updateButton = new JButton("Insert/Modify");

  //create custom renderer for the company & group
  //drop down menus - changes their behavior
  class PromptComboBoxRenderer extends BasicComboBoxRenderer
  {
    //set the text to display when no item has been selected
    private String prompt;

    public PromptComboBoxRenderer(String prompt)
    {
      this.prompt = prompt;
    }

    @SuppressWarnings("rawtypes")
    public Component getListCellRendererComponent(
        JList list, Object value, int index,
        boolean isSelected, boolean cellHasFocus)
    {
      super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);

      if(value == null)
        setText(prompt);

      return this;
    }
  }

  groupsDropDown = new JComboBox<String>();
  groupsDropDown.setRenderer(new PromptComboBoxRenderer("Select a Group"));
  groupsDropDown.addItemListener(new ItemListener() //anonymous inner class
  {
    @Override
    public void itemStateChanged(ItemEvent event)
    {
      if(event.getStateChange() == ItemEvent.SELECTED)
      {
        if(groupsDropDown.getSelectedItem().toString() != "")
        {
          String selectedGroup = groupsDropDown.getSelectedItem().toString();
          //System.out.println("You selected group: " + selectedGroup);
          groupField.setText(selectedGroup);
        }
      }
    }
  });

  companiesDropDown = new JComboBox<String>();
  companiesDropDown.setRenderer(new PromptComboBoxRenderer("Select a Company"));
  companiesDropDown.addItemListener(new ItemListener() //anonymous inner class
  {
    @Override
    public void itemStateChanged(ItemEvent event)
    {
      if(event.getStateChange() == ItemEvent.SELECTED)
      {
        if(companiesDropDown.getSelectedItem().toString() != "")
        {
          String selectedCompany = companiesDropDown.getSelectedItem().toString();
          //System.out.println("You selected company: " + selectedCompany);
          companyField.setText(selectedCompany);
        }
      }
    }
  });

  //user click "Insert/Modify"
  updateButton.addActionListener(new ActionListener()
  {
    @Override
    public void actionPerformed(ActionEvent e)
    {
      tblModel.insertRow(getFirstName(), getLastName(), getUserID(), getCompany(), getGroup());
      refreshScreen();

    }
  });

  //create ResultSetTableModel and display database table
  try
  {
    //create TableModel for results of the default query
    tblModel = new ResultSetTableModel(ORACLE_CONNECTION, USERNAME, PASSWORD, DEFAULT_QUERY);

    //create JTable based on the tblModel
    resultTable = new JTable(tblModel)
    {
      @Override
      public Dimension getPreferredScrollableViewportSize()
      {
        return new Dimension(600, 250);
      }
    };

    resultTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
    resultTable.getTableHeader().setResizingAllowed(false); //disable column resizing
    resultTable.getTableHeader().setReorderingAllowed(false); //disable column dragging
    resultTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); //sets table to only allow selection of single row
    resultTable.getSelectionModel().addListSelectionListener(new RowListener()); //register event handlers
    final JScrollPane tablePane = new JScrollPane(resultTable);

    //add items to JPanels
    tablePanel2.add(filterLabel);
    tablePanel2.add(Box.createRigidArea(new Dimension(0, 2)));
    tablePanel2.add(filterTextField);
    tablePanel2.add(Box.createRigidArea(new Dimension(0, 10)));
    tablePanel2.add(filterButton);

    tablePanel.add(tablePane);
    tablePanel.add(tablePanel2);

    buttonPanel.add(updateButton);

    //fill ComboBoxes
    conn = DriverManager.getConnection(ORACLE_CONNECTION, USERNAME, PASSWORD);
    Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

    ResultSet groupResultSet = stmt.executeQuery(ALL_GROUPS_QUERY);

    while(groupResultSet.next())
    {
      String group = groupResultSet.getString("GROUPNAME");
      groupsDropDown.addItem(group);
    }

    groupsDropDown.setSelectedIndex(-1);
    groupField.setText("");

    ResultSet companiesResultSet = stmt.executeQuery(ALL_COMPANIES_QUERY);

    while(companiesResultSet.next())
    {
      String company = companiesResultSet.getString("COMPANYNAME");
      companiesDropDown.addItem(company);
    }

    companiesDropDown.setSelectedIndex(-1);
    companyField.setText("");

    //add items to modifyPanel
    c.fill = GridBagConstraints.HORIZONTAL;
    c.insets = new Insets(10, 150, 5, 0); //no padding on right for labels
    c.gridx = 0;
    c.gridy = 0;
    modifyPanel.add(firstNameLabel, c);

    c.gridx = 0;
    c.gridy = 1;
    modifyPanel.add(lastNameLabel, c);

    c.gridx = 0;
    c.gridy = 2;
    modifyPanel.add(userIDLabel, c);

    c.gridx = 0;
    c.gridy = 3;
    modifyPanel.add(companyLabel, c);

    c.gridx = 0;
    c.gridy = 4;
    modifyPanel.add(groupLabel, c);

    c.insets = new Insets(0, 10, 5, 10); //no padding on top for fields
    c.anchor = GridBagConstraints.SOUTH;
    c.fill = GridBagConstraints.BOTH;
    c.weightx = 1;

    c.gridx = 1;
    c.gridy = 0;
    c.gridwidth = 2;
    modifyPanel.add(firstNameField, c);

    c.gridx = 1;
    c.gridy = 1;
    c.gridwidth = 2;
    modifyPanel.add(lastNameField, c);

    c.gridx = 1;
    c.gridy = 2;
    c.gridwidth = 2;
    modifyPanel.add(userIDField, c);

    c.gridx = 1;
    c.gridy = 3;
    c.gridwidth = 2;
    modifyPanel.add(companyField, c);

    c.gridx = 1;
    c.gridy = 4;
    c.gridwidth = 2;
    modifyPanel.add(groupField, c);

    c.insets = new Insets(0, 10, 5, 80); //padding for dropdowns
    c.gridx = 4;
    c.gridy = 3;
    modifyPanel.add(companiesDropDown, c);

    c.gridx = 4;
    c.gridy = 4;
    modifyPanel.add(groupsDropDown, c);

    //add JPanels to frame
    adminFrame.add(tablePanel, BorderLayout.NORTH);
    adminFrame.add(modifyPanel, BorderLayout.CENTER);
    adminFrame.add(buttonPanel, BorderLayout.SOUTH);   

    final TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tblModel);
    resultTable.setRowSorter(sorter);

    //create listener for filterButton
    filterButton.addActionListener(new ActionListener()
    {
      //pass filter text to Listener
      public void actionPerformed(ActionEvent e)
      {
        String text = filterTextField.getText();

        if (text.length() == 0)
          sorter.setRowFilter(null);
        else
        {
          try
          {
            //make filter case-insensitive
            sorter.setRowFilter(RowFilter.regexFilter("(?i)" + text));
          }
          catch (PatternSyntaxException pse)
          {
            JOptionPane.showMessageDialog(null, "Bad regex pattern",
                "Bad regex pattern", JOptionPane.ERROR_MESSAGE);
          }
        }
      }
    });

    pack();

    //dispose of window when user quits application
    //(do not want to close application)
    adminFrame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    adminFrame.setSize(800, 600);
    adminFrame.setVisible(true);
    adminFrame.setLocationRelativeTo(null);
    adminFrame.setResizable(false);

    //ensure database is closed when user quits application
    adminFrame.addWindowListener(new WindowAdapter()
    {
      //disconnect from database and exit when window has closed
      public void windowClosed(WindowEvent event)
      {
        tblModel.disconnectFromDatabase();
        System.exit(0);
      }
    });
  }
  catch (SQLException sqlException)
  {
    JOptionPane.showMessageDialog(null, sqlException.getMessage(),
        "Database error", JOptionPane.ERROR_MESSAGE);
    tblModel.disconnectFromDatabase();
    System.exit(1); //terminate application
  }
}

private class RowListener implements ListSelectionListener
{
  @Override
  public void valueChanged(ListSelectionEvent event)
  {
    if(!event.getValueIsAdjusting())
    {
      int row = resultTable.getSelectedRow();
      if(row == -1) //no row found
        JOptionPane.showMessageDialog(adminFrame, "Selected row not found in filtered set",
            null, JOptionPane.WARNING_MESSAGE);
      else
      {
        firstNameField.setText(resultTable.getValueAt(row, resultTable.getColumn("FIRSTNAME").getModelIndex()).toString());
        lastNameField.setText(resultTable.getValueAt(row, resultTable.getColumn("LASTNAME").getModelIndex()).toString());
        userIDField.setText(resultTable.getValueAt(row, resultTable.getColumn("EMPLOYEENO").getModelIndex()).toString());
        companyField.setText(resultTable.getValueAt(row, resultTable.getColumn("COMPANYNAME").getModelIndex()).toString());
        groupField.setText(resultTable.getValueAt(row, resultTable.getColumn("GROUPNAME").getModelIndex()).toString());
      }
    }
  }
};

//refreshes the window after an update
//by clearing out all fields
//and resetting dropdown menus
public void refreshScreen()
{
  firstNameField.setText("");
  lastNameField.setText("");
  userIDField.setText("");
  companyField.setText("");
  groupField.setText("");

  groupsDropDown.setSelectedIndex(-1);
  companiesDropDown.setSelectedIndex(-1);

}

//get methods for text fields
public String getFirstName()
{
  return firstNameField.getText();
}

public String getLastName()
{
  return lastNameField.getText();
}

public String getUserID()
{
  return userIDField.getText();
}

public String getCompany()
{
  return companyField.getText();
}

public String getGroup()
{
  return groupField.getText();
}

}//end class EmpInOutBoard

I noticed other similar posts using abstracttablemodel have used Lists or some other type of collection to modify the underlying result set. Is this something I need with my application as well or am I missing something very basic here?

Thank in advance.

Probius
  • 79
  • 10
  • You don't change the value of `numberOfRows`, which means the `TableModel` thinks nothing has (really) changed – MadProgrammer Jun 12 '17 at 22:01
  • I'd also discourage the user of `tableDataChanged` (when removing removes), as this will require the `JTable` to revalidate the `TableColumnModel`, `TableRowSorter` and the `ListSelectionModel`, along with all the rows, which is not a small amount of work – MadProgrammer Jun 12 '17 at 22:03
  • I'd suggest making use of `ResultSet#last()` and `ResultSet@#getRow()` to get the row count – MadProgrammer Jun 12 '17 at 22:06
  • @MadProgrammer I tried the same functions you mentioned in your 3rd comment to update `numberOfRows` (same as I did in the `removeRow()` method) but it changed nothing. I agree with you on avoiding the use of `fireTableDataChanged` but when I tried `fireTableRows(Inserted/Deleted)` the view did not update. I have a feeling there's something I'm not understanding especially when I compare my implementation to other users/posts/forums. – Probius Jun 13 '17 at 21:16
  • I'd make sure that the row was actually insert into the result set. Did the row count change after the insert? Is the model you're updating the same model attached to the table? – MadProgrammer Jun 13 '17 at 22:08
  • @MadProgrammer Looks like the row did not get inserted into the resultSet (I updated `numberofRows` the same as I did in the `deleteRow` method). As for the model, I believe it's the same model (listed as variable `tblModel` in the code). How would I test this? – Probius Jun 14 '17 at 16:22
  • @MadProgrammer I noticed that I was getting an `exhausted ResultSet` error when scrolled to the bottom of the table. – Probius Jun 26 '17 at 21:25
  • @MadProgrammer I'm no longer getting the `exhausted resultSet` error message. I've added to print statements, before & after the insert and noticed that `numberOfRows` does not change. This is strange as when I check the database, the row is clearly there (it also shows in the table when I reload the program). Secondly, I used `fireTableRowsInserted(numberOfRows - 1, numberOfRows -1)` and saw no change in the table. – Probius Jul 05 '17 at 18:36

0 Answers0