JDBC核心是连得上、查得准、写得稳:Statement适用于无参查询但不防SQL注入;插入必须用PreparedStatement防注入并支持自增键;SQLException需按SQLState区分处理;autoCommit默认为true,手动事务须显式commit/rollback。

如何用 Connection 和 Statement 执行简单查询
直接用 Statement 查询是最基础的方式,适合写死 SQL、不带参数的场景。但要注意:它不能防止 SQL 注入,且每次执行都需数据库重新编译 SQL。
- 必须显式调用
connection.createStatement()获取实例,不能复用多个线程的Statement - 查询后必须手动关闭
ResultSet、Statement、Connection(推荐用 try-with-resources) -
executeQuery()返回ResultSet,需用next()移动游标,再用getString("col")或getInt(1)取值
try (Connection conn = DriverManager.getConnection(url, user, pwd);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, name FROM users WHERE age > 25")) {
while (rs.next()) {
System.out.println(rs.getInt("id") + ": " + rs.getString("name"));
}
}
为什么插入要用 PreparedStatement 而不是 Statement
插入操作几乎总是带变量,用 Statement 拼接字符串极易出错且危险;PreparedStatement 预编译 SQL、支持占位符、自动转义,是唯一合理选择。
-
setString(1, "Alice")中的数字是问号?出现顺序,不是列序号 - 执行插入后,若需获取自增主键,必须在创建时加参数:
conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) - 批量插入别用循环多次
executeUpdate(),改用addBatch()+executeBatch()
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
try (PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, "Bob");
ps.setString(2, "bob@example.com");
int affected = ps.executeUpdate();
if (affected == 1) {
try (ResultSet keys = ps.getGeneratedKeys()) {
if (keys.next()) {
long id = keys.getLong(1); // 自增 ID
}
}
}
}
SQLException 常见触发点和应对方式
不是所有 SQLException 都代表业务失败——有些是连接中断、超时、锁等待,需要区分重试还是抛出。
-
SQLState前两位比异常类名更有用:比如"08"开头多为连接问题,"23"是约束冲突(如唯一键重复) - 不要捕获后吞掉异常,至少记录
e.getSQLState()和e.getErrorCode() - HikariCP 等连接池会自动重试连接,但业务层仍要处理事务回滚:遇到
SQLException后,若已开启事务,必须显式connection.rollback()
事务控制中容易被忽略的 autoCommit 行为
默认每个 executeUpdate() 都自动提交,一旦设为 false,后续所有操作都在同一事务里,直到你调用 commit() 或 rollback() —— 这点在嵌套方法或异步逻辑中极易漏掉。
立即学习“Java免费学习笔记(深入)”;
- 设置
conn.setAutoCommit(false)后,即使没异常,不commit()就会一直挂起,连接可能被池回收导致事务丢失 - 别在
finally块里无条件commit(),必须确保只在业务成功路径上提交 - 使用 Spring 的
@Transactional时,底层仍是靠这个机制,但异常类型决定是否回滚,默认只对RuntimeException回滚
SQLState=40001 时知道是死锁而不是代码写错了。










