Java应用MySQL连接数爆满主因是Connection等资源未显式close,须在finally中倒序关闭或用try-with-resources;SQL注入防护必须用PreparedStatement;URL参数与驱动版本需匹配;多SQL操作须显式控制事务。

Connection没close会导致连接数爆满
Java应用跑着跑着突然连不上MySQL,错误里反复出现 Communications link failure 或 Too many connections,八成是 Connection、Statement、ResultSet 没关。JDBC不自动回收,靠JVM GC?别信——连接池可能复用,但裸写JDBC时,每个 getConnection() 都对应一个真实TCP连接,不显式 close() 就一直占着。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 必须在
finally块里逐个调用close(),顺序倒着来:先rs.close(),再stmt.close(),最后conn.close() - 用 try-with-resources 更安全,但要注意:只有实现了
AutoCloseable的对象才能用,Connection、Statement、ResultSet都符合 - 别在
catch里吞掉SQLException后就不管了——关资源的逻辑不能被异常跳过
PreparedStatement防SQL注入不是可选项
拼接字符串写 "SELECT * FROM user WHERE id = " + id,看着省事,但只要 id 来自用户输入(比如HTTP参数),就等于把数据库大门钥匙塞给攻击者。MySQL允许堆叠语句、注释符绕过,1; DROP TABLE user-- 这种输入能直接执行删表。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 所有带变量的SQL,一律用
PreparedStatement,用?占位,再调setInt(1, id)、setString(2, name)等方法赋值 -
executeQuery()只用于SELECT;executeUpdate()用于INSERT/UPDATE/DELETE,返回影响行数,别用错 - 批量操作别用循环反复
executeUpdate(),改用addBatch()+executeBatch(),性能差一个数量级
MySQL驱动版本和JDBC URL参数不匹配会静默失败
用 mysql-connector-java:8.0.33 却写老式URL jdbc:mysql://localhost:3306/test,看起来能连,但时区错乱、emoji存成问号、甚至某些字段类型映射失败——因为8.x驱动默认启用 serverTimezone=UTC 和 useSSL=false,不配好就容易出诡异问题。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- JDBC URL至少补上关键参数:
jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true - Maven依赖用
mysql:mysql-connector-java(8.x)就别混用mysql-connector-java(5.x)的jar,类加载冲突会导致ClassNotFoundException或Wrong column type - 驱动类名从
com.mysql.jdbc.Driver(5.x)变成com.mysql.cj.jdbc.Driver(8.x),硬编码的话得同步改
手动封装CRUD工具类容易漏掉事务边界
自己写个 JdbcUtils,把 getConnection() 和 close() 包起来,结果发现转账操作里A账户扣钱成功、B账户加钱失败,钱却没了——因为没控制事务,每条SQL自动提交,没法回滚。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 涉及多条SQL的业务逻辑,必须显式调
conn.setAutoCommit(false),成功后commit(),异常时rollback() - 别把
Connection当参数传进工具类的update()方法里再关掉——事务上下文就断了;应该让整个业务方法共用一个Connection - 如果真要封装,优先考虑用
ThreadLocal<connection></connection>绑定当前线程的连接,但得小心线程池复用导致连接污染
最麻烦的不是写几行CRUD,而是哪一行该在事务里、哪一行该单独提交、哪一行的异常必须传播出去——这些边界一旦模糊,数据一致性就只能靠运气。










