Java数据库连接与操作的语法基础

Java 8+无需Class.forName(),DriverManager自动加载驱动;需确保JAR在classpath、URL格式正确;PreparedStatement防SQL注入且高效;ResultSet遍历必须用if(rs.next());资源按rs→ps→conn逆序关闭,try-with-resources声明顺序影响关闭顺序。

Java中如何正确加载JDBC驱动并建立Connection

Java 8 及以后版本已不再需要显式调用 Class.forName() 加载驱动,DriverManager.getConnection() 会自动触发服务发现机制。但如果你用的是老旧驱动(如 MySQL Connector/J 5.1.x),仍可能遇到 java.sql.SQLException: No suitable driver found 错误。

关键点在于:驱动 JAR 必须在 classpath 中,且 URL 格式必须匹配驱动要求。常见错误是把 mysql-connector-java-8.0.33.jar 放错位置(比如只丢进 IDE 的 lib 目录却没配置为 module dependency)。

  • MySQL 8+ 推荐 URL:jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
  • PostgreSQL 示例:jdbc:postgresql://localhost:5432/mydb
  • 确保连接参数含 serverTimezone(MySQL)或 sslmode(PostgreSQL),否则可能抛出时区/SSL 异常

PreparedStatement比Statement更安全也更高效

直接拼接 SQL 字符串用 Statement 执行,不仅易引发 SQL 注入(比如用户输入 ' OR '1'='1),还会让数据库反复解析相同结构的语句。而 PreparedStatement 预编译后可复用执行计划,并自动转义参数。

注意:占位符 ? 不能用于表名、列名或排序方向(ORDER BY ? 是非法的),这些必须通过白名单校验后字符串拼接。

String sql = "SELECT id, name FROM users WHERE status = ? AND created_at > ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "active");
ps.setTimestamp(2, Timestamp.from(Instant.now().minusSeconds(86400)));
ResultSet rs = ps.executeQuery();

ResultSet遍历时必须检查next()返回值

很多人写 rs.next(); String name = rs.getString("name");,结果空指针——因为 next() 返回 false 时游标不在有效行上,后续取值操作会抛 SQLException

典型场景是查询单条记录(如 SELECT * FROM users WHERE id = ?),应始终用 if (rs.next()) { ... } 而非 while (rs.next())

  • rs.getString() 对 null 值返回 null,不是空字符串;需用 rs.wasNull() 判断是否为数据库 NULL
  • 不要在循环内重复调用 rs.getMetaData(),它开销较大
  • 若只需读取一次结果,记得及时关闭 ResultSet(推荐 try-with-resources)

资源释放顺序和try-with-resources的坑

JDBC 资源(ConnectionPreparedStatementResultSet)必须按 ResultSet → PreparedStatement → Connection 逆序关闭,否则某些驱动(如 Oracle JDBC)会在关闭 Connection 时强制关闭子资源并吞掉异常。

用 try-with-resources 时要注意:如果多个资源在同一语句中声明,关闭顺序由声明顺序决定(从右到左)。所以应写成:

try (Connection conn = DriverManager.getConnection(url);
     PreparedStatement ps = conn.prepareStatement(sql);
     ResultSet rs = ps.executeQuery()) {
    // 处理结果
} catch (SQLException e) {
    // 异常处理
}

而不是把 Connection 放最后——那样会导致 psrsconn 关闭前就被关了,部分驱动会报 “connection closed”。

真正容易被忽略的是:即使用了 try-with-resources,也要在 catch 块里检查 e.getSuppressed(),因为多个资源关闭失败时,只有第一个异常被抛出,其余

被压制,不查就永远不知道哪一步挂了。