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, "[email protected]");
int affected = ps.executeUpdate();
if (affected == 1) {
try (ResultSet keys = ps.getGeneratedKeys()) {
if (keys.next()) {
long id = keys.getLong(1); // 自增 ID
}
}
}
}
SQLException 常见触发点和应对方式
不是所有 SQLException 都代表业务失败——有些是连接中断、超时、锁等待,需要区分重试还是抛出。