user12119790
user12119790

Reputation: 7

JTable Not Updated After Deleting Row

I'm trying to update the rows of a JTable (or data?) after I perform a delete on a row, so when another row is selected the selected row can be edited. Currently deleting the row will shift the rows below the deleted row up, but if that row number is selected and an attempt to edit is made it returns (JOptionPane) as if nothing is selected. It seems to not "refresh" the data(?).

Here is an SCCE, hopefully someone can run it and point out the issue. I tried to scale it down as much as possible allow for easy copy/paste:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import javax.swing.table.*;
import java.util.*;

public class JTableTest4 extends JFrame implements ActionListener {
    private JPanel dbOutputPanel = new JPanel();
    private JTable dbOutputTable;
    private JScrollPane dbOutputTableSP = new JScrollPane(dbOutputTable);
    private JButton deleteButton = new JButton("Delete");
    private JButton editButton = new JButton("Edit");
    private JButton searchButton = new JButton("Search");
    private JPanel south = new JPanel();
    protected HashMap<Integer, Student> studentMap;
    protected int studentIndex;

    public JTableTest4() {
        super("Student Database");
        populateMap();
        south.add(deleteButton);
        south.add(editButton);
        south.add(searchButton);
        dbOutputTable = new JTable();
        dbOutputTable.setFillsViewportHeight(true);
        dbOutputTable.setPreferredScrollableViewportSize(new Dimension(450, 250));
        dbOutputTableSP = new JScrollPane(dbOutputTable);
        dbOutputPanel.add(dbOutputTableSP);
        searchButton.addActionListener(this);
        editButton.addActionListener(this);
        deleteButton.addActionListener(this);

        this.add(new JLabel("Welcome to " + getTitle(), JLabel.CENTER), BorderLayout.NORTH);
        this.add(dbOutputPanel, BorderLayout.CENTER);
        this.add(south, BorderLayout.SOUTH);
        this.setSize(600, 500);
        this.setResizable(false);
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        Object source = event.getSource();
        int selectedRow = dbOutputTable.getSelectedRow();
        if (source == searchButton) {
            displayTable();
        } else if (source == editButton) {
            Student student = studentMap.get(selectedRow);
            displayTable();
            if (student != null) {
                System.out.println("\n" + student.toString() + "\n");
            } else {
                JOptionPane.showMessageDialog(this, "Please select a student to edit",
                        getTitle(), JOptionPane.INFORMATION_MESSAGE);
            }
        }
        else if (source == deleteButton) {
            System.out.println("Row count: " + ((DefaultTableModel) dbOutputTable.getModel()).getRowCount());
            System.out.println("Selected Row: " + (selectedRow));
            String[] options = {"Yes", "No"};
            int result = JOptionPane.showOptionDialog(null, "Delete student from database?",
                    "Delete Student", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
                    null, options, options[1]);
            if (result == JOptionPane.YES_OPTION) {
                studentMap.remove(selectedRow);
                ((DefaultTableModel) dbOutputTable.getModel()).fireTableRowsDeleted(selectedRow, selectedRow);//not the issue
                displayTable();
            } else if (result == JOptionPane.NO_OPTION) {
            } 
        }
        displayTable();
    }

    public void populateMap() {
        studentMap = new HashMap<>();
        Student s1 = new Student("232", "john", "thomas", 22);
        Student s2 = new Student("56", "bob", "doe", 23);
        Student s3 = new Student("678", "sally", "smith", 24);
        Student s4 = new Student("32", "chris", "johnson", 21);
        Student s5 = new Student("12", "dsfg", "sgfsdg", 22);
        studentMap.put(0, s1);
        studentMap.put(1, s2);
        studentMap.put(2, s3);
        studentMap.put(3, s4);
        studentMap.put(4, s5);
    }
    
    public TableModel toTableModel() {
        System.out.println("Using toTableModel");
        ArrayList<Student> studentList = new ArrayList<>();
        for (Integer key : studentMap.keySet()) {
            Student newStudent = (Student) studentMap.get(key);
            studentList.add(newStudent);
        }
        DefaultTableModel tmodel = new DefaultTableModel(
                new Object[]{"Id", "First", "Last", "Age"}, 0);
        for (int i = 0; i < studentList.size(); i++) {
            Student newStudent = studentList.get(i);
            tmodel.addRow(new Object[] {newStudent.getStudentId(),
            newStudent.getStudentFirstName(), 
            newStudent.getStudentLastName(), 
            newStudent.getStudentAge()});
        }
        return tmodel;
    }

