上一篇
java 怎么实现查询
- 后端开发
- 2025-08-17
- 2
Java可通过JDBC连接数据库,创建Statement对象执行SQL查询,遍历ResultSet获取数据;也可整合MyBatis等ORM
以下是关于 Java 实现数据库查询 的完整技术指南,涵盖核心原理、多种实现方式及最佳实践:
基础认知:Java 与数据库交互的本质
Java 通过 JDBC (Java DataBase Connectivity) 标准接口实现与关系型数据库的通信,其核心流程为:
- 建立连接 → 2. 创建语句对象 → 3. 执行 SQL 命令 → 4. 处理结果集 → 5. 释放资源
此过程遵循「获取-操作-关闭」的资源管理模式,需特别注意异常处理和资源释放。
关键组件对照表
功能 | JDBC 接口/类 | 作用 |
---|---|---|
驱动管理器 | DriverManager |
注册/获取数据库驱动 |
数据库连接 | Connection |
代表与数据库的会话,可创建语句对象 |
执行 SQL 的载体 | Statement /PreparedStatement |
发送 SQL 指令至数据库 |
存储查询结果 | ResultSet |
二维网格结构,支持向前/向后遍历(仅限特定驱动) |
数据集存取优化 | ResultSetMetaData |
动态获取列名、类型等信息,用于灵活解析未知结构的查询结果 |
主流实现方案详解
▶️ 方案 1:原生 JDBC + 手动管理(适合学习底层机制)
// 完整示例:查询 department 表中薪资 > 8000 的员工信息 import java.sql.; public class JdbcDemo { public static void main(String[] args) { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { // 1. 加载驱动(新版 JDBC 可省略此步) Class.forName("com.mysql.cj.jdbc.Driver"); // MySQL 8+ 驱动类名 // 2. 建立连接(URL 含时区配置防警告) String url = "jdbc:mysql://localhost:3306/company?serverTimezone=UTC"; conn = DriverManager.getConnection(url, "root", "password"); // 3. 创建预编译语句(防 SQL 注入) String sql = "SELECT id, name, salary FROM employees WHERE salary > ?"; pstmt = conn.prepareStatement(sql); pstmt.setDouble(1, 8000); // 设置第一个占位符的值 // 4. 执行查询 rs = pstmt.executeQuery(); // 5. 遍历结果集 while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); double salary = rs.getDouble("salary"); System.out.printf("ID: %d | Name: %-10s | Salary: %.2f%n", id, name, salary); } } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } finally { // 6. 逆向顺序关闭资源(重点!) 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) {} } } }
️ 注意事项:
- 永远在 finally 块中关闭资源,且关闭顺序应为
ResultSet → Statement → Connection
- 优先使用
PreparedStatement
:既防 SQL 注入,又提高重复执行效率(预编译机制) - 列名推荐使用常量维护:避免硬编码字符串导致的拼写错误
- 大数据量分页处理:通过
LIMIT ?, ?
实现物理分页,而非一次性加载全部数据
▶️ 方案 2:Try-with-Resources 语法糖(Java 7+)
上述代码可简化为:
try (Connection conn = DriverManager.getConnection(url, user, pass); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setXXX(index, value); // 设置参数 try (ResultSet rs = pstmt.executeQuery()) { while (rs.next()) { / 处理结果 / } } } // 自动关闭所有 AutoCloseable 资源
优势:无需显式在 finally 中关闭资源,代码更简洁且安全。
▶️ 方案 3:Spring JDBC Template(企业级封装)
Spring 对 JDBC 进行了深度包装,典型用法如下:
@Autowired private JdbcTemplate jdbcTemplate; // Spring 提供的模板类 public List<Employee> findHighSalaryStaff(double threshold) { String sql = "SELECT id, name, salary FROM employees WHERE salary > ?"; return jdbcTemplate.query(sql, new Object[]{threshold}, (rs, rowNum) -> { Employee emp = new Employee(); emp.setId(rs.getInt("id")); emp.setName(rs.getString("name")); emp.setSalary(rs.getDouble("salary")); return emp; }); }
核心特性:
- 自动管理连接池(需配合
DataSource
) - 支持多样的结果集映射(单行映射、列表映射、流式处理)
- 统一异常转换(将 SQLException 转为 DataAccessException)
- 声明式事务管理(@Transactional 注解)
▶️ 方案 4:MyBatis(轻量级 ORM)
通过 XML 或注解定义 SQL 映射关系:
<!-UserMapper.xml --> <select id="selectByDept" resultType="com.example.User"> SELECT FROM users WHERE department_id = #{deptId} </select>
Java 调用:
SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession(); try { List<User> users = session.selectList("selectByDept", 101); } finally { session.close(); }
特点:
- SQL 写在外部文件,便于 DBA 审核和维护
- 动态 SQL 功能强大(if/choose/foreach 标签)
- 一级缓存(SqlSession 级别)和二级缓存(Namespace 级别)提升性能
▶️ 方案 5:Hibernate(全功能 ORM)
基于 JPA 规范实现,完全面向对象操作:
// HQL 查询示例(Hibernate Query Language) String hql = "FROM Employee e WHERE e.salary > :threshold"; Query<Employee> query = session.createQuery(hql, Employee.class); query.setParameter("threshold", 8000D); List<Employee> results = query.list();
适用场景:复杂对象关系映射、跨数据库迁移需求较强的项目。
关键技巧与最佳实践
场景 | 解决方案 |
---|---|
防止 SQL 注入 | 强制使用 PreparedStatement 禁止字符串拼接 SQL |
批量插入/更新 | 使用 addBatch() + executeBatch() ,比逐条执行快 5~10 倍 |
大文本字段处理 | CLOB 类型对应 Clob 接口,通过 Reader 读写 |
BLOB 二进制数据处理 | Blob 接口配合 InputStream ,注意流式传输避免 OOM |
敏感词过滤 | 可在业务层添加白名单校验,或使用数据库内置函数(如 REGEXP_REPLACE) |
慢查询优化 | EXPLAIN 分析执行计划 → 添加合适索引 → 避免全表扫描 |
连接池配置 | HikariCP(目前最快):maximumPoolSize=10 , connectionTimeout=30000 |
常见问题排查手册
Q1: java.sql.SQLException: Access denied for user ‘root’@’localhost’ (using password: YES)
原因:数据库用户名密码错误,或权限不足
解决:
- 检查配置文件中的用户名密码是否正确
- 确认该用户有访问目标数据库的权限(GRANT ALL PRIVILEGES ON dbname TO ‘user’@’host’;)
- 若使用远程连接,需放开防火墙端口(默认 MySQL 3306)
Q2: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
原因:网络中断或数据库服务未启动
解决:
- 检查数据库进程是否运行(
systemctl status mysql
) - 测试网络连通性(telnet localhost 3306)
- 检查 URL 中的 IP/端口是否正确
- 若使用云数据库,确认白名单已添加应用服务器公网 IP
进阶方向建议
- 分布式事务:Seata 框架实现跨库事务一致性
- 读写分离:MyCat 中间件实现主从负载均衡
- NoSQL 融合:MongoDB 通过 Morphia 实现文档型数据操作
- 监控告警:Druid 监控面板实时查看 SQL 执行情况
- 压力测试:JMeter 模拟高并发场景验证系统稳定性
通过掌握以上技术要点,开发者可根据项目规模选择合适的持久层方案,从基础 JDBC 到成熟 ORM 框架均能游刃有余