简介这份资源是吉林大学《数据库应用程序开发》课程的实验报告面向正在学习Java与DB2数据库交互的高校学生及自学者帮助其理解JDBC编程的核心流程与常见问题排查。报告围绕JDBC连接、Statement与PreparedStatement使用、结果集遍历、事务提交控制及异常处理等任务展开并记录了权限授予、对象名称不匹配、数值溢出、事务未提交等典型调试过程可作为实验复盘与排错思路的参考。资源包共1个doc文档大小约1.76MB内容以实验步骤、错误分析与运行结果截图为主结构完整便于对照学习。目前已有901人学习下载适合需要完成同类数据库实验或巩固JDBC基础操作的读者参考。1. 权限报错、未定义名称、事务不生效JDBC 连 DB2 的三个高频翻车点刚接手 JDBC 连 DB2 的活儿最容易撞上的不是复杂 SQL而是三类看着像玄学、其实都有明确根因的报错一是db2admin没有访问EMPLOYEE的权限程序一跑就抛授权异常二是命令行执行 Java 文件时提示DB2ADMIN.TEMPL是一个未定义的名称三是更新语句明明执行成功、返回了影响行数数据库里的数据却纹丝不动。这三类问题在数据库应用程序开发实验里几乎人人都会踩一遍新手卡在权限和名称解析上熟手也常在事务提交上翻车。下面按故障场景拆开讲每个都给出可抄作业的命令和配置照着排查基本能定位到根因。2. 权限与名称解析报错从授权失败到未定义名称2.1 db2admin 没有访问 EMPLOYEE 的权限现象很直接用db2admin账号连上SAMPLE库执行查询EMPLOYEE表的语句JDBC 抛出的异常里带 SQLCODE-551或 SQLSTATE42501消息大意是当前用户对目标表没有 SELECT 权限。很多人第一反应是去改连接串里的用户名密码其实方向错了——连接是通的卡在授权这一层。根因在于 DB2 的表权限是按用户/组授予的EMPLOYEE表建在JLU这个 schema 下属主是JLU用户db2admin默认只有管理权限没有被显式授予对该表的访问权。常见做法是切到属主用户用GRANT把权限授出去-- 以 JLU 用户登录 DB2 命令行授予 db2admin 对 EMPLOYEE 表的查询权限 CONNECT TO sample USER JLU USING jlu密码; GRANT SELECT ON JLU.EMPLOYEE TO USER db2admin; -- 如果后续还要更新补上 UPDATE / INSERT / DELETE GRANT UPDATE, INSERT, DELETE ON JLU.EMPLOYEE TO USER db2admin;这里几个参数要看清CONNECT TO sample USER JLU是以属主身份连库只有属主或被授予WITH GRANT OPTION的用户才能继续授权ON JLU.EMPLOYEE必须带 schema 前缀否则会按当前用户的默认 schema 去找表找不到就报未定义名称TO USER db2admin明确授给用户而不是组。授权完最好用db2admin重新连一次跑一条SELECT COUNT(*) FROM JLU.EMPLOYEE验证别只信 GRANT 没报错就以为成了。提示授权后如果 Java 程序仍报权限错先确认连接串里的用户确实是db2admin再确认程序里 SQL 写的表名带了JLU.前缀两个条件缺一不可。2.2 命令行执行报 DB2ADMIN.TEMPL 未定义名称第二个坑更隐蔽代码里写的是TEMPL命令行一跑就提示DB2ADMIN.TEMPL是一个未定义的名称。注意报错里自动补了DB2ADMIN.前缀这说明 DB2 在解析表名时用的是当前连接用户的默认 schema也就是db2admin而TEMPL这张表实际建在JLUschema 下自然找不到。解决办法是把代码里的表名改成带 schema 的全名// 修改前DB2 会按当前用户默认 schema 解析成 DB2ADMIN.TEMPL String sql SELECT * FROM TEMPL; // 修改后显式指定 schema避免默认 schema 解析歧义 String sql SELECT * FROM JLU.TEMPL;逻辑说明DB2 对未加 schema 前缀的对象名会按CURRENT SCHEMA特殊寄存器去补全而CURRENT SCHEMA默认等于连接用户名大写。所以db2admin连库时TEMPL被补成DB2ADMIN.TEMPL。显式写JLU.TEMPL就绕开了这套补全逻辑。如果表多、改代码麻烦也可以在连接后执行SET SCHEMA JLU让后续所有未限定名都按JLU解析-- 连接建立后立即执行后续 SQL 中的未限定表名都按 JLU 解析 SET SCHEMA JLU;两种做法各有适用场景代码里写全名最稳适合交付给别人跑的脚本SET SCHEMA适合临时调试或表名大量复用的场景。我一般优先写全名因为 schema 切换是会话级的连接池复用连接时容易串味排查起来又是一轮折腾。2.3 别名冲突导致建表失败还有一个和名称解析同源的坑想建JLU.TEMPL表结果报对象已存在或无法创建。根因是之前给EMPLOYEE建过别名TEMPL别名和表在 DB2 里共享同一个命名空间同名就冲突。解决方法是先删掉旧别名再建表-- 先确认别名是否存在再删除最后建表 DROP ALIAS JLU.TEMPL; CREATE TABLE JLU.TEMPL ( ID INTEGER NOT NULL, NAME VARCHAR(30), SALARY DECIMAL(9,2) );删别名前建议先SELECT * FROM SYSCAT.TABLES WHERE TABNAMETEMPL看一眼对象类型TYPE列A是别名、T是表确认删的是别名不是误删了表。这个坑的血泪经验是命名空间冲突不会在编译期提示只在运行到建表语句时才炸所以建表前养成查SYSCAT的习惯能省不少时间。3. 更新不生效与数值溢出事务提交和类型转换的连环坑3.1 执行成功但数据没变setAutoCommit(false) 忘了提交这个故障最迷惑人PreparedStatement.executeUpdate()返回了大于 0 的影响行数程序也没抛异常可去 DB2 里一查salary一点没变。很多人怀疑是 SQL 写错、条件没匹配上甚至怀疑驱动有问题其实根因就一行代码——sample.setAutoCommit(false)把自动提交关了但后面没有显式commit()。Connection sample DriverManager.getConnection(jdbc:db2:sample, db2admin, db2admin); sample.setAutoCommit(false); // 关闭自动提交开启手动事务 try { PreparedStatement pstmt sample.prepareStatement( UPDATE JLU.staff SET salary salary * 1.01 WHERE dept ?); pstmt.setInt(1, mydeptno); int updateCount pstmt.executeUpdate(); sample.commit(); // 关键手动事务必须显式提交否则连接关闭时回滚 System.out.println(updated rows: updateCount); } catch (SQLException e) { sample.rollback(); // 出错回滚避免半截事务 throw e; }参数和逻辑说明setAutoCommit(false)之后每条 DML 都处在一个未提交事务里executeUpdate只是把变更写进事务日志没落盘commit()才真正生效rollback()在异常分支里兜底。实验里把false改回true就能改成功正是因为恢复了自动提交。生产环境不建议图省事直接开自动提交正确姿势是保留手动事务、在业务逻辑末尾显式提交并用 try-catch 保证异常时回滚。注意连接池场景下连接归还前如果事务没提交也没回滚下次借出这条连接可能带着脏事务表现为“莫名其妙锁表”或“数据时有时无”排查成本极高。3.2 输入 20 就溢出salary 放大后超出 DECIMAL 精度另一个高频报错是类型转换溢出输入部门号 20 时抛溢出异常输入 15、16 却正常。根因不在输入值本身而在更新表达式把salary放大后超出了列定义精度。实验里把salary改成salary*215 和 20 都溢出改成salary*1.01两个都不溢出这就反证了是放大后的数值超限。-- 先查目标行的原始值和列精度判断放大后是否会越界 SELECT id, salary FROM JLU.staff WHERE dept 20; -- 查看列定义精度DECIMAL(9,2) 表示总 9 位、小数 2 位整数部分最多 7 位 SELECT COLNAME, TYPENAME, LENGTH, SCALE FROM SYSCAT.COLUMNS WHERE TABNAME STAFF AND COLNAME SALARY;逻辑说明DECIMAL(9,2)能存的最大值是9999999.99如果某行salary已经是6000000.00乘 2 就是12000000.00整数部分 8 位直接溢出。排查顺序是先查原始值、再查列精度、最后算放大后的理论值三者一对比就能定位是哪一行、哪个因子越界。修复方向有两个调小放大因子或者用ALTER TABLE扩精度但扩精度要评估下游依赖别随手改。3.3 输入 16 返回修改 0 行条件没匹配到记录还有一种“假故障”输入 16 时executeUpdate返回 0程序没报错但也没改到任何数据。根因很简单——WHERE dept 16在表里确实没有对应记录。这不是 bug是数据本身不存在。排查时先用同条件SELECT验证-- 用和 UPDATE 完全相同的条件先查一遍确认是否有匹配行 SELECT COUNT(*) FROM JLU.staff WHERE dept 16;如果COUNT(*)为 0说明条件没命中检查部门号是否录错、数据是否被别的会话改过。养成“先 SELECT 后 UPDATE”的习惯能避免大量“改了没生效”的误判。返回 0 行和事务没提交是两种完全不同的故障前者是数据问题后者是事务问题排查路径不能混。4. 异常处理与 GUI 改造把报错挡在对话框里4.1 违反约束、类型不匹配、溢出统一用 JOptionPane 提示实验要求把操纵类错误以对话框形式提示覆盖违反约束、类型不匹配、溢出、SQL 非法符号四类。核心是把SQLException的getErrorCode()、getSQLState()、getMessage()三个信息组合起来判断再分派到不同提示文案try { int updateCount pstmt.executeUpdate(); } catch (SQLException e) { String state e.getSQLState(); String msg; if (23505.equals(state)) { msg 违反唯一约束记录已存在; } else if (22003.equals(state)) { msg 数值溢出请检查输入范围; } else if (22018.equals(state)) { msg 类型不匹配请检查输入格式; } else if (42601.equals(state)) { msg SQL 语句含非法符号; } else { msg SQLState state , code e.getErrorCode() , e.getMessage(); } JOptionPane.showMessageDialog(null, msg, 操作失败, JOptionPane.ERROR_MESSAGE); }参数说明SQLState是五位标准状态码23505唯一约束冲突、22003数值越界、22018字符转数值失败、42601语法错误这几个是实验里最常撞上的getErrorCode()是 DB2 私有错误码定位具体对象时更有用getMessage()给人看。分派逻辑先按SQLState归类兜底分支把原始信息全打出来方便排查没覆盖到的错误。GUI 改造时注意JOptionPane要在事件分发线程里调用别在后台线程直接弹窗。4.2 结果集空值统一显示为“空”结果集里NULL直接getString会返回null界面上显示成空白用户分不清是空值还是没查到。处理方式是在取值时判空替换String name rs.getString(NAME); name (name null) ? 空 : name; // 所有列统一走这个判空逻辑逻辑说明wasNull()也可以用来判断上一列是否为 SQL NULL但逐列判空再替换更直观。增、改、查三个结果集都要走同一套判空别只改查询忘了更新回显否则界面表现不一致用户会以为数据丢了。5. 快速定位这类 JDBC 故障的排查顺序遇到 JDBC 连 DB2 的报错我一般按“连接 → 权限 → 名称 → 事务 → 数值”五层往下筛每层都有明确的验证动作不靠猜。排查层典型报错验证动作根因方向连接连接被拒绝、驱动类找不到检查Class.forName驱动名和 JDBC URL驱动未加载或 URL 写错权限SQLCODE -551 / SQLSTATE 42501用属主账号GRANT后重连验证表权限未授予名称未定义名称、自动补 schema 前缀查SYSCAT.TABLES确认对象和 schema默认 schema 解析歧义或别名冲突事务返回影响行数但数据未变检查setAutoCommit与commit配对手动事务未提交数值溢出、类型转换失败查原始值 列精度 放大后理论值超出 DECIMAL 精度或条件未命中具体操作上先跑一条最小验证 SQL 确认连接和权限没问题再把程序里的 SQL 原样贴到 DB2 命令行执行能复现就是 SQL 或数据问题不能复现就往 JDBC 代码层查。名称类报错优先看报错里自动补的前缀那个前缀就是当前CURRENT SCHEMA一眼能看出解析方向对不对。事务类问题在executeUpdate后立刻加一行commit()验证能改成功就锁定是提交遗漏。数值类问题先SELECT原始行再对照SYSCAT.COLUMNS的精度算一遍基本不用调试器就能定位。从那以后我每次写 JDBC 更新逻辑都强制走一遍“先 SELECT 验证条件、再确认事务提交、最后核对列精度”的三步哪怕代码看着再简单也不省。这套顺序帮我省掉了大量“改了没生效”的无效排查。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?