    public void displayTable() {
        dbOutputTable.setModel(toTableModel());
//        dbOutputTable.revalidate();
    }

    public static void main(String[] args) throws Exception {
        JTableTest4 test = new JTableTest4();
        UIManager.setLookAndFeel(new NimbusLookAndFeel());
    }
}

class Student {
    private String studentId;
    private String studentFirstName;
    private String studentLastName;
    private int studentAge;
    public Student(String id, String first, String last, int age) {
        this.studentId = id;
        this.studentFirstName = first;
        this.studentLastName = last;
        this.studentAge = age;
    }
    public String getStudentId() {return studentId;}
    public void setStudentId(String studentId) {this.studentId = studentId;}
    public String getStudentFirstName() {return studentFirstName;}
    public void setStudentFirstName(String studentFirstName) {this.studentFirstName = studentFirstName;}
    public String getStudentLastName() {return studentLastName;}
    public void setStudentLastName(String studentLastName) {this.studentLastName = studentLastName;}
    public int getStudentAge() {return studentAge;}
    public void setStudentAge(int studentAge) {this.studentAge = studentAge;}
    @Override
    public String toString() {
        String st = "_id : " + this.studentId + "\n";
        st += "First Name : " + this.studentFirstName + "\n";
        st += "Last Name : " + this.studentLastName + "\n";
        st += "Age : " + this.studentAge + "\n";
        return st;
    }
}

I've seen this question on here in various forms all a bit different, usually the same suggestions, and I have tried a few. Any help you can provide would be greatly appreciated. Thanks!

Upvotes: 0

Views: 642

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347334

Your "data model" and the TableModel need to be kept closer in sync. Your displayTable method is worrying and is demonstrating a basic mis-understanding of how the model, view and data should be managed.

Personally, I'd use a AbstractTable which better married the existing studentList with the TableModel instead of using two different models, but that's probably getting beyond the scope of the issue.

Below is slightly modified version of your code, which uses DefaultTableModel#removeRow to update the JTable

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;

public class JTableTest4 extends JFrame implements ActionListener {

    private JPanel dbOutputPanel = new JPanel();
    private JTable dbOutputTable;
    private JScrollPane dbOutputTableSP = new JScrollPane(dbOutputTable);
    private JButton deleteButton = new JButton("Delete");
    private JButton editButton = new JButton("Edit");
    private JButton searchButton = new JButton("Search");
    private JPanel south = new JPanel();
    protected HashMap<Integer, Student> studentMap;
    protected int studentIndex;

    public JTableTest4() {
        super("Student Database");
        populateMap();
        south.add(deleteButton);
        south.add(editButton);
        south.add(searchButton);
        dbOutputTable = new JTable();
        dbOutputTable.setModel(toTableModel());
        dbOutputTable.setFillsViewportHeight(true);
        dbOutputTable.setPreferredScrollableViewportSize(new Dimension(450, 250));
        dbOutputTableSP = new JScrollPane(dbOutputTable);
        dbOutputPanel.add(dbOutputTableSP);
        searchButton.addActionListener(this);
        editButton.addActionListener(this);
        deleteButton.addActionListener(this);

        this.add(new JLabel("Welcome to " + getTitle(), JLabel.CENTER), BorderLayout.NORTH);
        this.add(dbOutputPanel, BorderLayout.CENTER);
        this.add(south, BorderLayout.SOUTH);
        this.setSize(600, 500);
        this.setResizable(false);
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        Object source = event.getSource();
        int selectedRow = dbOutputTable.getSelectedRow();
        if (source == searchButton) {
            //displayTable();
        } else if (source == editButton) {
            Student student = studentMap.get(selectedRow);
            //displayTable();
            if (student != null) {
                System.out.println("\n" + student.toString() + "\n");
            } else {
                JOptionPane.showMessageDialog(this, "Please select a student to edit",
                        getTitle(), JOptionPane.INFORMATION_MESSAGE);
            }
        } else if (source == deleteButton) {
            System.out.println("Row count: " + ((DefaultTableModel) dbOutputTable.getModel()).getRowCount());
            System.out.println("Selected Row: " + (selectedRow));
            String[] options = {"Yes", "No"};
            int result = JOptionPane.showOptionDialog(null, "Delete student from database?",
                    "Delete Student", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
                    null, options, options[1]);
            if (result == JOptionPane.YES_OPTION) {
                studentMap.remove(selectedRow);
                ((DefaultTableModel)dbOutputTable.getModel()).removeRow(selectedRow);
//                ((DefaultTableModel) dbOutputTable.getModel()).fireTableRowsDeleted(selectedRow, selectedRow);//not the issue
//                displayTable();
            } else if (result == JOptionPane.NO_OPTION) {
            }
        }
        //displayTable();
    }

