java 8+无需class.forname(),引入mysql-connector-j依赖,url需配servertimezone和utf8mb4;用preparedstatement防sql注入;resultset操作须先rs.next();事务需setautocommit(false)并正确commit/rollback。

怎么加载MySQL驱动并建立JDBC连接
Java 8 及之后版本不需要手动 Class.forName("com.mysql.cj.jdbc.Driver"),MySQL Connector/J 8.0+ 会自动注册驱动。但必须确保 mysql-connector-j(注意不是旧版 mysql-connector-java)已正确引入 Maven 依赖:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.3.0</version> </dependency>
连接 URL 格式要带时区和字符集,否则中文乱码或 SQLException: The server time zone value 'XXX' is unrecognized 错误频发:
jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4&useSSL=false-
useSSL=false在本地开发可省事,生产环境应配 TLS -
characterEncoding=utf8mb4支持 emoji 和生僻字,utf8在 MySQL 中实际是 utf8mb3,别用
PreparedStatement 为什么比 Statement 更安全
直接拼接 SQL 字符串用 Statement 执行,等于把 SQL 注入大门敞开。比如:
String name = "'; DROP TABLE users; --"; String sql = "SELECT * FROM users WHERE name = '" + name + "'"; // 实际执行:SELECT * FROM users WHERE name = ''; DROP TABLE users; --'
改用 PreparedStatement 能彻底规避:
立即学习“Java免费学习笔记(深入)”;
String sql = "SELECT * FROM users WHERE name = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, name); // 参数被当作纯数据,不参与 SQL 解析
- 所有参数都走
setXxx()方法,数据库驱动负责转义 - 批量操作用
addBatch()+executeBatch(),比循环执行executeUpdate()快得多 - 注意:表名、列名、排序方向(ASC/DESC)不能参数化,需白名单校验后拼接
ResultSet 处理时最容易漏掉的三件事
查完数据不关资源、没判空、字段名写错大小写,是 NullPointerException 和连接泄漏的高发原因:
- 务必在
finally或 try-with-resources 中关闭ResultSet、PreparedStatement、Connection—— 单靠 GC 不可靠 -
rs.next()必须调用一次才能读数据,直接rs.getString("id")会抛SQLException: Before start of result set - MySQL 默认字段名大小写敏感(尤其在 Linux 服务器),建议统一用
rs.getInt("id")而非rs.getInt("ID");更稳妥的是用列索引rs.getInt(1)
事务控制里 commit() 和 rollback() 的实际触发点
MySQL 默认是自动提交(autocommit=true),意味着每条 SQL 执行完立刻生效。要手动控制事务,必须显式关闭:
conn.setAutoCommit(false);
try {
ps1.executeUpdate(); // 插入订单
ps2.executeUpdate(); // 插入订单项
conn.commit(); // 两步都成功才提交
} catch (SQLException e) {
conn.rollback(); // 任一步失败就回滚
}-
rollback()只对未提交的变更有效;一旦commit()成功,无法回退 - 连接池(如 HikariCP)中获取的
Connection,其autocommit状态可能被池预设,别假设它是 false - 长事务会锁表/行,超时导致
Lock wait timeout exceeded,业务上应尽量缩短事务范围
连接泄漏、SQL 注入、时区与编码配置错、ResultSet 使用顺序颠倒、事务边界不清——这五处出问题的概率远高于语法本身。每个点都对应一个真实报错,修起来快,但第一次踩坑往往花半天。










