AbstractTableModel GUI 显示问题

AbstractTableModel GUI display issue

我正在为数据库制作一个 GUI 项目,其中有两个 class 用于 GUI's。连接器 class 用于从用户凭据进行连接。如果凭据正确,则它会获取 AbstractTableModel 中的所有数据。当程序 运行 第一个 GUI 有一个按钮时,我们单击它并获取底层 TableModel 中的所有数据。但我面临两个问题。第一次在GUI2class,有时会这样打开


有时会这样显示

我不知道为什么会这样。第二个问题是,当我们 select 来自 table 的任何行并单击 DeleteSelectedRow 按钮时,它会删除该行。此按钮在 GUI2 class 中有一个 ActionListener。但我想要的是在删除行时自动更新 table 。我该怎么做?

class 第一个 GUI

public class Gui extends JFrame {
    private static Connector conni;
    private Connection conn = null;
    private JButton bt;
    private JPanel panel;

    public Gui() {
        super("Frame");
        panel = new JPanel();
        bt = new JButton("Connect to Database 'World'");
        panel.add(bt);
        bt.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {

                conn = conni.Connector();

                if (conn != null) {
                    dispose();
                    new Gui2(conn);

                } else {
                    System.out.println("Return false");

                }

            }

        });
        add(panel);

        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);

    }

}

连接器class

public class Connector {

    private static Connection conn = null;

    public static Connection Connector() {
        String data = "jdbc:mysql://localhost/world";
        String user = "root";
        String pass = "toot";
        try {
            conn = DriverManager.getConnection(data, user, pass);

        } catch (Exception e) {

            JOptionPane.showMessageDialog(null, e.getMessage());

        }
        if (conn != null) {

            System.out.println("Connection Suceess");
            return conn;

        } else {

            return conn;

        }

    }

}

class 第二个 GUI2

public class Gui2 extends JFrame {
    private Statement state = null;
    private ResultSet rs = null;

    private JButton bt, delete;
    private JTextField text;
    private JPanel panel;
    private GridBagLayout layout;
    private GridBagConstraints constraints;

    public Gui2(Connection conn) {
        layout = new GridBagLayout();
        constraints = new GridBagConstraints();
        panel = new JPanel();
        panel.setLayout(layout);

        text = new JTextField(15);
        bt = new JButton("Submit Query");
        delete = new JButton("Delete Selected Row");
        constraints.insets = new Insets(5, 2, 5, 10);
        constraints.gridy = 0;// row 0
        constraints.gridx = 0;// column 0
        // TextField add on JPanel with given constraints
        panel.add(text, constraints);
        constraints.gridx++;
        panel.add(delete, constraints);
        constraints.gridx++;
        panel.add(bt, constraints);

        // North BorderLayout
        add(panel, BorderLayout.NORTH);

        try {
            state = conn.createStatement();
            rs = state.executeQuery("select * from city");
        } catch (SQLException e) {

            JOptionPane.showMessageDialog(null, e.getMessage());
        }

        JTable table = new JTable();
        JScrollPane spane = new JScrollPane(table);

        add(spane, BorderLayout.CENTER);

        table.setModel(new TableModel(rs));

        delete.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {

                int rowIndex = table.getSelectedRow();

                Object columnIndexValue = table.getModel().getValueAt(rowIndex, 0);

                String columnName = table.getModel().getColumnName(0);

                String query = "delete from world.city" + " where " + columnName + "=" + columnIndexValue;

                try {

                    PreparedStatement pre = conn.prepareStatement(query);

                    pre.executeUpdate();

                    JOptionPane.showMessageDialog(null, "Row Deleted Successfully");
                } catch (Exception e1) {
                    JOptionPane.showMessageDialog(null, e1.getMessage());
                }

            }

        });

        setSize(817, 538);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);

    }

}

Tablemodel Class

public class TableModel extends AbstractTableModel {

    private List ColumnHeader;
    private List tableData;
    private List rowData;

    private int totalcolumn;