    public void populateMap() {
        studentMap = new HashMap<>();
        Student s1 = new Student("232", "john", "thomas", 22);
        Student s2 = new Student("56", "bob", "doe", 23);
        Student s3 = new Student("678", "sally", "smith", 24);
        Student s4 = new Student("32", "chris", "johnson", 21);
        Student s5 = new Student("12", "dsfg", "sgfsdg", 22);
        studentMap.put(0, s1);
        studentMap.put(1, s2);
        studentMap.put(2, s3);
        studentMap.put(3, s4);
        studentMap.put(4, s5);
    }

    public TableModel toTableModel() {
        System.out.println("Using toTableModel");
        ArrayList<Student> studentList = new ArrayList<>();
        for (Integer key : studentMap.keySet()) {
            Student newStudent = (Student) studentMap.get(key);
            studentList.add(newStudent);
        }
        DefaultTableModel tmodel = new DefaultTableModel(
                new Object[]{"Id", "First", "Last", "Age"}, 0);
        for (int i = 0; i < studentList.size(); i++) {
            Student newStudent = studentList.get(i);
            tmodel.addRow(new Object[]{newStudent.getStudentId(),
                newStudent.getStudentFirstName(),
                newStudent.getStudentLastName(),
                newStudent.getStudentAge()});
        }
        return tmodel;
    }

    public void displayTable() {
        dbOutputTable.setModel(toTableModel());
//        dbOutputTable.revalidate();
    }

    public static void main(String[] args) throws Exception {
        JTableTest4 test = new JTableTest4();
        UIManager.setLookAndFeel(new NimbusLookAndFeel());
    }
}

class Student {

    private String studentId;
    private String studentFirstName;
    private String studentLastName;
    private int studentAge;

    public Student(String id, String first, String last, int age) {
        this.studentId = id;
        this.studentFirstName = first;
        this.studentLastName = last;
        this.studentAge = age;
    }

    public String getStudentId() {
        return studentId;
    }

    public void setStudentId(String studentId) {
        this.studentId = studentId;
    }

    public String getStudentFirstName() {
        return studentFirstName;
    }

    public void setStudentFirstName(String studentFirstName) {
        this.studentFirstName = studentFirstName;
    }

    public String getStudentLastName() {
        return studentLastName;
    }

    public void setStudentLastName(String studentLastName) {
        this.studentLastName = studentLastName;
    }

    public int getStudentAge() {
        return studentAge;
    }

    public void setStudentAge(int studentAge) {
        this.studentAge = studentAge;
    }

    @Override
    public String toString() {
        String st = "_id : " + this.studentId + "\n";
        st += "First Name : " + this.studentFirstName + "\n";
        st += "Last Name : " + this.studentLastName + "\n";
        st += "Age : " + this.studentAge + "\n";
        return st;
    }
}

A more "advanced" implementation

This example makes use of a custom TableModel (named StudentTableModel) which wraps the TableModel around the base data model and coordinates the updates.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import javax.swing.table.AbstractTableModel;

public class JTableTest4 extends JFrame implements ActionListener {

    private JPanel dbOutputPanel = new JPanel();
    private JTable dbOutputTable;
    private JScrollPane dbOutputTableSP = new JScrollPane(dbOutputTable);
    private JButton deleteButton = new JButton("Delete");
    private JButton editButton = new JButton("Edit");
    private JButton searchButton = new JButton("Search");
    private JPanel south = new JPanel();

