Java Swing中JTable无法实时显示MySQL新增数据的问题
问题分析与解决方案
问题原因
- ResultSet游标位置错误:在
DatabaseConnector(String sqlQuery, JTable table)构造方法中,调用resultSet.next()判断结果集是否为空时,会将游标从初始的before first位置移动到第一行。而DbUtils.resultSetToTableModel(resultSet)会从当前游标位置读取数据,导致第一行数据被跳过(包括新增的记录)。 - TableRowSorter未同步:StaffLibraryTable初始化时给JTable绑定了TableRowSorter,新增数据后重新设置TableModel时,旧的排序器仍绑定原model,新model的变化无法同步到界面。
- 代码设计缺陷:DatabaseConnector的构造方法承担了连接、增删改、查询绑定等多重职责,逻辑混乱;同时使用
String.format拼接SQL存在严重的SQL注入风险。
解决方案
1. 修复ResultSet游标问题
修改DatabaseConnector中绑定JTable的构造方法,使用可滚动ResultSet并避免提前移动游标:
public DatabaseConnector(String sqlQuery, JTable table) { try{ Class.forName("com.mysql.cj.jdbc.Driver"); connection = DriverManager.getConnection(url, uname, password); // 创建可滚动的ResultSet,避免游标移动导致数据丢失 statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); resultSet = statement.executeQuery(sqlQuery); // 安全判断结果集是否为空,不移动游标 if (!resultSet.isBeforeFirst()) { System.out.println("result set is empty"); } tableModel = DbUtils.resultSetToTableModel(resultSet); table.setModel(tableModel); } catch (ClassNotFoundException | SQLException e) { System.out.println("error in DatabaseConnector(String sqlQuery, JTable table) - first part"); System.out.println(e.getMessage()); } finally { try { // 确保所有资源都被关闭 if (resultSet != null) resultSet.close(); if (statement != null) statement.close(); if (connection != null) connection.close(); } catch (SQLException e) { System.out.println("error in DatabaseConnector(String sqlQuery, JTable table) - second part"); System.out.println(e.getMessage()); } } }
2. 重新绑定TableRowSorter
在新增数据后,重新创建并绑定排序器到新的TableModel:
addButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // ... 原有输入校验逻辑 ... DatabaseConnector databaseConnector = new DatabaseConnector(sqlQuery); JOptionPane.showMessageDialog(null, "Dodano pracownika"); // 清空输入框... // 刷新表格 new DatabaseConnector("select * from stafflist", staffTable); // 重新绑定行排序器 TableRowSorter<TableModel> newRowSorter = new TableRowSorter<>(staffTable.getModel()); staffTable.setRowSorter(newRowSorter); // ... 原有逻辑 ... } });
3. 优化代码,解决SQL注入与职责混乱
替换SQL拼接为PreparedStatement
在DatabaseConnector中新增安全的插入方法:
public void insertStaff(String name, String surname, String address, String postcode, String city, String login) throws SQLException { String sql = "INSERT INTO stafflist(`name`, `surname`, `address`, `postcode`, `city`,`login`) VALUES (?, ?, ?, ?, ?, ?)"; try (PreparedStatement pstmt = connection.prepareStatement(sql)) { pstmt.setString(1, name); pstmt.setString(2, surname); pstmt.setString(3, address); pstmt.setString(4, postcode); pstmt.setString(5, city); pstmt.setString(6, login); pstmt.executeUpdate(); } }
在StaffLibraryTable中调用该方法替代SQL拼接:
try { DatabaseConnector dbConnector = new DatabaseConnector(); dbConnector.insertStaff(name, surname, address, postcode, city, login); JOptionPane.showMessageDialog(null, "Dodano pracownika"); // 清空输入框与刷新表格逻辑... } catch (SQLException ex) { JOptionPane.showMessageDialog(null, "添加失败:" + ex.getMessage()); ex.printStackTrace(); }
拆分DatabaseConnector职责
将构造方法仅用于初始化连接,把增删改、查询等操作拆分为独立方法:
- 新增
executeUpdate(String sql, Object... params)处理增删改 - 新增
getTableModel(String sql)获取查询结果的TableModel - 新增
refreshJTable(JTable table, String sql)专门负责刷新JTable
4. 修复loginChecker的SQL注入问题
static boolean loginChecker(String providedLogin) { boolean wynik = true; String sql = "SELECT * FROM loggintable WHERE login = ?"; try (DatabaseConnector dbConnector = new DatabaseConnector(); PreparedStatement pstmt = dbConnector.connection.prepareStatement(sql)) { pstmt.setString(1, providedLogin); try (ResultSet rs = pstmt.executeQuery()) { if (rs.next()) { wynik = false; } } } catch (SQLException e) { System.out.println("error in LoginChecker"); System.out.println(e.getMessage()); } return wynik; }
内容的提问来源于stack exchange,提问作者Przemysław Poskrobko
相关产品推荐
相关产品推荐

