AbstractTableModel GUI显示问题

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

在此处输入图像描述

有时它显示这样

http://imageshack.com/i/p3gBDt9Ej

我不知道为什么会这样。 第二个问题是当我们从表中选择任何行并单击DeleteSelectedRow按钮时它会删除该行。 此按钮在GUI2类中有一个ActionListener 。 但我想要的是我删除行时自动更新表。 我怎样才能做到这一点?

第一个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); } } 

连接器类

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

第二个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

 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> worker = new SwingWorker<Boolean, List>() { @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); } } 

因为数据库访问本质上是异步的,所以您肯定希望在后台检索行以避免阻塞事件派发线程 ; SwingWorker使这相对容易。 获取doInBackground()publish()临时结果的实现中的行,并将它们添加到process()实现中的表模型中。 此处显示了一个完整的示例,概述了随之而来的好处。 该示例循环遍历文件,但您可以替换ResultSet操作。

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

tableData.add()推迟到process()

关注自定义TableModel与其包含的SwingWorker之间的交互,以下完整示例创建一个具有N行的测试数据库,并显示一个JTable显示该表的查询结果。 尤其是,

  • JDBCModel扩展了AbstractTableModel 。 为简单起见,模型的data存储在ListResultSetMetaData用于列名。 作为一个更抽象的替代方案,请参阅Apache Commons DbUtils ,它使用类文字作为运行时类型标记ResultSetMetaData来安全地创建行数据的实例。

  • JDBCModel将行检索委托给私有JDBCWorker ; 它在从ResultSet检索的每一行上调用publish() ; 因为process()在EDT上运行,所以worker可以使用fireTableRowsInserted()优化代表父模型触发的表模型事件的数量。

  • 同样,您的delete()应该驻留在JDBCModel ,而不是GUI中; 从数据库中成功删除行并从data删除后,应该使用fireTableRowsDeleted()

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

  • 使用setProgress()和此处显示的PropertyChangeListener来显示进度; done()时的JOptionPane可能是多余的。

  • 重写getPreferredScrollableViewportSize()以自定义包含JScrollPane的表的大小。

  • 避免与常见API名称冲突的类名,例如TableModel

  • 此处检查在视图中实现实时过滤的变体。

图片

 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 https://stackoverflow.com/a/34742409/230513 * @see https://stackoverflow.com/a/24762078/230513 */ 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 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, Row> { @Override protected List 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 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); } }