资讯详情

资讯详情

JDBC实战避坑指南:从实验报告到课设骨架的完整链路

简介这份资源是吉林大学《数据库应用程序开发》课程的实验报告面向正在学习Java与DB2数据库交互的高校学生及自学者帮助其理解JDBC编程的核心流程与常见问题排查。报告围绕JDBC连接、Statement与PreparedStatement使用、结果集遍历、事务提交控制及异常处理等知识点展开并记录了权限授予、对象名称不匹配、数值溢出、事务未提交等典型调试过程还涉及GUI改造与约束异常提示等进阶任务。资源包共1个doc文件大小约1.76MB内容为完整的实验报告文档便于对照实验步骤与排错思路进行复盘。目前已有901人学习下载适合需要完成同类数据库实验或巩固JDBC实践能力的读者参考。1. 从一份实验报告里拆出的 JDBC 实战链路翻到这份《数据库应用程序开发》实验报告时我第一反应不是“学生作业”而是一份被低估的 JDBC 踩坑合集。它用八个任务把 Java 连 DB2 的完整链路走了一遍建连接、跑查询、开事务、做 GUI、处理异常、改结果集。里面那些“为什么溢出”“为什么改了没生效”的记录比很多教程都实在。如果你手头正好有一套 JDBC 实验代码要跑通或者想找一个能二次开发的数据库课设骨架这份报告的任务拆解方式值得照着走一遍。它适合刚接触 JDBC 的开发者照着复现也适合熟手拿来对照自己项目里的事务和异常处理有没有漏。2. 实验任务全景八个任务对应的 JDBC 能力矩阵2.1 任务清单与技术点映射这份报告的任务不是随便排的每个任务都在补一块 JDBC 的能力拼图。先把全景摆出来后面拆代码时才知道每段在练什么。任务核心动作涉及 JDBC 能力典型问题任务1补全程序片段权限授予、对象名限定表名未加 schema 前缀任务2改 labstaff.javaConnection/Statement/ResultSet结果集遍历任务3改 labupdate.javaPreparedStatement、事务提交溢出、提交关闭任务4改 GUI 版JOptionPane 输入输出弹窗取值类型任务5设计插入界面单行/多行/子查询插入别名冲突导致建表失败任务6空值显示为“空”ResultSet 空值判断null 直接拼接任务7异常对话框SQLException 分类捕获约束/类型/溢出区分任务8任意行列修改可滚动结果集、SQLWarning游标定位从表里能看出来前三个任务是“能跑”中间三个是“好用”最后两个是“能改”。这个递进顺序本身就是一条合理的学习路径二次开发时也可以按这个层次往上加功能。2.2 技术栈与运行环境报告里出现的类名和连接串已经把技术栈说得很清楚Java JDBC IBM DB2驱动是COM.ibm.db2.jdbc.app.DB2Driver数据库实例叫 SAMPLEschema 是 JLU操作用户 db2admin。GUI 部分用的是 Swing 的JOptionPane没有引入额外框架。这里有个容易忽略的点COM.ibm.db2.jdbc.app.DB2Driver是 DB2 的本地 JDBC 驱动type 2要求客户端装了 DB2 客户端并配好实例。如果换到只有 JDBC 驱动的环境常见做法是改用com.ibm.db2.jcc.DB2Drivertype 4 纯 Java 驱动连接串也要从jdbc:db2:sample保持兼容但不再依赖本地库。我一般会在项目里把驱动类名和连接串抽到配置文件避免换环境时满代码找字符串。// 驱动与连接参数集中管理换环境只改这里 public class DbConfig { // type 2 驱动依赖本地 DB2 客户端纯 Java 环境换成 com.ibm.db2.jcc.DB2Driver public static final String DRIVER COM.ibm.db2.jdbc.app.DB2Driver; public static final String URL jdbc:db2:sample; public static final String USER db2admin; public static final String PASSWORD db2admin; }参数说明URL里的sample是数据库实例名不是 schemaschema 在 SQL 里用JLU.前缀指定。USER/PASSWORD是连接账号报告里用的是 db2admin实际项目里不该硬编码至少走环境变量。2.3 目录与文件组织建议报告本身是文档形态没有给出工程目录。按这套任务的规模我一般会组织成下面这样方便逐个任务对照调试jdbc-lab/ ├── lib/ # db2 jdbc 驱动 jar ├── src/ │ ├── DbConfig.java # 连接参数 │ ├── Task1Fragment.java # 补全片段 │ ├── LabStaff.java # 任务2 查询 │ ├── LabUpdate.java # 任务3 更新事务 │ ├── LabUpdateGui.java # 任务4 GUI 更新 │ ├── TemplInsert.java # 任务5 插入界面 │ ├── TemplInsertNull.java# 任务6 空值显示 │ ├── TemplInsertEx.java # 任务7 异常对话框 │ └── StaffEdit.java # 任务8 任意行列修改 └── sql/ ├── grant.sql # 权限授予 └── templ.sql # 建表与别名清理把每个任务独立成一个类不是为了好看而是调试时能单独跑、单独看报错。任务之间共享的只有DbConfig这样改连接参数不会牵一发动全身。3. 核心代码拆解从连接到事务的完整链路3.1 连接与查询labstaff.java 的最小闭环任务2 要补的是一段标准 JDBC 查询。报告里列了五步建连接、建 Statement、执行查询、取结果集、移动游标。把这几步拼起来就是最小闭环。import java.sql.*; public class LabStaff { public static void main(String[] args) { Connection conn null; Statement stmt null; ResultSet rs null; try { Class.forName(DbConfig.DRIVER); // 建立连接三个参数分别是 URL、用户名、密码 conn DriverManager.getConnection( DbConfig.URL, DbConfig.USER, DbConfig.PASSWORD); stmt conn.createStatement(); // 注意 schema 前缀 JLU.报告里漏写导致“未定义名称” rs stmt.executeQuery( select NAME, JOB, SALARY from JLU.staff where ID 10); // next() 首次调用移动到第一行返回 false 表示无数据 boolean more rs.next(); while (more) { System.out.println(rs.getString(NAME) rs.getString(JOB) rs.getDouble(SALARY)); more rs.next(); } } catch (ClassNotFoundException e) { // 驱动没加载检查 jar 是否在 classpath e.printStackTrace(); } catch (SQLException e) { // 权限、表名、连接串问题都会落到这里 System.out.println(SQLState e.getSQLState() code e.getErrorCode() msg e.getMessage()); } finally { // 关闭顺序与打开顺序相反 try { if (rs ! null) rs.close(); } catch (SQLException ignored) {} try { if (stmt ! null) stmt.close(); } catch (SQLException ignored) {} try { if (conn ! null) conn.close(); } catch (SQLException ignored) {} } } }逻辑说明Class.forName加载驱动是 JDBC 4.0 之前必须的步骤报告里保留它说明环境较老。executeQuery只用于 SELECT返回 ResultSetnext()既是“移动到下一行”也是“判断有没有行”第一次调用前游标在第一行之前。参数上getString/getDouble按列名取值列名大小写不敏感但建议与 SQL 一致。报告里记录的两个错误很典型db2admin 没有 EMPLOYEE 权限解决是登录 JLU 用户授权DB2ADMIN.TEMPL未定义解决是把 TEMPL 改成JLU.TEMPL。前者是权限边界后者是 schema 限定这两个坑在真实项目里换环境时几乎必踩。3.2 更新与事务labupdate.java 的提交开关任务3 的重点不是 UPDATE 语句本身而是事务控制。报告里明确写了setAutoCommit(false)然后执行更新最后发现“成功修改但没生效”原因是提交被关掉了却没手动 commit。public class LabUpdate { public static void main(String[] args) { Connection conn null; PreparedStatement pstmt null; try { Class.forName(DbConfig.DRIVER); conn DriverManager.getConnection( DbConfig.URL, DbConfig.USER, DbConfig.PASSWORD); // 关闭自动提交后续多条更新作为一个事务 conn.setAutoCommit(false); String sql update JLU.staff set salary salary * ? where dept ?; pstmt conn.prepareStatement(sql); // 参数1涨幅因子参数2部门号 pstmt.setDouble(1, 1.01); pstmt.setInt(2, 20); int updateCount pstmt.executeUpdate(); System.out.println(affected rows updateCount); // 关键一步手动提交否则连接关闭时回滚 conn.commit(); } catch (SQLException e) { // 出错回滚避免半截事务 try { if (conn ! null) conn.rollback(); } catch (SQLException ignored) {} System.out.println(SQLState e.getSQLState() code e.getErrorCode() msg e.getMessage()); } finally { try { if (pstmt ! null) pstmt.close(); } catch (SQLException ignored) {} try { if (conn ! null) conn.close(); } catch (SQLException ignored) {} } } }参数说明setDouble(1, 1.01)对应第一个?是涨幅因子setInt(2, 20)对应部门号。报告里试过salary*2导致溢出、salary*1.01不溢出说明 SALARY 列有精度上限放大倍数要受列定义约束。executeUpdate返回受影响行数返回 0 不代表出错可能只是没有匹配记录——报告里输入 16 反馈 0 行就是这个原因。事务这块的血泪经验是setAutoCommit(false)之后任何一条 DML 不 commit 都不会落库程序正常退出时连接关闭会回滚。报告里“改了没生效”就是提交开关没打开把 false 改 true 或者补上 commit 都能解决。我一般会在 finally 里根据是否异常决定 commit 还是 rollback而不是只在 try 末尾 commit。3.3 GUI 输入输出JOptionPane 改造要点任务4 要求把命令行更新改成 GUI输入和结果都走JOptionPane。改造点不在界面多漂亮而在取值和类型转换。import javax.swing.JOptionPane; public class LabUpdateGui { public static void main(String[] args) { // showInputDialog 返回 String空输入或取消返回 null String deptStr JOptionPane.showInputDialog(请输入部门号); if (deptStr null || deptStr.trim().isEmpty()) { JOptionPane.showMessageDialog(null, 部门号不能为空); return; } int deptno; try { deptno Integer.parseInt(deptStr.trim()); } catch (NumberFormatException e) { // 非数字输入在这里拦下避免传到 SQL 层 JOptionPane.showMessageDialog(null, 部门号必须是整数); return; } // 后续连接、更新逻辑与 LabUpdate 一致结果用对话框输出 JOptionPane.showMessageDialog(null, 部门 deptno 更新完成); } }逻辑说明showInputDialog永远返回字符串取消时返回 null必须判空。Integer.parseInt对非数字抛NumberFormatException这是 GUI 版比命令行版多出来的一层校验。结果输出用showMessageDialog第一个参数传 null 表示居中显示。报告里任务4 的调试记录留白但按常见做法弹窗取值后一定要做 trim 和类型校验否则用户随手输入空格就会让 SQL 报类型错误。3.4 插入界面与空值处理任务5、6 的叠加改造任务5 要求实现单行插入、多行插入、子查询插入三种方式任务6 再把结果集里为空的列显示成“空”。这两步叠在一起核心是插入 SQL 的写法和结果集空值判断。// 单行插入参数化避免拼接引号 String single insert into JLU.templ(id, name, salary) values (?, ?, ?); // 多行插入批量 addBatch减少往返 String multi insert into JLU.templ(id, name, salary) values (?, ?, ?); // 子查询插入从 staff 里筛数据灌进 templ String sub insert into JLU.templ(id, name, salary) select id, name, salary from JLU.staff where dept ?; // 结果集空值显示 while (rs.next()) { // wasNull 必须在 get 之后调用判断上一列是否为 SQL NULL String name rs.getString(NAME); String nameShow (name null) ? 空 : name; System.out.println(nameShow); }参数说明批量插入时addBatch()攒语句、executeBatch()一次提交适合多行场景子查询插入不需要外部传值部门号作为子查询条件。空值判断的关键是rs.getString返回 null 时不能直接拼接否则界面显示“null”字符串。wasNull()是另一种判断方式但必须在取值之后调用顺序错了结果不可靠。报告里任务5 踩的坑是“无法创建 jlu.templ 表”原因是之前给 employee 建过别名 templ别名和表名冲突。解决是先删别名再建表。这个坑说明 DB2 里别名和表共享命名空间建表前要先查SYSCAT.TABLES或SYSCAT.ALIASES确认名字没被占。3.5 异常分类捕获任务7 的对话框提示任务7 要求把违反约束、类型不匹配、溢出、非法符号这几类错误都用对话框提示。做法是按SQLException的 SQLState 和 errorCode 分流而不是一个 catch 全吞。try { pstmt.executeUpdate(); } catch (SQLException e) { String msg; String state e.getSQLState(); int code e.getErrorCode(); if (23505.equals(state) || 23503.equals(state)) { // 23505 唯一约束冲突23503 外键约束 msg 违反约束 e.getMessage(); } else if (22003.equals(state)) { // 22003 数值超出范围对应报告里的溢出 msg 数值溢出请检查输入范围; } else if (22018.equals(state) || 22007.equals(state)) { // 类型转换/日期格式不匹配 msg 类型不匹配 e.getMessage(); } else { msg SQL 错误 code code state state; } JOptionPane.showMessageDialog(null, msg, 操作失败, JOptionPane.ERROR_MESSAGE); }逻辑说明SQLState 是五字符标准码前两位是类别23开头是完整性约束22开头是数据异常。errorCode 是 DB2 私有码两者结合能覆盖大部分操作类错误。报告里列的四类错误正好对应约束、类型、溢出、语法按 SQLState 分流比按异常消息字符串匹配稳得多因为消息会随语言环境变。4. 避坑与排查五个真实翻车现场4.1 表名没加 schema 前缀报“未定义名称”现象命令行执行 Java 程序报DB2ADMIN.TEMPL是一个未定义的名称。原因连接用户是 db2admin默认 schema 也是 DB2ADMINSQL 里写TEMPL会被解析成DB2ADMIN.TEMPL而表实际在 JLU schema 下。解决SQL 里显式写JLU.TEMPL或者连接后执行SET SCHEMA JLU。我一般选前者因为显式前缀在多人协作时不会因为会话默认值不同而行为不一致。4.2 权限不足导致查询直接失败现象db2admin 访问 EMPLOYEE 表被拒。原因表属主是 JLU 用户db2admin 没有被授予 SELECT 权限。解决用 JLU 登录执行GRANT SELECT ON JLU.EMPLOYEE TO db2admin需要写权限就再加 INSERT/UPDATE/DELETE。注意授权后要重新连接或刷新权限缓存否则当前会话可能仍报旧错。4.3 事务未提交导致“改了没生效”现象程序提示更新成功affected rows 大于 0但数据库里数据没变。原因setAutoCommit(false)关闭了自动提交程序结束前没有commit()连接关闭时事务回滚。解决在更新成功后显式conn.commit()异常分支conn.rollback()。这个坑的隐蔽之处在于程序不报错只能靠查库发现排查时先看连接是否关了自动提交。4.4 数值放大导致溢出现象输入部门 15 或 20更新时报类型转换溢出。原因salary * 2后超出 SALARY 列精度上限。解决降低放大倍数报告里改成 1.01 验证或者先查列定义确认精度再决定倍数。排查方法是用 DB2 命令行直接跑同样的 UPDATE如果命令行也溢出说明是列精度问题而不是 Java 类型问题。4.5 别名与表名冲突导致建表失败现象创建JLU.TEMPL表时报已存在或无法创建。原因之前给 EMPLOYEE 建过别名 TEMPL别名和表共享命名空间。解决先DROP ALIAS JLU.TEMPL再建表。建表前查一下SYSCAT.TABLES和SYSCAT.ALIASES能提前发现冲突比报错后再回头找快。5. 二次开发把实验代码改成能用的课设骨架5.1 从单任务类到分层结构这套代码现在是一个任务一个 main 方法二次开发第一步是把连接管理和业务逻辑分开。我的做法是抽一个DbUtil管连接和关闭每个任务类只保留 SQL 和参数设置。这样改连接串、加连接池都只动一个地方。public class DbUtil { public static Connection getConnection() throws SQLException { try { Class.forName(DbConfig.DRIVER); } catch (ClassNotFoundException e) { throw new SQLException(驱动加载失败, e); } return DriverManager.getConnection( DbConfig.URL, DbConfig.USER, DbConfig.PASSWORD); } public static void close(AutoCloseable... resources) { for (AutoCloseable r : resources) { try { if (r ! null) r.close(); } catch (Exception ignored) {} } } }改造后每个任务类只写try (Connection c DbUtil.getConnection())关闭交给 try-with-resources。这一步做完后面加连接池或者换驱动都不用改业务代码。5.2 异常处理从对话框升级为统一错误码任务7 的对话框提示适合课设演示但二次开发时更常见的是把 SQLState 映射成业务错误码界面层只负责展示。我一般会建一个SqlErrorMapper把23505映射成“数据重复”、22003映射成“数值超限”这样换界面框架时错误提示不用重写。验证方法是故意触发每类错误看映射是否命中而不是只看正常流程跑通。5.3 结果集修改的边界任务8 要求对结果集任意行任意列修改这依赖可滚动结果集。创建 Statement 时要指定ResultSet.TYPE_SCROLL_INSENSITIVE和CONCUR_UPDATABLE否则absolute()/updateXxx()会抛异常。改造时要注意不是所有查询都可更新带聚合或连接的查询通常只读。验证时先rs.last()看能否定位再rs.updateString()后rs.updateRow()最后重新查库确认落盘。改造点原实现建议做法验证方式连接管理每个 main 里建连接抽 DbUtil换连接串只改一处异常提示对话框拼消息SQLState 映射错误码逐类触发错误结果集修改默认只读可滚动可更新updateRow 后查库空值显示直接拼接null 判断显示“空”插入 null 行验证5.4 一个具体技巧用 wasNull 统一空值展示任务6 的空值显示如果散落在每个字段很容易漏。我的习惯是写一个show(Object v, boolean wasNull)小工具所有取值后统一走它。wasNull()必须在 get 之后立刻调用中间插了别的 get 就会判断错列。这个细节我翻车过一次后来每次写结果集遍历都先把 wasNull 调用紧跟在取值后面再谈展示逻辑。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →