哈希表、字典、数组,读取txt文本
2026/7/24 13:26:48
视频看了几百小时还迷糊?关注我,几分钟让你秒懂!(发点评论可以给博主加热度哦)
Statement拼 SQL,结果被 SQL 注入攻击了?Connection,连接池爆满,服务宕机?SimpleDateFormat和 JDBC 混用,数据错乱?🚨问题根源:你只学会了“JDBC 能连数据库”,却没掌握生产级使用规范!
本文将带你深入JDBC 核心原理 + 最佳实践,结合Spring Boot 场景 + 正反案例对比,让你从此写出安全、高效、优雅的数据库代码!
JDBC(Java Database Connectivity)是 Java 访问数据库的标准 API**,它定义了一套接口(如
Connection,Statement,ResultSet),由数据库厂商提供驱动实现(如 MySQL Connector/J)。**
✅ 核心价值:
Statement拼接 SQL(SQL 注入高危!)// 危险!用户输入 ' OR '1'='1 就能绕过登录! String sql = "SELECT * FROM users WHERE name = '" + username + "'"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql);Connection conn = DriverManager.getConnection(url, user, pwd); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT ..."); // 忘记 rs.close(), stmt.close(), conn.close() → 连接池耗尽!conn.setAutoCommit(false); try { // 执行多个操作 conn.commit(); } catch (Exception e) { // 忘记 rollback() → 数据不一致! }💥 这些代码在生产环境就是“定时炸弹”!
PreparedStatement+ try-with-resourcespublic User login(String username, String password) throws SQLException { String sql = "SELECT id, name, email FROM users WHERE username = ? AND password = ?"; // try-with-resources 自动关闭所有资源! try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { // 1. 设置参数(自动转义,防注入) ps.setString(1, username); ps.setString(2, password); // 2. 执行查询 try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { return new User( rs.getLong("id"), rs.getString("name"), rs.getString("email") ); } } } return null; }✅优势:
?占位符由驱动处理,用户输入被当作纯数据;try-with-resources确保Connection/Statement/ResultSet全部关闭;| 组件 | 作用 | 注意事项 |
|---|---|---|
DriverManager | 加载驱动、获取连接 | 生产环境不要用!应使用连接池 |
DataSource | 连接池接口(如 HikariCP) | Spring Boot 默认集成,性能高 |
Connection | 数据库连接 | 用完必须关闭(或归还连接池) |
PreparedStatement | 预编译 SQL | 永远优先于 Statement |
ResultSet | 查询结果集 | 用完必须关闭 |
📌记住:
永远不要在业务代码中直接调用DriverManager.getConnection()!
JdbcTemplate(Spring 封装的 JDBC)@Repository public class UserDao { @Autowired private JdbcTemplate jdbcTemplate; public User findById(Long id) { String sql = "SELECT id, name, email FROM users WHERE id = ?"; return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"), rs.getString("email")), id ); } public void save(User user) { String sql = "INSERT INTO users(name, email) VALUES (?, ?)"; jdbcTemplate.update(sql, user.getName(), user.getEmail()); } }✅优势:
@Transactional)@Service public class UserService { @Autowired private UserDao userDao; @Transactional // ←←← 声明式事务,自动 commit/rollback public void transfer(Long fromId, Long toId, BigDecimal amount) { User from = userDao.findById(fromId); User to = userDao.findById(toId); from.setBalance(from.getBalance().subtract(amount)); to.setBalance(to.getBalance().add(amount)); userDao.save(from); userDao.save(to); // 如果这里抛异常,Spring 自动回滚! } }💡比手动写
conn.commit()/rollback()安全 100 倍!
public void batchInsert(List<User> users) { String sql = "INSERT INTO users(name, email) VALUES (?, ?)"; jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { User user = users.get(i); ps.setString(1, user.getName()); ps.setString(2, user.getEmail()); } @Override public int getBatchSize() { return users.size(); } }); }✅ 比循环单条插入快 10~100 倍!
public void processLargeData() { String sql = "SELECT * FROM huge_table"; jdbcTemplate.query(sql, rs -> { // 每行回调,不会一次性加载到内存 String name = rs.getString("name"); // 处理逻辑... }); }⚠️ 需配置
fetchSize(如 MySQL 的useCursorFetch=true)。
错!JDBC 是底层,MyBatis 是封装。合理使用 JDBC 性能更高(无反射开销)。
注意:
?不能用于表名、字段名!
动态表名需用白名单校验:if (!allowedTables.contains(tableName)) throw new IllegalArgumentException("非法表名"); String sql = "SELECT * FROM " + tableName + " WHERE id = ?";
错!过多连接会导致数据库 CPU 打满。
HikariCP 默认maximumPoolSize=10,一般够用。
application.yml)spring: datasource: url: jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC username: root password: 123456 hikari: maximum-pool-size: 20public class User { private Long id; private String name; private String email; // constructor, getter, setter }@Repository public class UserDao { @Autowired private JdbcTemplate jdbcTemplate; public List<User> findAll() { return jdbcTemplate.query("SELECT * FROM users", (rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"), rs.getString("email")) ); } }| 场景 | 正确做法 | 错误做法 |
|---|---|---|
| SQL 拼接 | PreparedStatement+? | Statement+ 字符串拼接 |
| 资源管理 | try-with-resources或JdbcTemplate | 手动 close(易漏) |
| 事务控制 | @Transactional | 手动 commit/rollback |
| 批量操作 | batchUpdate | 循环单条 update |
| 大数据查询 | 流式读取 | List<ResultSet>一次性加载 |
✅记住:
JDBC 不是过时技术,而是高性能场景的利器!
关键在于——用对方式,守住安全底线。
视频看了几百小时还迷糊?关注我,几分钟让你秒懂!(发点评论可以给博主加热度哦)