上一篇
java中怎么写数据库的查询
- 后端开发
- 2025-08-07
- 4
Java通过JDBC实现数据库查询:加载驱动→建立连接→创建Statement→执行SQL查询语句
以下是关于 Java 中实现数据库查询 的完整指南,涵盖核心原理、代码实践、最佳实践及常见问题解决方案:
前置准备与核心组件
1 依赖环境搭建
组件 | 说明 | 示例值 |
---|---|---|
JDK版本 | Java Development Kit | ≥8 (建议LTS长期支持版) |
数据库驱动 | 根据目标数据库选择对应厂商提供的JDBC驱动 | MySQL: mysql-connector-java-8.x.jar |
IDE/构建工具 | IntelliJ IDEA/Eclipse + Maven/Gradle | |
数据库服务端 | 已启动且可访问的数据库实例 | 本地MySQL/PostgreSQL/Oracle等 |
2 关键类库解析
类/接口 | 功能描述 | 典型用途 |
---|---|---|
DriverManager |
管理数据库驱动注册与连接创建 | getConnection() |
Connection |
表示与数据库的会话连接 | 设置事务隔离级别、创建Statement |
Statement |
执行静态SQL语句的基础接口 | 简单查询/更新操作 |
PreparedStatement |
支持预编译带占位符(?)的SQL语句 | 防SQL注入、批量操作 |
ResultSet |
存储查询结果的二维表结构 | 遍历数据行、获取列值 |
ResultSetMetaData |
描述结果集的结构信息 | 动态获取列名、类型、数量 |
标准JDBC查询实现流程
1 基础查询四步曲
// 1. 加载数据库驱动(新版JDBC可省略此步) Class.forName("com.mysql.cj.jdbc.Driver"); // 显式加载驱动类 // 2. 建立数据库连接 String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC"; String user = "root"; String password = "123456"; Connection conn = DriverManager.getConnection(url, user, password); // 3. 创建Statement并执行查询 String sql = "SELECT id, name, age FROM users WHERE status = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setInt(1, 1); // 设置第一个占位符参数为整型1 ResultSet rs = pstmt.executeQuery(); // 4. 处理结果集 while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); System.out.printf("ID: %d, Name: %s, Age: %d%n", id, name, age); } // 5. 释放资源(务必按反向顺序关闭) rs.close(); pstmt.close(); conn.close();
2 关键细节说明
环节 | 注意事项 | 改进方案 |
---|---|---|
驱动加载 | Java SPI机制会自动发现META-INF/services/java.sql.Driver 文件 |
旧版需手动调用Class.forName() |
连接字符串参数 | characterEncoding=UTF-8 解决中文乱码;rewriteBatchedStatements=true 提升批处理性能 |
根据数据库特性调整参数 |
SQL执行方式 | executeQuery() 仅用于SELECT;executeUpdate() 用于INSERT/UPDATE/DELETE |
区分返回类型 |
结果集遍历 | rs.next() 每次移动游标到下一行,初始位置在首行之前 |
使用do-while 循环处理空结果集 |
资源关闭 | 未关闭会导致连接池耗尽;推荐使用try-with-resources自动关闭 | 见下文异常处理章节 |
高级特性与最佳实践
1 参数化查询防注入
风险对比:
| 写法类型 | 示例代码 | 安全风险 |
|——————–|——————————————|——————————|
| 字符串拼接 | "SELECT FROM users WHERE name='"+user+"'"
| 易受SQL注入攻击 |
| PreparedStatement | pstmt.setString(1, userInput)
| 完全避免SQL注入 |
批量操作优化:
// 批量插入示例 String batchSql = "INSERT INTO logs(content) VALUES(?)"; PreparedStatement batchPstmt = conn.prepareStatement(batchSql); for (String log : logList) { batchPstmt.setString(1, log); batchPstmt.addBatch(); // 累积批处理命令 } int[] results = batchPstmt.executeBatch(); // 一次性执行所有命令
2 事务控制
try { conn.setAutoCommit(false); // 关闭自动提交 // 执行多个关联操作... conn.commit(); // 全部成功则提交 } catch (SQLException e) { conn.rollback(); // 出现异常则回滚 throw new RuntimeException(e); } finally { conn.setAutoCommit(true); // 恢复默认设置 }
3 try-with-resources自动关闭资源
try (Connection conn = DriverManager.getConnection(url, user, password); PreparedStatement pstmt = conn.prepareStatement(sql)) { // 执行查询逻辑... } catch (SQLException e) { // 异常处理... } // 自动关闭conn和pstmt,无需显式调用close()
主流ORM框架对比
框架 | 特点 | 适用场景 | 学习曲线 |
---|---|---|---|
MyBatis | SQL映射灵活,支持动态SQL | 复杂查询较多的企业级应用 | 中等 |
Hibernate | JPA规范实现,面向对象映射 | ORM需求强烈的领域驱动设计 | 较高 |
JdbcTemplate | Spring封装的轻量级模板 | Spring生态项目快速开发 | 低 |
Flyway/Liquibase | 数据库版本控制工具 | 团队协作时的数据库迁移管理 | 低 |
常见问题排查手册
Q1: java.sql.SQLException: Access denied for user ‘root’@’localhost’ (using password: YES)
原因分析:
- 用户名/密码错误
- 用户权限不足(缺少SELECT权限)
- 身份验证插件不匹配(MySQL8默认使用caching_sha2_password)
解决方案:
- 通过命令行验证账号密码:
mysql -u root -p
- 授予权限:
GRANT ALL PRIVILEGES ON mydb. TO 'root'@'localhost'; FLUSH PRIVILEGES;
- 修改连接字符串添加认证插件参数:
?allowPublicKeyRetrieval=true
Q2: com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
原因分析:
- 数据库服务未启动或网络不通
- 防火墙阻止了3306端口
- 连接超时时间过短(默认8秒)
解决方案:
- 检查数据库服务状态:
systemctl status mysql
- 测试端口连通性:
telnet localhost 3306
- 调整连接超时参数:
jdbc:mysql://localhost:3306/mydb?connectTimeout=5000
相关问答FAQs
Q1: 如何判断查询结果是否为空?
答: 通过ResultSet
的next()
方法判断,当rs.next()
返回false
时表示没有数据,推荐使用以下模式:
if (!rs.next()) { System.out.println("未查询到任何记录"); } else { do { // 处理每行数据... } while (rs.next()); }
注意:next()
方法会将指针移动到第一行,因此需要先判断是否存在数据。
Q2: 为什么推荐使用PreparedStatement而不是Statement?
答: 主要优势包括:
- 安全性:自动转义特殊字符,彻底杜绝SQL注入攻击,例如当用户输入
' OR '1'='1
时,普通Statement会变成反面查询,而PreparedStatement会将其视为普通字符串。 - 性能优化:数据库服务器会对预编译的SQL进行缓存,重复执行相同结构的查询时效率更高。
- 代码可读性:使用占位符(?)使SQL语句更清晰,参数设置与业务逻辑分离。
- 类型安全:通过
setXXX()
方法强制类型转换,避免类型错误