当前位置:首页 > 后端开发 > 正文

java中怎么写数据库的查询

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注入 |

批量操作优化:

java中怎么写数据库的查询  第1张

// 批量插入示例
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)

原因分析:

  1. 用户名/密码错误
  2. 用户权限不足(缺少SELECT权限)
  3. 身份验证插件不匹配(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

原因分析:

  1. 数据库服务未启动或网络不通
  2. 防火墙阻止了3306端口
  3. 连接超时时间过短(默认8秒)

解决方案:

  • 检查数据库服务状态:systemctl status mysql
  • 测试端口连通性:telnet localhost 3306
  • 调整连接超时参数:jdbc:mysql://localhost:3306/mydb?connectTimeout=5000

相关问答FAQs

Q1: 如何判断查询结果是否为空?

答: 通过ResultSetnext()方法判断,当rs.next()返回false时表示没有数据,推荐使用以下模式:

if (!rs.next()) {
    System.out.println("未查询到任何记录");
} else {
    do {
        // 处理每行数据...
    } while (rs.next());
}

注意:next()方法会将指针移动到第一行,因此需要先判断是否存在数据。

Q2: 为什么推荐使用PreparedStatement而不是Statement?

答: 主要优势包括:

  1. 安全性:自动转义特殊字符,彻底杜绝SQL注入攻击,例如当用户输入' OR '1'='1时,普通Statement会变成反面查询,而PreparedStatement会将其视为普通字符串。
  2. 性能优化:数据库服务器会对预编译的SQL进行缓存,重复执行相同结构的查询时效率更高。
  3. 代码可读性:使用占位符(?)使SQL语句更清晰,参数设置与业务逻辑分离。
  4. 类型安全:通过setXXX()方法强制类型转换,避免类型错误
0