当前位置:首页 > 数据库 > 正文

java查询数据库的代码怎么写

va用JDBC连接数据库,先加载驱动、建连接,再创Statement对象执行SQL查询

核心开发流程解析

  1. 加载驱动类
    必须通过Class.forName()显式注册JDBC驱动(如MySQL需使用com.mysql.cj.jdbc.Driver),现代JDBC4+虽支持自动加载,但显式声明可避免版本兼容性问题。

    Class.forName("com.mysql.cj.jdbc.Driver"); // 适用于MySQL Connector/J 8.0+
  2. 建立连接对象
    使用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");
  3. 创建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, "北京"); // 第二个问号替换为字符串
  4. 结果集遍历规范
    遵循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(); // 换行分隔记录
    }
  5. 资源释放顺序
    严格按创建逆序关闭资源(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);

扩展实践建议

  1. ORM框架集成
    当项目规模扩大时,推荐迁移至MyBatis或Hibernate等ORM框架,以MyBatis为例,XML映射文件可实现对象关系自动转换:

    <select id="listProductsByCategory" resultType="com.example.Product">
      SELECT  FROM products WHERE category_id = #{categoryId}
    </select>
  2. Druid监控面板
    接入阿里巴巴开源的Druid监控组件,实时查看SQL执行情况、慢查询日志和连接池状态:

    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setUrl(DB_URL);
    dataSource.setUsername(USERNAME);
    dataSource.setPassword(PASSWORD);
    // 配置监控统计过滤器...
  3. 分布式事务解决方案
    微服务架构下采用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("账户扣款失败,用户不存在或余额不足
0