AbstractTableModel GUI 显示问题 [英] AbstractTableModel GUI display issue
问题描述
我正在为数据库制作一个GUI
项目,有两个类用于GUI
.连接器类用于从用户凭据进行连接.如果凭据正确,则从 AbstractTableModel
中获取所有数据.当程序第一次运行时,GUI
有一个按钮,我们点击它,它会获取底层 TableModel
中的所有数据.但我面临两个问题.首先在 GUI2
类中,有时会这样打开.
有时会这样显示
我不知道为什么会这样.第二个问题是当我们从表中选择任何行并单击 DeleteSelectedRow
按钮时,它会删除该行.这个按钮在 GUI2
类中有一个 ActionListener
.但我想要的是当行被删除时我自动更新表.我该怎么做?
第一个GUI
public class Gui extends JFrame {私有静态连接器 conni;私人连接 conn = null;私人 JButton bt;私人 JPanel 面板;公共桂(){超级(框架");面板 = 新 JPanel();bt = new JButton("连接到数据库'世界'");panel.add(bt);bt.addActionListener(new ActionListener() {@覆盖public void actionPerformed(ActionEvent e) {conn = conni.Connector();如果(连接!= null){处置();新的 Gui2(conn);} 别的 {System.out.println("返回false");}}});添加(面板);盒();setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setLocationRelativeTo(null);设置可见(真);}}
连接器类
公共类连接器{私有静态连接 conn = null;公共静态连接连接器(){String data = "jdbc:mysql://localhost/world";字符串用户 = "root";String pass = "嘟";尝试 {conn = DriverManager.getConnection(data, user, pass);} 捕获(异常 e){JOptionPane.showMessageDialog(null, e.getMessage());}如果(连接!= null){System.out.println("连接成功");返回连接;} 别的 {返回连接;}}}
第二个GUI2
public class Gui2 extends JFrame {私人声明状态=空;私有结果集 rs = null;私有 JButton bt,删除;私有 JTextField 文本;私人 JPanel 面板;私有 GridBagLayout 布局;私有 GridBagConstraints 约束;公共 Gui2(连接连接){布局 = 新的 GridBagLayout();约束 = 新的 GridBagConstraints();面板 = 新 JPanel();panel.setLayout(布局);text = new JTextField(15);bt = new JButton("提交查询");delete = new JButton("删除选定行");约束.插入=新插入(5、2、5、10);Constraints.gridy = 0;//第 0 行Constraints.gridx = 0;//第 0 列//TextField 在 JPanel 上添加给定的约束panel.add(文本,约束);约束.gridx++;panel.add(删除,约束);约束.gridx++;panel.add(bt, 约束);//北边框布局添加(面板,BorderLayout.NORTH);尝试 {状态 = 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);添加(范围,BorderLayout.CENTER);table.setModel(new TableModel(rs));delete.addActionListener(new ActionListener() {@覆盖public void actionPerformed(ActionEvent e) {int rowIndex = table.getSelectedRow();对象 columnIndexValue = table.getModel().getValueAt(rowIndex, 0);String columnName = table.getModel().getColumnName(0);String query = "delete from world.city" + " where " + columnName + "=" + columnIndexValue;尝试 {PreparedStatement pre = conn.prepareStatement(query);pre.executeUpdate();JOptionPane.showMessageDialog(null, "行删除成功");} 捕获(异常 e1){JOptionPane.showMessageDialog(null, e1.getMessage());}}});设置大小(817、538);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setLocationRelativeTo(null);设置可见(真);}}
Tablemodel
类
public class TableModel extends AbstractTableModel {私人列表列标题;私有列表表数据;私人列表 rowData;私有整数总列;公共表模型(结果集 rs){尝试 {结果集元数据元 = rs.getMetaData();totalcolumn = meta.getColumnCount();ColumnHeader = new ArrayList(totalcolumn);tableData = new ArrayList();for (int i = 1; i <= totalcolumn; i++) {ColumnHeader.add(meta.getColumnName(i));}} 捕获(异常 e){JOptionPane.showMessageDialog(null, e.getMessage());}SwingWorker>worker = new SwingWorker>() {@覆盖受保护的布尔 doInBackground() 抛出异常 {而(rs.next()){rowData = new ArrayList(totalcolumn);for (int i = 1; i <= totalcolumn; i++) {rowData.add(rs.getObject(i));}发布(行数据);}返回真;}@覆盖受保护的无效进程(列出块){tableData.add(chunks);}@覆盖受保护的无效完成(){尝试 {布尔状态 = get();JOptionPane.showMessageDialog(null, "任务完成");} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}};worker.execute();}//构造函数结束@覆盖公共 int getColumnCount() {返回 ColumnHeader.size();}公共字符串 getColumnName(int columnIndex) {返回(字符串)ColumnHeader.get(columnIndex);}@覆盖公共 int getRowCount() {返回 tableData.size();}@覆盖公共对象 getValueAt(int rowIndex, int columnIndex) {List rowData2 = (List) tableData.get(rowIndex);返回 rowData2.get(columnIndex);}}
因为数据库访问本质上是异步的,所以您肯定希望在后台检索行以避免阻塞
import java.awt.Dimension;导入 java.awt.EventQueue;导入 java.sql.Connection;导入 java.sql.DriverManager;导入 java.sql.PreparedStatement;导入 java.sql.ResultSet;导入 java.sql.ResultSetMetaData;导入 java.sql.SQLException;导入 java.sql.Statement;导入 java.util.ArrayList;导入 java.util.List;导入 java.util.Random;导入 javax.swing.JFrame;导入 javax.swing.JScrollPane;导入 javax.swing.JTable;导入 javax.swing.SwingWorker;导入 javax.swing.table.AbstractTableModel;/*** @see https://stackoverflow.com/a/34742409/230513* @see https://stackoverflow.com/a/24762078/230513*/公共类 WorkerTest {私有静态最终 int N = 1_000;私有静态最终字符串 URL = "jdbc:h2:mem:test";私有静态最终随机 r = 新随机();私人无效显示(){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){@覆盖公共维度 getPreferredScrollableViewportSize() {返回新维度(320, 240);}}));f.pack();f.setLocationRelativeTo(null);f.setVisible(true);}私有静态类行{整数标识;字符串名称;}私有静态类 JDBCModel 扩展 AbstractTableModel {私有最终列表<行>数据 = 新的 ArrayList();私有结果集 rs = null;私有 ResultSetMetaData 元;公共 JDBCModel(连接连接,字符串查询){尝试 {语句 s = conn.createStatement();rs = s.executeQuery(查询);元 = rs.getMetaData();JDBCWorker worker = new JDBCWorker();worker.execute();} catch (SQLException e) {e.printStackTrace(System.err);}}@覆盖公共 int getRowCount() {返回数据大小();}@覆盖公共 int getColumnCount() {尝试 {返回 meta.getColumnCount();} catch (SQLException e) {e.printStackTrace(System.err);}返回0;}@覆盖公共对象 getValueAt(int rowIndex, int colIndex) {行行 = data.get(rowIndex);开关(colIndex){案例0:返回行.ID;情况1:返回行名称;}返回空;}@覆盖公共字符串 getColumnName(int colIndex) {尝试 {返回 meta.getColumnName(colIndex + 1);} catch (SQLException e) {e.printStackTrace(System.err);}返回空;}私有类 JDBCWorker 扩展了 SwingWorker, Row>{@覆盖受保护的列表<行>doInBackground() {尝试 {而(rs.next()){行 r = 新行();r.ID = rs.getInt(1);r.name = rs.getString(2);发布(r);}} catch (SQLException e) {e.printStackTrace(System.err);}返回数据;}@覆盖受保护的无效进程(列表<行>块){int n = getRowCount();for(行行:块){数据添加(行);}fireTableRowsInserted(n, n + chunks.size());}}}私有静态无效 createTestDatabase(int n) {连接 conn = getConnection();尝试 {语句 st = conn.createStatement();st.execute("创建表 city(id integer, name varchar2)");PreparedStatement ps = conn.prepareStatement(插入城市值(?,?)");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.执行();}} catch (SQLException ex) {ex.printStackTrace(System.err);}}私有静态连接 getConnection() {尝试 {返回 DriverManager.getConnection(URL, "", "");} catch (SQLException e) {e.printStackTrace(System.err);}返回空;}公共静态无效主(字符串 [] args){EventQueue.invokeLater(new WorkerTest()::display);}}
I'm making a GUI
Project for database there are two classes which are for GUI's
. And connector class is used to connect from user credentials. If credentials are correct than it fetch all data in the from of AbstractTableModel
. When program run first GUI
has a button in which we click it and it fetch all data in underlying TableModel
. But i'm facing two problems. First in GUI2
class, sometimes it open like this.
and sometimes it show like this
I don't know why it's happening. And second problem is when we select any row from table and click on DeleteSelectedRow
button it delete the row. This button has a ActionListener
in GUI2
class. But what i want is i automatic update the table when row has been deleted. How can i do that?
class for first 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);
}
}
Connector 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 for second 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);
}
}
Because database access is inherently asynchronous, you'll surely want to retrieve rows in the background to avoid blocking the 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. The example loops through a file, but you can substitute your ResultSet
operations.
while (rs.next()) {
//collect row data
publish(rowData);
}
Defer tableData.add()
to your implementation of process()
.
Focusing on the interaction between the custom TableModel
and its contained SwingWorker
, the following complete example creates a test database having N
rows and displays a JTable
showing the results of a query of that table. In particular,
JDBCModel
extendsAbstractTableModel
. For simplicity, the model'sdata
is stored in aList<Row>
, andResultSetMetaData
is used for the column names. As a more abstract alternative, see Apache CommonsDbUtils
, which uses Class Literals as Runtime-Type Tokens andResultSetMetaData
to safely create instances of row data.JDBCModel
delegates row retrieval to a privateJDBCWorker
; it invokespublish()
on each row retrieved from theResultSet
; becauseprocess()
runs on the EDT, the worker can optimize the number of table model events that it fires on behalf of the parent model usingfireTableRowsInserted()
.Similarly, your implementation of
delete()
should reside inJDBCModel
, not the GUI; it shouldfireTableRowsDeleted()
after the row is successfully deleted from the database and removed fromdata
.Add
Thread.sleep()
to the worker's background loop to see the effect of artificially increasing latency.Use
setProgress()
and aPropertyChangeListener
, shown here, to display progress; aJOptionPane
whendone()
may be superfluous.Override
getPreferredScrollableViewportSize()
to customize the size of the table's enclosingJScrollPane
.Avoid class names, e.g.
TableModel
, that collide with common API names.A variation that implements live filtering in the view is examined here.
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<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);
}
}
这篇关于AbstractTableModel GUI 显示问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!