上一篇
java里怎么调用数据库
- 后端开发
- 2025-08-17
- 3
在 Java 中通过 JDBC 调用数据库:先加载驱动类,用 DriverManager 获取连接;再创建 Statement 对象,执行 SQL 语句;最后处理结果集
以下是关于 Java 调用数据库 的完整技术指南,涵盖核心步骤、最佳实践及典型场景实现:
前置准备与核心依赖
1 必备组件清单
类别 | 名称/描述 | 作用说明 |
---|---|---|
JDBC驱动 | mysql-connector-java.jar |
MySQL官方提供的Java数据库连接器 |
项目配置文件 | jdbc.properties |
存储数据库连接参数(如URL/账号/密码) |
Java核心API | java.sql. |
提供标准数据库操作接口 |
IDE工具链 | Maven/Gradle + IntelliJ IDEA | 项目管理与代码调试支持 |
2 环境配置要点
- Maven依赖声明(以MySQL为例):
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency>
- 属性文件内容示例 (
src/main/resources/jdbc.properties
):db.url=jdbc:mysql://localhost:3306/mydatabase?useSSL=false&serverTimezone=UTC db.user=root db.password=123456
标准开发流程详解
1 五步法实现数据库交互
序号 | 阶段 | 关键技术点 | 注意事项 |
---|---|---|---|
1 | 加载数据库驱动 | Class.forName("com.mysql.cj.jdbc.Driver") |
仅适用于传统方式,新版JDBC可省略此步 |
2 | 建立数据库连接 | DriverManager.getConnection(url, user, password) |
必传三要素:URL/用户名/密码 |
3 | 创建执行器对象 | Connection.createStatement() → 普通语句prepareStatement(sql) → 预编译语句 |
优先使用PreparedStatement 防SQL注入 |
4 | 执行SQL指令 | executeUpdate() →更新操作executeQuery() →查询操作 |
根据业务类型选择对应方法 |
5 | 处理结果集 | ResultSet 遍历+next() 判断+getXXX() 取值 |
严格遵循列索引/别名映射规则 |
6 | 资源释放 | close() 顺序:ResultSet→Statement→Connection |
必须放在finally 块保证执行 |
2 完整代码示例(增删改查全场景)
import java.sql.; import java.util.Properties; public class JdbcDemo { // 静态常量定义 private static final String PROPERTIES_PATH = "jdbc.properties"; public static void main(String[] args) { // 1. 读取配置文件 Properties prop = new Properties(); try { prop.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(PROPERTIES_PATH)); } catch (Exception e) { System.err.println("配置文件加载失败: " + e.getMessage()); return; } // 2. 声明数据库连接变量 Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { // 3. 建立数据库连接 conn = DriverManager.getConnection( prop.getProperty("db.url"), prop.getProperty("db.user"), prop.getProperty("db.password") ); // ========== 插入数据示例 ========== String insertSql = "INSERT INTO users(name, email) VALUES(?, ?)"; pstmt = conn.prepareStatement(insertSql); pstmt.setString(1, "张三"); pstmt.setString(2, "zhangsan@example.com"); int affectedRows = pstmt.executeUpdate(); System.out.println("插入成功,影响行数: " + affectedRows); // ========== 查询数据示例 ========== String selectSql = "SELECT id, name, email FROM users WHERE id = ?"; pstmt = conn.prepareStatement(selectSql); pstmt.setInt(1, 1); // 假设刚插入的ID为1 rs = pstmt.executeQuery(); while(rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); String email = rs.getString("email"); System.out.printf("查询结果 ID:%d, 姓名:%s, 邮箱:%s%n", id, name, email); } // ========== 更新数据示例 ========== String updateSql = "UPDATE users SET email=? WHERE id=?"; pstmt = conn.prepareStatement(updateSql); pstmt.setString(1, "new_email@example.com"); pstmt.setInt(2, 1); affectedRows = pstmt.executeUpdate(); System.out.println("更新成功,影响行数: " + affectedRows); // ========== 删除数据示例 ========== String deleteSql = "DELETE FROM users WHERE id=?"; pstmt = conn.prepareStatement(deleteSql); pstmt.setInt(1, 1); affectedRows = pstmt.executeUpdate(); System.out.println("删除成功,影响行数: " + affectedRows); } catch (SQLException e) { System.err.println("数据库操作异常: " + e.getMessage()); e.printStackTrace(); } finally { // 4. 逆向关闭资源 try { if(rs != null) rs.close(); } catch(SQLException ignored) {} try { if(pstmt != null) pstmt.close(); } catch(SQLException ignored) {} try { if(conn != null) conn.close(); } catch(SQLException ignored) {} } } }
3 关键机制解析
技术点 | 实现原理 | 优势分析 |
---|---|---|
预编译语句(占位符) | SQL模板预先编译,参数化绑定 | 防止SQL注入 提升重复执行效率 |
事务控制 | conn.setAutoCommit(false) + commit() /rollback() |
保证原子性操作 批量操作一致性 |
批处理 | addBatch() + executeBatch() |
⏱️ 减少网络往返次数 ⏱️ 提升大数据量性能 |
连接池 | HikariCP/C3P0/DBCP等第三方库 | 复用物理连接 降低建连开销 |
进阶优化策略
1 连接池选型对比表
连接池名称 | 特点 | 适用场景 |
---|---|---|
HikariCP | 轻量级/高性能/零配置启动 | 中小型应用首选 |
C3P0 | 功能丰富/支持缓存/JMX监控 | 复杂企业级系统 |
DBCP | Apache Commons组件/简单易用 | 快速原型开发 |
Druid | 阿里开源/监控功能强大/支持SQL防火墙 | 高并发生产环境 |
2 性能调优建议
- 索引优化:对高频查询字段建立复合索引
- 分页查询:使用
LIMIT startIndex, pageSize
替代全表扫描 - 懒加载:仅在需要时初始化数据库连接
- 超时设置:
jdbc:mysql://...?connectTimeout=5000&socketTimeout=10000
- 二进制日志:开启
binlog
用于主从复制和故障恢复
常见错误解决方案
错误现象 | 根本原因 | 解决方法 |
---|---|---|
Communications link failure |
网络中断/数据库服务未启动 | 检查防火墙/重启数据库服务 |
Access denied for user |
用户名密码错误/权限不足 | 核对凭证/授予相应权限 |
Table doesn't exist |
表名大小写不匹配/未创建表 | 统一使用反引号`table_name` |
Too many connections |
连接数超过最大限制 | 增大maxPoolSize 或优化业务逻辑 |
Lock wait timeout exceeded |
长事务导致锁竞争 | 缩短事务时长/优化锁粒度 |
相关问答FAQs
Q1: 如何有效防止SQL注入攻击?
A: 应始终使用PreparedStatement
代替字符串拼接的Statement
,通过占位符进行参数化绑定,JDBC驱动会自动对特殊字符进行转义。
// 危险做法(易受注入) String sql = "SELECT FROM users WHERE username='" + userInput + "'"; // 安全做法(推荐) String sql = "SELECT FROM users WHERE username=?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, userInput);
Q2: 为什么要按照ResultSet→Statement→Connection的顺序关闭资源?
A: 因为这三个对象存在依赖关系:ResultSet
依赖于Statement
,Statement
又依赖于Connection
,如果先关闭父级对象(如Connection
),子级对象(如ResultSet
)会立即失效,反向关闭可以确保所有资源都被正确释放,避免内存泄漏,典型的finally
块结构如下:
finally { try { if(rs != null) rs.close(); } catch(SQLException ignored) {} try { if(pstmt != null) pstmt.close(); } catch(SQLException ignored) {} try { if(conn != null) conn.close(); } catch(SQLException ignored) {}