Connection创建失败主因是驱动JAR缺失或URL格式错误;PreparedStatement需类型匹配避免隐式转换;ResultSet等资源须逆序关闭;事务必须成对控制autoCommit状态。

Connection对象创建失败:检查驱动加载和URL格式
Java 8 及以后版本已不再需要显式调用 Class.forName() 加载 JDBC 驱动,但很多旧教程仍保留这一步,反而可能引发 ClassNotFoundException(尤其使用 MySQL 8+ 的 com.mysql.cj.jdbc.Driver 时)。真正关键的是确保驱动 JAR 在 classpath 中,且 URL 格式匹配驱动版本。
- MySQL 8+ 推荐 URL:
jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true—— 缺少serverTimezone会导致SQLException: The server time zone value 'XXX' is unrecognized - PostgreSQL 示例:
jdbc:postgresql://localhost:5432/mydb?currentSchema=public,注意默认不带ssl=false可能触发连接拒绝 - HikariCP 等连接池会自动处理驱动发现,此时手动
Class.forName不仅冗余,还可能因类重复加载报java.lang.LinkageError
PreparedStatement预编译失效:参数占位符与类型绑定要匹配
写 SELECT * FROM user WHERE id = ? 却用 setString(1, "123") 看似能运行,但数据库可能无法复用执行计划,尤其当字段是 INT 类型时。JDBC 驱动虽会尝试转换,但隐式转换常绕过索引,或在 SQL Server/Oracle 上直接报 SQLServerException: Conversion failed when converting the varchar value 'abc' to data type int。
- 查主键或数字字段,优先用
setInt(1, 123)、setLong(1, 123L) - 日期字段避免传字符串:
setTimestamp(1, Timestamp.valueOf("2023-01-01 00:00:00")),而非setString(1, "2023-01-01") - 批量操作务必用
addBatch()+executeBatch(),不要循环执行单条executeUpdate(),否则网络往返开销剧增
ResultSet遍历后未关闭:资源泄漏比想象中更隐蔽
很多人以为只要 connection.close() 就万事大吉,但 Statement 和 ResultSet 是独立的物理资源。某些 JDBC 驱动(如早期 Oracle thin driver)在 ResultSet.close() 前不会真正释放游标,导致数据库端句柄耗尽,错误表现为 ORA-01000: maximum open cursors exceeded。
- 必须按
ResultSet → Statement → Connection逆序关闭(或使用 try-with-resources) - 即使 catch 了异常,也要在
finally块里关闭,否则异常路径下资源必然泄漏 - Spring JDBC 或 MyBatis 会自动管理这些,但手写 JDBC 时,每一层都得自己兜底
事务控制失效:autoCommit模式切换时机很关键
调用 connection.setAutoCommit(false) 后,所有后续 executeUpdate() 都进入事务上下文——但这个状态会持续到连接被归还给连接池(或显式 commit()/rollback())。如果忘记恢复 autoCommit=true,下个业务逻辑拿到该连接时,看似普通插入,实则被挂起在未提交事务里,极易造成锁表或连接卡死。
立即学习“Java免费学习笔记(深入)”;
- 事务块必须成对出现:
setAutoCommit(false)→ 执行 SQL →commit()或rollback()→setAutoCommit(true) - 连接池(如 HikariCP)提供
connection.setReadOnly(true)和setTransactionIsolation(),但它们不替代手动事务边界控制 - 跨多个 DAO 方法的事务,不能靠每个方法自己开关 autoCommit,必须由外层统一控制,否则嵌套调用会破坏一致性
实际项目里最常被忽略的,是连接从连接池取出后的初始状态不可控——不同连接池实现对 autoCommit、readOnly、isolation 的默认值处理不一致,别假设“刚拿来的 connection 一定是 autoCommit=true”。