    public TableModel(ResultSet rs) {

        try {

            ResultSetMetaData meta = rs.getMetaData();

            totalcolumn = meta.getColumnCount();

            ColumnHeader = new ArrayList(totalcolumn);

            tableData = new ArrayList();

            for (int i = 1; i <= totalcolumn; i++) {
                ColumnHeader.add(meta.getColumnName(i));

            }
        } catch (Exception e) {

            JOptionPane.showMessageDialog(null, e.getMessage());
        }

        SwingWorker<Boolean, List<Object>> worker = new SwingWorker<Boolean, List<Object>>() {

            @Override
            protected Boolean doInBackground() throws Exception {

                while (rs.next()) {

                    rowData = new ArrayList(totalcolumn);
                    for (int i = 1; i <= totalcolumn; i++) {
                        rowData.add(rs.getObject(i));
                    }
                    publish(rowData);


                }

                return true;

            }

            @Override
            protected void process(List chunks) {
                tableData.add(chunks);

            }

            @Override
            protected void done() {
                try {
                    Boolean status = get();
                    JOptionPane.showMessageDialog(null, "Task is DONE");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }

        };

        worker.execute();
    }// constructor end



    @Override
    public int getColumnCount() {

        return ColumnHeader.size();
    }

    public String getColumnName(int columnIndex) {
        return (String) ColumnHeader.get(columnIndex);

    }

    @Override
    public int getRowCount() {

        return tableData.size();
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {

        List rowData2 = (List) tableData.get(rowIndex);

        return rowData2.get(columnIndex);
    }

}

因为数据库访问本质上是异步的,您肯定希望在后台检索行以避免阻塞 event dispatch thread; SwingWorker makes this relatively easy. Fetch rows in your implementation of doInBackground(), publish() interim results, and add them to the table model in your implementation of process(). A complete example that outlines the attendant benefits is shown here。该示例循环遍历文件,但您可以替换 ResultSet 操作。

while (rs.next()) {
    //collect row data
    publish(rowData);
}

推迟 tableData.add() 到您实施 process()

关注自定义 TableModel 与其包含的 SwingWorker 之间的交互,以下 complete example 创建一个具有 N 行的测试数据库并显示 JTable 显示 table 的查询结果。特别是,

  • JDBCModel 扩展 AbstractTableModel。为简单起见,模型的 data 存储在 List<Row> 中,而 ResultSetMetaData 用于列名。作为更抽象的替代方案,请参阅 Apache Commons DbUtils, which uses Class Literals as Runtime-Type TokensResultSetMetaData 以安全地创建行数据实例。

  • JDBCModel 将行检索委托给私有 JDBCWorker;它在从 ResultSet 检索到的每一行上调用 publish();因为 process() 在 EDT 上运行,worker 可以使用 fireTableRowsInserted().

    [=87= 优化它代表父模型触发的 table 模型事件的数量]
  • 同样,您对 delete() 的实现应该驻留在 JDBCModel 中,而不是 GUI;它应该 fireTableRowsDeleted() 在该行成功从数据库中删除并从 data.

  • 中删除后
  • Thread.sleep()添加到worker的后台循环,看看人为增加延迟的效果。

  • 使用setProgress()和一个PropertyChangeListener,显示here,来显示进度; a JOptionPane when done() 可能是多余的。

  • 覆盖 getPreferredScrollableViewportSize() 以自定义 table 的大小 JScrollPane.

  • 避免使用 class 个名字,例如TableModel,与常见的 API 名称冲突。

  • 实现直播的变体filtering in the view is examined

import java.awt.Dimension;
import java.awt.EventQueue;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel;

/**
 * @see 
 * @see 
 */
public class WorkerTest {

    private static final int N = 1_000;
    private static final String URL = "jdbc:h2:mem:test";
    private static final Random r = new Random();

    private void display() {
        JFrame f = new JFrame("WorkerTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        createTestDatabase(N);
        JDBCModel model = new JDBCModel(getConnection(), "select * from city");
        f.add(new JScrollPane(new JTable(model) {

            @Override
            public Dimension getPreferredScrollableViewportSize() {
                return new Dimension(320, 240);
            }
        }));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static class Row {
        int ID;
        String name;
    }

    private static class JDBCModel extends AbstractTableModel {

        private final List<Row> data = new ArrayList<>();
        private ResultSet rs = null;
        private ResultSetMetaData meta;

        public JDBCModel(Connection conn, String query) {
            try {
                Statement s = conn.createStatement();
                rs = s.executeQuery(query);
                meta = rs.getMetaData();
                JDBCWorker worker = new JDBCWorker();
                worker.execute();
            } catch (SQLException e) {
                e.printStackTrace(System.err);
            }
        }

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

        @Override
        public int getColumnCount() {
            try {
                return meta.getColumnCount();
            } catch (SQLException e) {
                e.printStackTrace(System.err);
            }
            return 0;
        }

        @Override
        public Object getValueAt(int rowIndex, int colIndex) {
            Row row = data.get(rowIndex);
            switch (colIndex) {
                case 0:
                    return row.ID;
                case 1:
                    return row.name;
            }
            return null;
        }

        @Override
        public String getColumnName(int colIndex) {
            try {
                return meta.getColumnName(colIndex + 1);
            } catch (SQLException e) {
                e.printStackTrace(System.err);
            }
            return null;
        }

        private class JDBCWorker extends SwingWorker<List<Row>, Row> {

            @Override
            protected List<Row> doInBackground() {
                try {
                    while (rs.next()) {
                        Row r = new Row();
                        r.ID = rs.getInt(1);
                        r.name = rs.getString(2);
                        publish(r);
                    }
                } catch (SQLException e) {
                    e.printStackTrace(System.err);
                }
                return data;
            }

            @Override
            protected void process(List<Row> chunks) {
                int n = getRowCount();
                for (Row row : chunks) {
                    data.add(row);
                }
                fireTableRowsInserted(n, n + chunks.size());
            }
        }
    }

    private static void createTestDatabase(int n) {
        Connection conn = getConnection();
        try {
            Statement st = conn.createStatement();
            st.execute("create table city(id integer, name varchar2)");
            PreparedStatement ps = conn.prepareStatement(
                "insert into city values (?, ?)");
            for (int i = 0; i < n; i++) {
                ps.setInt(1, i);
                ps.setString(2, (char) ('A' + r.nextInt(26))
                    + String.valueOf(r.nextInt(1_000_000)));
                ps.execute();
            }
        } catch (SQLException ex) {
            ex.printStackTrace(System.err);
        }
    }

    private static Connection getConnection() {
        try {
            return DriverManager.getConnection(URL, "", "");
        } catch (SQLException e) {
            e.printStackTrace(System.err);
        }
        return null;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new WorkerTest()::display);
    }
}