    public JTableTest4() {
        super("Student Database");
        south.add(deleteButton);
        south.add(editButton);
        south.add(searchButton);
        dbOutputTable = new JTable();
        dbOutputTable.setModel(makeModel());
        dbOutputTable.setFillsViewportHeight(true);
        dbOutputTable.setPreferredScrollableViewportSize(new Dimension(450, 250));
        dbOutputTableSP = new JScrollPane(dbOutputTable);
        dbOutputPanel.add(dbOutputTableSP);
        searchButton.addActionListener(this);
        editButton.addActionListener(this);
        deleteButton.addActionListener(this);

        this.add(new JLabel("Welcome to " + getTitle(), JLabel.CENTER), BorderLayout.NORTH);
        this.add(dbOutputPanel, BorderLayout.CENTER);
        this.add(south, BorderLayout.SOUTH);
        this.setSize(600, 500);
        this.setResizable(false);
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        Object source = event.getSource();
        int selectedRow = dbOutputTable.convertRowIndexToModel(dbOutputTable.getSelectedRow());

        if (selectedRow < 0) {
            return;
        }

        if (source == searchButton) {
            //displayTable();
        } else if (source == editButton) {
//            Student student = studentMap.get(selectedRow);
//            //displayTable();
//            if (student != null) {
//                System.out.println("\n" + student.toString() + "\n");
//            } else {
//                JOptionPane.showMessageDialog(this, "Please select a student to edit",
//                        getTitle(), JOptionPane.INFORMATION_MESSAGE);
//            }
        } else if (source == deleteButton) {
            StudentTableModel model = (StudentTableModel)dbOutputTable.getModel();
            model.removeRow(selectedRow);
//            System.out.println("Row count: " + ((DefaultTableModel) dbOutputTable.getModel()).getRowCount());
//            System.out.println("Selected Row: " + (selectedRow));
//            String[] options = {"Yes", "No"};
//            int result = JOptionPane.showOptionDialog(null, "Delete student from database?",
//                    "Delete Student", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
//                    null, options, options[1]);
//            if (result == JOptionPane.YES_OPTION) {
//                studentMap.remove(selectedRow);
//                ((DefaultTableModel) dbOutputTable.getModel()).removeRow(selectedRow);
//                //                ((DefaultTableModel) dbOutputTable.getModel()).fireTableRowsDeleted(selectedRow, selectedRow);//not the issue
//                //                displayTable();
//            } else if (result == JOptionPane.NO_OPTION) {
//            }
        }
        //displayTable();
    }

    public StudentTableModel makeModel() {
        System.out.println("Using toTableModel");
        ArrayList<Student> studentList = new ArrayList<>();
        studentList.add(new Student("232", "john", "thomas", 22));
        studentList.add(new Student("56", "bob", "doe", 23));
        studentList.add(new Student("678", "sally", "smith", 24));
        studentList.add(new Student("32", "chris", "johnson", 21));
        studentList.add(new Student("12", "dsfg", "sgfsdg", 22));
        return new StudentTableModel(studentList);
    }

    public static void main(String[] args) throws Exception {
        JTableTest4 test = new JTableTest4();
        UIManager.setLookAndFeel(new NimbusLookAndFeel());
    }

    class Student {

        private String studentId;
        private String studentFirstName;
        private String studentLastName;
        private int studentAge;

        public Student(String id, String first, String last, int age) {
            this.studentId = id;
            this.studentFirstName = first;
            this.studentLastName = last;
            this.studentAge = age;
        }

        public String getStudentId() {
            return studentId;
        }

        public void setStudentId(String studentId) {
            this.studentId = studentId;
        }

        public String getStudentFirstName() {
            return studentFirstName;
        }

        public void setStudentFirstName(String studentFirstName) {
            this.studentFirstName = studentFirstName;
        }

        public String getStudentLastName() {
            return studentLastName;
        }

        public void setStudentLastName(String studentLastName) {
            this.studentLastName = studentLastName;
        }

        public int getStudentAge() {
            return studentAge;
        }

        public void setStudentAge(int studentAge) {
            this.studentAge = studentAge;
        }

        @Override
        public String toString() {
            String st = "_id : " + this.studentId + "\n";
            st += "First Name : " + this.studentFirstName + "\n";
            st += "Last Name : " + this.studentLastName + "\n";
            st += "Age : " + this.studentAge + "\n";
            return st;
        }
    }

    class StudentTableModel extends AbstractTableModel {

        List<Student> studentList;

        public StudentTableModel(List<Student> studentList) {
            this.studentList = studentList;
        }

        @Override
        public int getRowCount() {
            return studentList.size();
        }

        @Override
        public int getColumnCount() {
            return 4;
        }

        @Override
        public String getColumnName(int column) {
            switch (column) {
                case 0: return "ID";
                case 1: return "First Name";
                case 2: return "Last Name";
                case 3: return "Age";
            }
            return "??";
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            Student student = studentList.get(rowIndex);
            switch (columnIndex) {
                case 0: return student.studentId;
                case 1: return student.studentFirstName;
                case 2: return student.studentLastName;
                case 3: return student.studentAge;
            }
            return null;
        }

        // This is a more advance topic then I want to get into here
        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return false;
        }

        public Student studentAt(int row) {
            return studentList.get(row);
        }

        public void removeRow(int index) {
            studentList.remove(index);
            fireTableRowsDeleted(index, index);
        }

    }
}

Take a closer look at How to Use Tables for more details

Upvotes: 2

Related Questions