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
存储在List
,ResultSetMetaData
用于列名。 作为一个更抽象的替代方案,请参阅Apache CommonsDbUtils
,它使用类文字作为运行时类型标记和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); } }