NetBeans 8中Java DB报错:当前光标位置操作无效(图书归还功能)
问题描述
我在NetBeans 8中开发图书馆图书归还按钮功能:图书逾期归还时向借阅者收取费用,在规定日期内归还则不收费,但运行时出现报错:Invalid operation at current cursor position.
相关实现代码:
private void btnReturnActionPerformed(java.awt.event.ActionEvent evt) { String rCustomer = customerName.getText(); String rDate = Date.getText(); String rBook = bookName.getText(); String rStatus = "Available"; if (rCustomer.equals("") || rDate.equals("") || rBook.equals("")) { JOptionPane.showMessageDialog(this, "Please Enter Required Details"); } else { try { String returnDateString = Rset2.getString("RETURNDATE"); Date returnDate = new SimpleDateFormat("yyyy-MM-dd").parse(returnDateString); Date currentDate = new Date(); long balance = ( currentDate.getTime() - returnDate.getTime() )/ (24 * 60 * 60 * 1000); if (balance < 0){ String updateSQL = "UPDATE ADMIN1.INVENTORY SET STATUS = '" + rStatus + "', DATE = NULL WHERE BOOKNAME = '" + rBook + "'"; state.executeUpdate(updateSQL); JOptionPane.showMessageDialog(this, "Book Returned Successfully"); LibraryFrame lib = new LibraryFrame(); lib.setVisible(true); dispose(); }else { int choice = JOptionPane.showConfirmDialog(this, "Please pay the fee of: " + balance, "Alert", JOptionPane.OK_CANCEL_OPTION); if (choice == JOptionPane.OK_OPTION) { try { String deleteSQL = "DELETE FROM ADMIN1.BORROWEDBOOK WHERE CustomerName = '" + rCustomer + "' AND BookName = '"+ rBook + "'"; Rset2 = state.executeQuery(deleteSQL); JOptionPane.showMessageDialog(this, "Payment Successfull"); } catch( SQLException err ){ System.out.println(err.getMessage()); } } } conn.commit(); Rset.close(); Rset2.close(); state.close(); JOptionPane.showMessageDialog(this, "Book Borrowed Successfully"); LibraryFrame lib = new LibraryFrame(); lib.setVisible(true); dispose(); } catch (SQLException err) { System.out.println(err.getMessage()); } catch (ParseException ex) { Logger.getLogger(returnBook.class.getName()).log(Level.SEVERE, null, ex); } } }
错误原因及修复方案
1. ResultSet游标未定位
报错核心原因是直接调用Rset2.getString("RETURNDATE")时,ResultSet游标未指向有效数据行。ResultSet默认初始位置在第一行之前,必须先调用next()方法移动游标到有效行才能读取数据。
修改代码:
// 原代码:String returnDateString = Rset2.getString("RETURNDATE"); if (Rset2.next()) { String returnDateString = Rset2.getString("RETURNDATE"); // 后续日期解析、计算逻辑 } else { JOptionPane.showMessageDialog(this, "未找到该图书的借阅记录"); return; // 终止后续逻辑 }
2. DELETE语句执行方法错误
执行DELETE、UPDATE这类更新数据库的语句,必须使用executeUpdate()方法,而非executeQuery()(后者仅用于查询并返回ResultSet)。用executeQuery()执行DELETE会返回空ResultSet,后续操作会触发游标位置错误。
同时,原代码字符串拼接SQL存在SQL注入风险,建议改用PreparedStatement:
// 原逾期处理中的DELETE代码替换为: if (choice == JOptionPane.OK_OPTION) { try { String deleteSQL = "DELETE FROM ADMIN1.BORROWEDBOOK WHERE CustomerName = ? AND BookName = ?"; PreparedStatement pstmt = conn.prepareStatement(deleteSQL); pstmt.setString(1, rCustomer); pstmt.setString(2, rBook); int rowsDeleted = pstmt.executeUpdate(); if (rowsDeleted > 0) { JOptionPane.showMessageDialog(this, "缴费成功"); // 同步更新库存状态 String updateSQL = "UPDATE ADMIN1.INVENTORY SET STATUS = ?, DATE = NULL WHERE BOOKNAME = ?"; PreparedStatement updatePstmt = conn.prepareStatement(updateSQL); updatePstmt.setString(1, rStatus); updatePstmt.setString(2, rBook); updatePstmt.executeUpdate(); updatePstmt.close(); } else { JOptionPane.showMessageDialog(this, "未找到对应借阅记录"); } pstmt.close(); } catch( SQLException err ){ System.out.println(err.getMessage()); } }
3. 代码逻辑混乱修复
- 原代码中按时归还分支已执行
dispose()关闭窗口,后续的事务提交、资源关闭和重复页面跳转代码不会执行,且存在资源泄漏风险。建议用try-with-resources自动关闭数据库资源,或把资源关闭逻辑放在finally块中。 - 最后错误的提示文本
Book Borrowed Successfully改为图书归还完成,避免语义错误。 - 移除重复的页面跳转代码,确保逻辑流程清晰。
4. 其他细节优化
- 变量名
Date是Java自带类名,作为文本框变量名易混淆,建议改为returnDateTxt这类有意义的名字。 - 日期计算时,当前时间包含时分秒可能导致逾期天数计算偏差,建议转为纯日期比较:
// 替换原日期计算逻辑: SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); sdf.setTimeZone(TimeZone.getDefault()); // 设置时区避免偏差 Date returnDate = sdf.parse(returnDateString); Date currentDate = sdf.parse(sdf.format(new Date())); // 忽略时分秒,仅保留日期部分 long overdueDays = (currentDate.getTime() - returnDate.getTime()) / (24 * 60 * 60 * 1000);
修复后完整代码示例
private void btnReturnActionPerformed(java.awt.event.ActionEvent evt) { String rCustomer = customerName.getText(); String rDate = returnDateTxt.getText(); // 重命名Date文本框为returnDateTxt String rBook = bookName.getText(); String rStatus = "Available"; if (rCustomer.trim().isEmpty() || rDate.trim().isEmpty() || rBook.trim().isEmpty()) { JOptionPane.showMessageDialog(this, "请填写必要信息"); return; } try (Statement state = conn.createStatement()) { // try-with-resources自动关闭资源 // 先查询借阅记录的归还日期 String querySQL = "SELECT RETURNDATE FROM ADMIN1.BORROWEDBOOK WHERE CustomerName = ? AND BookName = ?"; try (PreparedStatement queryPstmt = conn.prepareStatement(querySQL)) { queryPstmt.setString(1, rCustomer); queryPstmt.setString(2, rBook); try (ResultSet Rset2 = queryPstmt.executeQuery()) { if (!Rset2.next()) { JOptionPane.showMessageDialog(this, "未找到该图书的借阅记录"); return; } String returnDateString = Rset2.getString("RETURNDATE"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); sdf.setTimeZone(TimeZone.getDefault()); Date returnDate = sdf.parse(returnDateString); Date currentDate = sdf.parse(sdf.format(new Date())); long overdueDays = (currentDate.getTime() - returnDate.getTime()) / (24 * 60 * 60 * 1000); if (overdueDays < 0) { // 按时归还,更新库存并删除借阅记录 String updateSQL = "UPDATE ADMIN1.INVENTORY SET STATUS = ?, DATE = NULL WHERE BOOKNAME = ?"; try (PreparedStatement updatePstmt = conn.prepareStatement(updateSQL)) { updatePstmt.setString(1, rStatus); updatePstmt.setString(2, rBook); updatePstmt.executeUpdate(); } String deleteSQL = "DELETE FROM ADMIN1.BORROWEDBOOK WHERE CustomerName = ? AND BookName = ?"; try (PreparedStatement deletePstmt = conn.prepareStatement(deleteSQL)) { deletePstmt.setString(1, rCustomer); deletePstmt.setString(2, rBook); deletePstmt.executeUpdate(); } JOptionPane.showMessageDialog(this, "图书归还成功"); } else { // 逾期,提示缴费 int choice = JOptionPane.showConfirmDialog(this, "请支付逾期费用:" + overdueDays + "元", "提示", JOptionPane.OK_CANCEL_OPTION); if (choice == JOptionPane.OK_OPTION) { // 删除借阅记录并更新库存 String deleteSQL = "DELETE FROM ADMIN1.BORROWEDBOOK WHERE CustomerName = ? AND BookName = ?"; try (PreparedStatement deletePstmt = conn.prepareStatement(deleteSQL)) { deletePstmt.setString(1, rCustomer); deletePstmt.setString(2, rBook); int rowsDeleted = deletePstmt.executeUpdate(); if (rowsDeleted > 0) { String updateSQL = "UPDATE ADMIN1.INVENTORY SET STATUS = ?, DATE = NULL WHERE BOOKNAME = ?"; try (PreparedStatement updatePstmt = conn.prepareStatement(updateSQL)) { updatePstmt.setString(1, rStatus); updatePstmt.setString(2, rBook); updatePstmt.executeUpdate(); } JOptionPane.showMessageDialog(this, "缴费成功,图书已归还"); } else { JOptionPane.showMessageDialog(this, "未找到对应借阅记录"); return; } } } else { JOptionPane.showMessageDialog(this, "已取消归还操作"); return; } } } } conn.commit(); JOptionPane.showMessageDialog(this, "图书归还完成"); LibraryFrame lib = new LibraryFrame(); lib.setVisible(true); dispose(); } catch (SQLException err) { System.out.println(err.getMessage()); try { conn.rollback(); // 出错回滚事务 } catch (SQLException e) { e.printStackTrace(); } } catch (ParseException ex) { Logger.getLogger(returnBook.class.getName()).log(Level.SEVERE, null, ex); JOptionPane.showMessageDialog(this, "日期格式错误,请检查输入"); } }
内容的提问来源于stack exchange,提问作者Ley Cabs
相关产品推荐
相关产品推荐

