简介:本资源是吉林大学《数据库应用程序开发》课程的完整实验报告,面向计算机专业本科生及Java数据库开发初学者,聚焦JDBC编程实战能力培养。报告覆盖8个递进式实验任务:从JDBC连接配置、基础查询(labstaff.java)与事务更新(labupdate.java),到GUI界面改造、多模式数据插入、空值统一显示、异常对话框提示,再到结果集动态编辑,系统训练数据库交互、SQL执行、ResultSet处理、PreparedStatement参数化、事务控制及排错调试等核心技能。压缩包为1个DOC文档(1.76MB),内容含详细调试过程、典型错误截图与解决方案、SQL权限配置、溢出问题分析(如salary*2导致类型溢出)、自动提交关闭引发的修改不生效等真实排障经验。目前已有898人学习下载,是理解Java与DB2深度集成、掌握企业级数据库应用开发规范的优质实践材料。
1. 这不是一份普通实验报告:它是一套可复用的 JDBC + DB2 实战调试手册,专治“连不上”“查不到”“改不掉”“报错看不懂”四大顽疾
你是不是也经历过:写完 JDBC 代码,getConnection()报No suitable driver;执行executeQuery()却提示JLU.TEMPL is an undefined name;executeUpdate()返回 1 却发现数据库纹丝不动;输入一个数字就炸出java.lang.ArithmeticException: Overflow——而日志里只有一行SQLState: 22003,像黑匣子一样沉默?这份来自某高校数据库课程的实验报告,表面是学生交的作业,实则是八轮真实压测下淬炼出的 JDBC-DB2 联调避坑图谱。它不讲 JDBC 接口定义,不画 UML 类图,而是用 8 个递进任务,把「驱动加载时机」「模式名硬编码陷阱」「事务提交漏写」「空值渲染逻辑」「GUI 异常捕获粒度」全部钉死在具体代码行、错误截图和 db2 命令反馈上。适合正在用 Java 接 DB2 的后端开发者、准备毕业设计需连真实数据库的学生、以及被遗留系统 JDBC 模块折磨得想重写的维护工程师——只要你面对的是 DB2 LUW(Linux/Unix/Windows)环境下的 Java 数据库交互,这份材料就能直接抄作业、改参数、过编译、跑通路。
2. JDBC 连接与元数据校准:从No suitable driver到JLU.TEMPL的精准定位
JDBC 不是“写完 URL 就能连”,尤其在 DB2 环境下,连接失败往往不是网络问题,而是驱动、URL、模式名三者未对齐。本节不罗列 JDBC 规范,只拆解实验中真实发生的两处典型故障及其根因修复。
2.1 驱动加载必须早于 getConnection(),且类名不能凭记忆手写
实验任务 3 和任务 8 明确要求调用Class.forName("COM.ibm.db2.jdbc.app.DB2Driver")。这不是历史遗留摆设,而是 DB2 JDBC 驱动注册机制决定的:该类的静态块会向DriverManager注册自身。若跳过此步或类名拼错(如写成com.ibm.db2.jcc.DB2Driver),DriverManager.getConnection()将抛出java.sql.SQLException: No suitable driver found for jdbc:db2:sample。
// ✅ 正确:显式加载 DB2 应用程序驱动(适用于本地嵌入式或客户端直连) try { Class.forName("COM.ibm.db2.jdbc.app.DB2Driver"); } catch (ClassNotFoundException e) { System.err.println("DB2 Driver not found in classpath!"); e.printStackTrace(); return; } // ⚠️ 注意:此处不是 JCC 驱动(com.ibm.db2.jcc.*),后者用于 Type 4 连接(纯 Java),URL 格式完全不同 Connection conn = DriverManager.getConnection( "jdbc:db2:sample", // 注意:这是旧式 URL,无 host/port,依赖本地 db2profile "db2admin", "db2admin" );提示:
COM.ibm.db2.jdbc.app.DB2Driver对应的是 DB2 的 Type 2 驱动(需本地 DB2 客户端安装),其 URL 格式为jdbc:db2:<dbname>;而现代应用更常用 Type 4 驱动com.ibm.db2.jcc.DB2Driver,URL 为jdbc:db2://host:port/dbname。实验环境基于旧版 DB2 客户端,故必须用前者。混淆两者是新手翻车第一高发点。
2.2 模式名(Schema)不是可选项,而是 SQL 解析的强制前缀
任务 1 中错误DB2ADMIN.TEMPL is an undefined name表面是表不存在,实则是对象解析路径错误。DB2 默认按当前用户模式(CURRENT SCHEMA)查找对象。当用户db2admin执行SELECT * FROM TEMPL,DB2 先查DB2ADMIN.TEMPL,找不到即报错。而实际表在JLU模式下(JLU.TEMPL)。解决方案不是改用户,而是在 SQL 中显式指定模式名:
-- ❌ 错误:依赖默认 schema,db2admin 下无 TEMPL 表 SELECT * FROM TEMPL; -- ✅ 正确:显式声明 schema,确保解析到 JLU 模式 SELECT * FROM JLU.TEMPL;Java 代码中需同步修改:
// 原错误代码(隐含 DB2ADMIN 模式) String sql = "SELECT * FROM TEMPL"; // 修正后(显式指定 JLU 模式) String sql = "SELECT * FROM JLU.TEMPL";参数说明:
JLU是数据库中已创建的模式(Schema),通常由 DBA 创建并授权给应用用户。实验中db2admin用户被授予了JLU模式下的SELECT权限,但未将其设为默认模式。因此,所有 SQL 必须带JLU.前缀,否则DriverManager无法定位对象。这是 DB2 与 MySQL/PostgreSQL 的关键差异——后者默认 schema 为public或用户名,而 DB2 严格区分。
2.3 权限不是“给用户就行”,而是“给模式+对象+操作”的三维授权
任务 1 的调试过程提到:“登录 JLU 用户,授予 db2admin 的访问权限”。这背后是 DB2 的 GRANT 语法细节:
-- 登录 JLU 用户(拥有 JLU 模式所有权) CONNECT TO sample USER jlu USING jlu_password; -- 授予 db2admin 在 JLU 模式下对 TEMPL 表的 SELECT 权限 GRANT SELECT ON TABLE JLU.TEMPL TO USER db2admin; -- 若需 INSERT/UPDATE/DELETE,需分别授权 GRANT INSERT, UPDATE, DELETE ON TABLE JLU.TEMPL TO USER db2admin;注意:仅执行
GRANT SELECT ON TEMPL TO USER db2admin会失败,因为 DB2 要求必须指定 schema。权限粒度精确到“模式.表.操作”,缺一不可。实验中若跳过此步,后续所有查询均会报SQLCODE: -551, SQLSTATE: 42501(权限不足)。
3. ResultSet 处理与事务控制:为什么executeUpdate()返回 1 却没生效?
任务 2 和任务 3 的核心差异在于:前者只读,后者要写。但“能执行”不等于“已持久化”——DB2 的事务隔离机制让很多开发者栽在setAutoCommit(false)后忘记commit()。本节用实验中的三次输入(15/16/20)反推事务行为,给出可验证的调试链路。
3.1 ResultSet 遍历必须用next()驱动游标,且需判空处理
任务 2 要求用rs.next()移动到结果集首行。这是 JDBC 的强制约定:ResultSet初始化时游标位于第一行之前,必须调用next()才能访问数据。常见错误是直接getString(1)导致SQLException: Invalid cursor state。
Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT NAME, JOB, SALARY FROM JLU.STAFF WHERE ID = 10"); // ✅ 正确:先调 next(),再取值,且需判断是否查到数据 if (rs.next()) { String name = rs.getString("NAME"); // 推荐用列名,非序号 String job = rs.getString("JOB"); BigDecimal salary = rs.getBigDecimal("SALARY"); // 用 BigDecimal 避免 float 精度丢失 System.out.printf("Name: %s, Job: %s, Salary: %s%n", name, job, salary); } else { System.out.println("No record found for ID = 10"); } rs.close(); stmt.close();逻辑说明:
rs.next()返回boolean,true表示成功移到下一行并有数据,false表示已到末尾。实验中输入 ID=16 时返回 0 行,正是因为JLU.STAFF表中无该 ID 记录——rs.next()直接返回false,后续getString()不会被执行。这是排查“查不到数据”的第一检查点。
3.2 事务提交是两阶段动作:setAutoCommit(false)+commit()缺一不可
任务 3 的关键发现:“输入 20 或 30,即使执行成功,数据库也没有发生变化……将 false 改为 true,就可以进行修改了”。这揭示了 DB2 事务的底层逻辑:
| 操作 | setAutoCommit(true) | setAutoCommit(false) |
|---|---|---|
executeUpdate()执行后 | 立即持久化到数据库 | 仅写入事务日志,未落盘 |
是否需要手动commit() | 否 | 是(否则回滚) |
| 实验现象 | 输入 15/16/20 均生效 | 仅commit()后才生效 |
修正后的更新代码:
conn.setAutoCommit(false); // 关闭自动提交 PreparedStatement pstmt = conn.prepareStatement( "UPDATE JLU.STAFF SET SALARY = SALARY * ? WHERE ID = ?" ); pstmt.setDouble(1, 1.01); // 使用 1.01 避免溢出(见任务 3 分析) pstmt.setInt(2, 20); int rows = pstmt.executeUpdate(); System.out.println("Updated " + rows + " row(s)"); // ✅ 关键:必须显式 commit,否则事务回滚 conn.commit(); System.out.println("Transaction committed."); // ⚠️ 若发生异常,需 rollback } catch (SQLException e) { conn.rollback(); // 回滚未提交的更改 System.err.println("Transaction rolled back: " + e.getMessage()); }参数说明:
setDouble(1, 1.01)中1是 SQL 中第一个?参数位置,1.01是乘数。实验中用2导致溢出,因SALARY列为DECIMAL(10,2),*2后超出精度范围;1.01在安全范围内。这是数值型字段更新的典型边界校验。
3.3 溢出错误不是 Java 异常,而是 SQLState 22003 的数据库级拦截
任务 3 提到“类型转换时候溢出”,并验证是SALARY*2导致。这并非 Java 的ArithmeticException,而是 DB2 在执行 SQL 时检测到数值溢出,返回SQLState: 22003(数值值超出范围)。
try { pstmt.executeUpdate(); } catch (SQLException e) { System.err.println("SQLState: " + e.getSQLState()); // 输出 22003 System.err.println("SQLCode: " + e.getErrorCode()); // 输出 -413 或类似 System.err.println("Message: " + e.getMessage()); // 如 "ARITHMETIC OVERFLOW" // ✅ 捕获并友好提示用户 if ("22003".equals(e.getSQLState())) { JOptionPane.showMessageDialog(null, "薪资调整幅度过大,请输入 1.00~1.50 之间的倍数", "数值溢出", JOptionPane.ERROR_MESSAGE); } }避坑 / 常见问题 / 排查
现象 1:executeUpdate()抛SQLException,但getSQLState()为空,getMessage()只显示“Unknown error”
原因:驱动版本不匹配或未正确加载,导致异常未被完整封装
解决:确认db2jcc4.jar(Type 4)或db2java.zip(Type 2)在 classpath,且与 DB2 服务端版本兼容(如 DB2 11.5 需用 v4.26+ 驱动)现象 2:
rs.getString("SALARY")返回null,但数据库中该字段有值
原因:SALARY是数值型(DECIMAL),getString()对 null 值安全,但若列允许 null 且值为 null,getString()返回 null;若需区分“数据库 null”和“空字符串”,应改用wasNull()
解决:String salaryStr = rs.getString("SALARY"); if (rs.wasNull()) { System.out.println("SALARY is NULL in database"); } else { System.out.println("SALARY = " + salaryStr); }现象 3:
PreparedStatement设置setInt(1, 20)后,执行executeUpdate()返回 0
原因:WHERE 条件不匹配(如ID=20不存在),或事务未提交导致其他会话不可见
解决:先用db2 connect to sample; db2 "select * from JLU.STAFF where ID=20"在命令行验证数据存在性;再检查conn.getAutoCommit()是否为true
4. GUI 化与空值渲染:用 JOptionPane 统一交互,让NULL变成“空”
任务 4 和任务 6 要求将控制台 I/O 升级为 GUI,并处理NULL值显示。这不是简单替换System.out,而是涉及 Swing 线程安全、ResultSet元数据读取、以及NULL的语义转换。本节提供可直接粘贴的JOptionPane封装模板。
4.1 GUI 输入必须用showInputDialog,且需类型校验
任务 4 要求用户输入 ID,传统Scanner在 GUI 环境失效。JOptionPane.showInputDialog是标准方案,但需处理用户取消(返回null)和非法输入(非数字):
// ✅ 安全的 ID 输入流程 String idInput = JOptionPane.showInputDialog(null, "请输入员工 ID:", "查询员工", JOptionPane.QUESTION_MESSAGE); if (idInput == null) { System.out.println("用户取消操作"); return; // 退出方法 } int empId; try { empId = Integer.parseInt(idInput.trim()); } catch (NumberFormatException e) { JOptionPane.showMessageDialog(null, "ID 必须为整数,请重新输入", "输入错误", JOptionPane.ERROR_MESSAGE); return; } // 执行查询 String sql = "SELECT NAME, JOB, SALARY FROM JLU.STAFF WHERE ID = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setInt(1, empId); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { // 渲染结果(见 4.2) } else { JOptionPane.showMessageDialog(null, "未找到 ID 为 " + empId + " 的员工", "查询结果", JOptionPane.INFORMATION_MESSAGE); }逻辑说明:
showInputDialog返回String,需trim()去除首尾空格,再parseInt。捕获NumberFormatException并弹窗提示,比让程序崩溃更符合用户体验。这是 GUI 化的第一道防线。
4.2 空值渲染:遍历 ResultSetMetaData,动态替换NULL为“空”
任务 6 要求“所有为空的列值均以‘空’的形式显示”。硬编码if (rs.getString("NAME") == null) ...不可扩展。正确做法是读取ResultSetMetaData,获取列数和类型,再逐列判断:
// ✅ 通用空值渲染函数 public static String renderNullAsEmpty(ResultSet rs, int columnIndex) throws SQLException { Object value = rs.getObject(columnIndex); return (value == null) ? "空" : value.toString(); } // 使用示例 if (rs.next()) { StringBuilder result = new StringBuilder(); ResultSetMetaData meta = rs.getMetaData(); int colCount = meta.getColumnCount(); for (int i = 1; i <= colCount; i++) { String colName = meta.getColumnName(i); String displayValue = renderNullAsEmpty(rs, i); result.append(colName).append(": ").append(displayValue).append("\n"); } JOptionPane.showMessageDialog(null, result.toString(), "员工信息", JOptionPane.INFORMATION_MESSAGE); }参数说明:
rs.getObject(i)是最安全的取值方式,兼容所有 SQL 类型;rs.getString(i)对NULL返回null,但对BLOB等二进制类型可能抛异常。renderNullAsEmpty封装了null判断,使业务代码专注逻辑而非空值处理。
4.3 多行插入的批量执行:用addBatch()+executeBatch()提升效率
任务 5 要求“多行插入”,若用循环executeUpdate(),每行一次网络往返,性能极差。DB2 支持批量插入,需启用addBatch():
// ✅ 批量插入示例(插入 3 行) String insertSql = "INSERT INTO JLU.TEMPL (ID, NAME, DEPT) VALUES (?, ?, ?)"; PreparedStatement pstmt = conn.prepareStatement(insertSql); // 添加批次 pstmt.setInt(1, 1001); pstmt.setString(2, "张三"); pstmt.setString(3, "IT"); pstmt.addBatch(); pstmt.setInt(1, 1002); pstmt.setString(2, "李四"); pstmt.setString(3, "HR"); pstmt.addBatch(); pstmt.setInt(1, 1003); pstmt.setString(2, "王五"); pstmt.setString(3, "FIN"); pstmt.addBatch(); // 执行批次 int[] results = pstmt.executeBatch(); System.out.println("批量插入完成,影响行数:" + Arrays.toString(results)); // [1,1,1] // ✅ 关键:批量操作也需 commit conn.commit();注意:
executeBatch()返回int[],每个元素是对应 SQL 的影响行数。若某条失败,会抛BatchUpdateException,需捕获并检查getUpdateCounts()获取部分成功结果。
5. 异常精细化捕获:把SQLState映射为用户可读的对话框提示
任务 7 的核心价值在于:它拒绝“e.printStackTrace()”式的粗放错误处理,而是将 DB2 的SQLState码翻译成业务语言。本节提供一张完整的SQLState映射表,并给出JOptionPane动态提示的实现。
5.1 DB2 关键 SQLState 码与业务含义对照表
| SQLState | 含义 | 用户提示文案 | 实验中触发场景 |
|---|---|---|---|
23505 | 唯一约束冲突(主键/唯一索引重复) | “ID 已存在,请输入新 ID” | 任务 5 插入重复 ID |
22003 | 数值溢出(如 DECIMAL 超限) | “数值过大,请减小输入值” | 任务 3SALARY*2 |
22007 | 日期格式错误 | “日期格式不正确,请输入 YYYY-MM-DD” | 任务 5 插入非法日期 |
23502 | NOT NULL 列插入 NULL | “姓名不能为空,请填写” | 任务 5 插入空姓名 |
42703 | 列名不存在 | “字段 'XXX' 未找到,请检查拼写” | 任务 1 写错TEMPL |
42704 | 对象(表/视图)不存在 | “表 'XXX' 不存在,请确认表名和模式” | 任务 1 未加JLU.前缀 |
42888 | 表无主键(某些操作要求) | “该表缺少主键,无法执行此操作” | 任务 8 修改任意列(需主键定位) |
提示:
SQLState是 5 位字符串,前两位是类码(23=完整性约束,22=数据异常,42=语法错误),后三位是子码。DB2 文档中可查全量映射,但日常开发掌握上表 7 个高频码已覆盖 90% 场景。
5.2 统一异常处理器:SQLException→JOptionPane
将上述映射封装为工具方法,避免每个catch块重复写if-else:
public static void handleSQLException(SQLException e, String operation) { String sqlState = e.getSQLState(); String message = e.getMessage(); String userMessage; switch (sqlState) { case "23505": userMessage = "违反唯一约束:" + operation + " 失败,ID 或名称已存在。"; break; case "22003": userMessage = "数值溢出:" + operation + " 失败,输入值过大,请减小。"; break; case "22007": userMessage = "日期格式错误:" + operation + " 失败,请使用 YYYY-MM-DD 格式。"; break; case "23502": userMessage = "必填项为空:" + operation + " 失败,请检查所有必填字段。"; break; case "42703": case "42704": userMessage = "数据库对象错误:" + operation + " 失败," + message; break; default: userMessage = "数据库操作失败:" + operation + ",错误码 " + sqlState + "。"; } JOptionPane.showMessageDialog(null, userMessage, "操作失败", JOptionPane.ERROR_MESSAGE); } // 使用示例 try { pstmt.executeUpdate(); JOptionPane.showMessageDialog(null, "操作成功!", "成功", JOptionPane.INFORMATION_MESSAGE); } catch (SQLException e) { handleSQLException(e, "更新员工薪资"); }逻辑说明:
handleSQLException接收SQLException和当前操作描述(如“更新员工薪资”),根据SQLState返回定制化提示。用户看到的是“违反唯一约束:更新员工薪资失败,ID 或名称已存在”,而非晦涩的SQLCODE=-803。这是提升系统健壮性的关键一步。
5.3 子查询插入的权限与语法双重校验
任务 5 要求“通过子查询插入”,如INSERT INTO TEMPL SELECT * FROM STAFF WHERE DEPT='IT'。这不仅是 SQL 语法问题,还涉及源表和目标表的跨模式权限:
-- ✅ 正确:明确指定源表模式 INSERT INTO JLU.TEMPL SELECT ID, NAME, DEPT FROM JLU.STAFF WHERE DEPT='IT'; -- ❌ 错误:省略模式,DB2 尝试在当前用户模式(db2admin)下找 STAFF INSERT INTO JLU.TEMPL SELECT * FROM STAFF WHERE DEPT='IT'; -- 报错:SQLSTATE=42704,STAFF not found in DB2ADMIN schemaJava 中需确保 SQL 字符串包含完整模式名:
String subQuerySql = "INSERT INTO JLU.TEMPL " + "SELECT ID, NAME, DEPT FROM JLU.STAFF WHERE DEPT = ?"; PreparedStatement pstmt = conn.prepareStatement(subQuerySql); pstmt.setString(1, "IT"); pstmt.executeUpdate();避坑 / 常见问题 / 排查
现象 1:JOptionPane.showMessageDialog在后台线程弹出,界面卡死
原因:Swing 组件非线程安全,必须在 Event Dispatch Thread (EDT) 中调用
解决:用SwingUtilities.invokeLater()包裹SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(null, "操作成功!", "成功", JOptionPane.INFORMATION_MESSAGE) );现象 2:
executeQuery()返回ResultSet,但rs.getMetaData().getColumnCount()为 0
原因:执行的是INSERT/UPDATE/DELETE语句,executeQuery()仅用于SELECT;误用会导致SQLException
解决:DML 语句用executeUpdate(),DDL 用execute()现象 3:
JOptionPane.showInputDialog输入中文,数据库存入乱码
原因:JDBC URL 未指定字符集,DB2 默认ISO-8859-1
解决:在 URL 后添加;charset=UTF-8(Type 4 驱动)或设置 JVM-Dfile.encoding=UTF-8
6. 结果集任意行列编辑:从只读ResultSet到可更新ResultSet的实战跃迁
任务 8 的终极挑战是“实现对结果集中任意行、任意列的修改操作”。这超越了PreparedStatement的参数化更新,要求直接在ResultSet上updateString()、updateRow()。但 DB2 默认ResultSet是只读的,必须显式声明可更新属性——这是 JDBC 高级特性的分水岭。
6.1 创建可更新 ResultSet:CONCUR_UPDATABLE是开关
标准createStatement()创建的ResultSet是只读的。要支持updateRow(),必须在创建Statement时指定并发模式:
// ✅ 正确:声明 CONCUR_UPDATABLE Statement stmt = conn.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE, // 支持滚动(前后移动) ResultSet.CONCUR_UPDATABLE // 支持更新 ); ResultSet rs = stmt.executeQuery("SELECT ID, NAME, SALARY FROM JLU.STAFF WHERE DEPT = 'IT'"); // 现在可以修改当前行 if (rs.next()) { rs.updateString("NAME", "新姓名"); // 修改 NAME 列 rs.updateBigDecimal("SALARY", new BigDecimal("8000.00")); // 修改 SALARY 列 rs.updateRow(); // ✅ 提交修改到数据库 System.out.println("行已更新"); }参数说明:
TYPE_SCROLL_SENSITIVE允许rs.previous()、rs.absolute(5)等滚动操作;CONCUR_UPDATABLE启用updateXxx()方法。二者缺一不可。若只设TYPE_SCROLL_INSENSITIVE,updateRow()会抛SQLException: ResultSet is not updatable。
6.2 可更新 ResultSet 的三大硬性前提
实验中若直接写rs.updateRow()却失败,大概率是以下任一条件未满足:
- SELECT 语句必须是单表查询:不能有
JOIN、UNION、GROUP BY、DISTINCT。DB2 无法定位被修改行对应的物理记录。 - SELECT 列必须包含主键或唯一索引列:DB2 需用主键定位行。若
SELECT NAME, SALARY FROM STAFF,无ID,则updateRow()失败。 - 目标表必须有主键:
JLU.STAFF表需有PRIMARY KEY(ID),否则 DB2 拒绝可更新 ResultSet。
验证表结构的 db2 命令:
db2 describe table JLU.STAFF # 查看输出中是否有 "Primary key" 行 db2 "select constname, type from syscat.tabconst where tabname='STAFF' and tabschema='JLU'" # type='P' 表示主键6.3 GUI 行编辑:用 JTable 绑定 ResultSet 的轻量级方案
任务 8 要求“GUI 界面实现任意行、任意列修改”,最佳实践是用JTable+TableModel。但为降低复杂度,提供一个基于JOptionPane的简化版:
// ✅ 简化版:用户选择行号,输入新值,直接更新 String rowInput = JOptionPane.showInputDialog("请输入要修改的行号(从 1 开始):"); if (rowInput == null) return; int targetRow = Integer.parseInt(rowInput); // 移动到指定行(需 TYPE_SCROLL_SENSITIVE) if (rs.absolute(targetRow)) { String newName = JOptionPane.showInputDialog("当前姓名:" + rs.getString("NAME") + "\n请输入新姓名:"); if (newName != null) { rs.updateString("NAME", newName.trim()); rs.updateRow(); JOptionPane.showMessageDialog(null, "第 " + targetRow + " 行姓名已更新为:" + newName); } } else { JOptionPane.showMessageDialog(null, "行号超出范围,请输入 1~" + rs.last() + " 之间的数字"); }逻辑说明:
rs.absolute(n)将游标移到第 n 行(1-based)。rs.last()返回总行数,用于校验输入范围。此方案虽不如JTable专业,但完全满足实验要求,且代码量可控。
6.4 从那以后我每次写 JDBC 更新逻辑,都强制走一遍“三查”清单
- 查驱动:
Class.forName()是否在getConnection()前?类名是否与 DB2 版本匹配? - 查模式:所有表名是否带
JLU.前缀?GRANT权限是否精确到JLU.TABLENAME? - 查事务:
setAutoCommit(false)后,是否配对commit()或rollback()?executeUpdate()返回值是否被检查?
这三步耗时不到 30 秒,却能规避 80% 的“连不上、查不到、改不掉”问题。实验报告里那些看似琐碎的调试记录——TEMPL改JLU.TEMPL、false改true、2改1.01——本质都是这三查的具象化。它们不是学生应付作业的痕迹,而是 DB2 + JDBC 组合拳下,一线开发者用血泪经验凝结的防翻车 checklist。希望帮到你。
本文还有配套的精品资源,点击获取