Проблема с отображением графического интерфейса AbstractTableModel
Я создаю проект GUI
для базы данных, есть два classа для GUI's
. Класс соединителя используется для подключения к учетным данным пользователя. Если учетные данные верны, то они извлекают все данные из метода AbstractTableModel
. Когда в программе запускается первый GUI
есть кнопка, в которой мы нажимаем ее, и она извлекает все данные в базовой TableModel
. Но я столкнулся с двумя проблемами. Сначала в classе GUI2
, иногда он открывается так.
и иногда это выглядит так
- Многослойное стекло GlassPane в корневом контейнере
- Что делает super.paintComponent (g)?
- Как установить значок в JFrame
- Выбор файла в панели с Swing
- JComboBox Выбор слушателя?
http://imageshack.com/i/p3gBDt9Ej
Я не знаю, почему это происходит. И вторая проблема – когда мы выбираем любую строку из таблицы и DeleteSelectedRow
кнопку DeleteSelectedRow
, она удаляет строку. Эта кнопка имеет GUI2
classе GUI2
. Но я хочу, чтобы я автоматически обновлял таблицу, когда строка была удалена. Как я могу это сделать?
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); } }
Класс соединителя
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
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); } }
- Eclipse WindowBuilder, перекрывающиеся JPanels
- Выделяет subString в TableCell (s), который используется для JTable filetering
- drag and drop jlabel вокруг экрана
- анимация JPanel (слайд) с таймером
- Как захватить событие щелчка кнопки закрытия JFrame?
- Поместите JTable в JTree
- Swing GroupLayout: изменение размеров и ограничение размеров компонентов
- Трудности, связанные с пониманием механизма рендеринга JTable и JTree
Поскольку доступ к базе данных по сути является асинхронным, вы обязательно захотите получить строки в фоновом режиме, чтобы избежать блокировки streamа отправки событий ; 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
, который используетDbUtils
литералы как токены типа Runtime иResultSetMetaData
для безопасного создания экземпляров данных строки. -
JDBCModel
делегирует поиск строк частномуJDBCWorker
; он вызываетpublish()
для каждой строки, полученной изResultSet
; посколькуprocess()
работает на EDT, рабочий может оптимизировать количество событий модели таблицы, которые он запускает от имени родительской модели, используяfireTableRowsInserted()
. -
Аналогично, ваша реализация
delete()
должна находиться вJDBCModel
, а не в графическом интерфейсе; он долженfireTableRowsDeleted()
после того, как строка будет успешно удалена из базы данных и удалена изdata
. -
Добавьте
Thread.sleep()
в фоновый цикл работника, чтобы увидеть эффект искусственно увеличивающей латентности. -
Используйте
setProgress()
иPropertyChangeListener
, показанные здесь , для отображения прогресса;JOptionPane
когдаdone()
может быть излишним. -
Переопределите
getPreferredScrollableViewportSize()
чтобы настроить размер охватывающейJScrollPane
. -
Избегайте имен classов, например
TableModel
, которые сталкиваются с общими именами API. -
Здесь рассматривается вариант, который реализует фильтрацию в реальном времени в представлении.
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); } }