java查询数据库的代码怎么写
- 数据库
- 2025-07-27
- 4
核心开发流程解析
-
加载驱动类
必须通过Class.forName()显式注册JDBC驱动(如MySQL需使用com.mysql.cj.jdbc.Driver),现代JDBC4+虽支持自动加载,但显式声明可避免版本兼容性问题。Class.forName("com.mysql.cj.jdbc.Driver"); // 适用于MySQL Connector/J 8.0+ -
建立连接对象
使用DriverManager.getConnection()传入三要素:URL格式为协议://主机:端口/数据库名?参数键=值,典型配置包括:useSSL=false禁用安全连接(本地测试常用)serverTimezone=UTC解决时区异常characterEncoding=UTF-8确保中文正常传输
示例代码:String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"; Connection conn = DriverManager.getConnection(url, "username", "password");
-
创建Statement执行体
推荐采用预编译语句(PreparedStatement)防止SQL注入攻击,其优势在于:- 占位符动态绑定参数,自动处理特殊字符转义
- 数据库预编译优化提升多次执行效率
基本用法:String sql = "SELECT id, name FROM users WHERE age > ? AND city = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setInt(1, 18); // 第一个问号替换为整数 pstmt.setString(2, "北京"); // 第二个问号替换为字符串
-
结果集遍历规范
遵循while(rs.next())循环逐行读取数据,注意列索引从1开始,建议优先获取列名再取值以保证可读性:ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); for (int i = 1; i <= columnCount; i++) { System.out.printf("%-15s", metaData.getColumnName(i)); // 打印表头 } while (rs.next()) { for (int i = 1; i <= columnCount; i++) { Object val = rs.getObject(i); System.out.printf("%-15s", val != null ? val.toString() : "NULL"); } System.out.println(); // 换行分隔记录 } -
资源释放顺序
严格按创建逆序关闭资源(ResultSet→Statement→Connection),且每个close操作都应放在finally块或try-with-resources中,遗漏任一环节都会导致连接泄漏。
完整代码实现(含异常处理)
import java.sql.;
import javax.xml.bind.DatatypeConverter; // 用于Base64编码调试可选
public class JdbcDemo {
private static final String DB_URL = "jdbc:mysql://localhost:3306/testdb";
private static final String USERNAME = "root";
private static final String PASSWORD = "your_password";
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// Step 1: 注册驱动(JDBC4+可省略但建议保留)
Class.forName("com.mysql.cj.jdbc.Driver");
// Step 2: 获取数据库连接
connection = DriverManager.getConnection(DB_URL, USERNAME, PASSWORD);
System.out.println(" 成功连接到数据库");
// Step 3: 构建带参数的SQL查询
String query = "SELECT product_id, product_name, unit_price FROM products WHERE category_id = ?";
preparedStatement = connection.prepareStatement(query);
preparedStatement.setInt(1, 5); // 设置电子品类ID过滤条件
// Step 4: 执行查询并获取结果集
resultSet = preparedStatement.executeQuery();
// Step 5: 处理结果数据
System.out.println("n 商品列表:");
printTableHeader(resultSet); // 打印列标题行
while (resultSet.next()) {
int id = resultSet.getInt("product_id");
String name = resultSet.getString("product_name");
double price = resultSet.getDouble("unit_price");
System.out.printf("| %-6d | %-20s | %10.2f 元|n", id, name, price);
}
} catch (ClassNotFoundException e) {
System.err.println(" 未找到JDBC驱动程序!请检查依赖库是否包含mysql-connector-java-x.x.xx.jar");
e.printStackTrace();
} catch (SQLException e) {
System.err.println("️ SQL执行错误:" + e.getMessage());
if (e.getErrorCode() == 1062) { // Duplicate entry示例处理
System.err.println(" 唯一约束冲突,插入失败");
} else {
e.printStackTrace();
}
} finally {
// Step 6: 确保资源被正确释放
closeQuietly(resultSet);
closeQuietly(preparedStatement);
closeQuietly(connection);
}
}
// 辅助方法:静默关闭资源避免二次异常
private static void closeQuietly(AutoCloseable resource) {
if (resource != null) {
try {
resource.close();
} catch (Exception ignored) { / NOP / }
}
}
// 辅助方法:根据ResultSet元数据生成表格头部
private static void printTableHeader(ResultSet rs) throws SQLException {
ResultSetMetaData meta = rs.getMetaData();
int cols = meta.getColumnCount();
StringBuilder headerLine = new StringBuilder("+");
for (int i = 0; i < cols; i++) {
headerLine.append("-------------+");
}
System.out.println(headerLine);
for (int i = 1; i <= cols; i++) {
String colName = meta.getColumnLabel(i); // 优先使用别名,否则用原名
System.out.printf("| %-15s ", colName);
}
System.out.println("|");
System.out.println(headerLine);
}
}
关键注意事项清单
| 风险点 | 解决方案 | 示例代码片段 |
|---|---|---|
| SQL注入破绽 | 永远使用PreparedStatement替代Statement | pstmt.setString(index, value) |
| 连接池未配置 | 生产环境务必使用HikariCP等连接池组件 | Maven依赖:HikariCP |
| 字符编码不一致 | URL中指定characterEncoding参数,建表时设置CHARACTER SET utf8mb4 | jdbc:...&characterEncoding=utf8 |
| 批量操作性能低下 | addBatch()+executeBatch()组合实现高效批处理 | batchSize>50时效果显著 |
| 事务未提交回滚 | 显式开启事务后需commit/rollback,默认自动提交模式不适合复杂业务逻辑 | conn.setAutoCommit(false); |
| 大结果集内存溢出 | 启用流式读取模式(setFetchSize)或分页查询 | rs.setFetchSize(Integer.MIN_VALUE); |
扩展实践建议
-
ORM框架集成
当项目规模扩大时,推荐迁移至MyBatis或Hibernate等ORM框架,以MyBatis为例,XML映射文件可实现对象关系自动转换:<select id="listProductsByCategory" resultType="com.example.Product"> SELECT FROM products WHERE category_id = #{categoryId} </select> -
Druid监控面板
接入阿里巴巴开源的Druid监控组件,实时查看SQL执行情况、慢查询日志和连接池状态:DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(DB_URL); dataSource.setUsername(USERNAME); dataSource.setPassword(PASSWORD); // 配置监控统计过滤器...
-
分布式事务解决方案
微服务架构下采用Seata中间件保证跨库事务一致性,通过注解方式声明全局事务边界:@GlobalTransactional(timeoutMillis = 300000) public void placeOrder() { // 调用多个服务的数据库操作 }
FAQs
Q1: Java程序连接MySQL时报“Communications link failure”如何解决?
原因分析:通常是由于网络不通、防火墙拦截或MySQL服务未启动导致。
排查步骤:
① 使用telnet命令测试端口连通性:telnet localhost 3306
② 检查防火墙设置(Linux系统执行sudo ufw allow 3306/tcp)
③ 确认MySQL配置文件中的bind-address不是仅限本地访问(改为0.0.0允许外部连接)
④ 验证用户权限是否包含远程登录权限(GRANT ALL PRIVILEGES ON TO ‘user’@’%’ IDENTIFIED BY ‘pass’;)
Q2: 执行UPDATE语句后受影响行数如何获取?
实现方法:调用executeUpdate()返回值为int类型,表示实际更新的记录数量,典型用法如下:
String updateSql = "UPDATE accounts SET balance = balance ? WHERE user_id = ?";
PreparedStatement pstmt = conn.prepareStatement(updateSql);
pstmt.setBigDecimal(1, new BigDecimal("100.00"));
pstmt.setInt(2, targetUserId);
int affectedRows = pstmt.executeUpdate(); // 获取影响行数
if (affectedRows == 0) {
throw new RuntimeException("账户扣款失败,用户不存在或余额不足
