必须禁用字符串拼接SQL,因其将用户输入直接嵌入语句导致数据与代码混淆;唯一可靠防御是全程使用参数化查询(如PreparedStatement),动态标识符须经白名单校验。

避免 SQL 注入:永远不用字符串拼接 SQL
直接用 + 拼接用户输入生成 SQL 是最常见也最危险的错误。比如 "SELECT * FROM user WHERE name = '" + name + "'",一旦 name 是 "admin' -- ",就可能绕过验证。
✅ 正确做法:一律使用 PreparedStatement,用占位符 ? 传参:
String sql = "SELECT * FROM user WHERE age > ? AND status = ?";PreparedStatement ps = conn.prepareStatement(sql);ps.setInt(1, 18); ps.setString(2, "active");
资源必须显式关闭:Connection、Statement、ResultSet 一个都不能少
忘记关闭数据库资源会导致连接泄漏,轻则性能下降,重则应用卡死或报 Too many connections。
✅ 推荐写法:用 try-with-resources(JDK 7+),自动关闭:
try (Connection conn = ds.getConnection();PreparedStatement ps = conn.prepareStatement("...");ResultSet rs = ps.executeQuery()) {// 处理结果-
}// 自动关闭 rs → ps → conn
⚠️ 注意:不要在 try 块里 return 或 throw 异常后跳过关闭逻辑;也不要手动调用 close() 再套一层 try-catch —— 除非你真要捕获并处理关闭异常(极少需要)。
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
事务边界要清晰:别让 Connection 跨方法隐式传递
把 Connection 当参数层层传递,或在工具类中静态持有,极易造成事务混乱、连接复用错乱、隔离级别失效。
✅ 做法:用线程绑定(如 ThreadLocal)或依赖 Spring 的 @Transactional 管理事务生命周期:
- 自己管理时:开启事务 → 执行业务 → 成功 commit / 失败 rollback → 关闭连接
- Spring 环境下:方法加
@Transactional,由框架保证同一事务内复用 Connection - 避免在 service 方法里 new Connection 或从 DAO 返回 Connection
空值与类型匹配:ResultSet 取值前先判断是否为 null
rs.getString("col") 返回 null 是正常行为,但若直接调用 .length() 或 .toUpperCase() 就会 NPE;更隐蔽的是 getInt("col") 对 null 字段返回 0,掩盖了真实数据缺失。
✅ 安全取值建议:
- 对可能为空的列,先用
rs.wasNull()判断:String val = rs.getString("name"); if (rs.wasNull()) { ... } - 优先用包装类型方法(JDBC 4.2+):
rs.getObject("col", String.class),null 就返回 null,不强制转默认值 - 数据库字段类型和 Java 类型保持一致(如 DATETIME → LocalDateTime,需配置 JDBC URL 加
serverTimezone=UTC和useSSL=false等)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










