杰瑞科技汇

Java如何查询MySQL数据?

目录

  1. 准备工作
    • 安装 MySQL 数据库
    • 创建数据库和表
    • 添加测试数据
  2. Java 项目配置
    • 添加 MySQL JDBC 驱动依赖
    • 使用 Maven/Gradle 配置
  3. 核心代码示例
    • 基础查询(不带参数)
    • 预处理查询(防止 SQL 注入,带参数)
    • 读取查询结果
  4. 完整代码示例

    一个完整的可运行示例

    Java如何查询MySQL数据?-图1
    (图片来源网络,侵删)
  5. 最佳实践与资源管理
    • 使用 try-with-resources (推荐)
    • 使用连接池
    • 防止 SQL 注入
  6. 常见问题与排错

准备工作

a. 安装 MySQL 数据库

确保你已经安装并运行了 MySQL 数据服务器,如果没有,可以从 MySQL 官网 下载安装。

b. 创建数据库和表

打开 MySQL 命令行客户端或任何 MySQL GUI 工具(如 MySQL Workbench, Navicat, DBeaver),执行以下 SQL 语句来创建一个数据库、一张表并插入一些数据。

-- 创建一个名为 'test_db' 的数据库
CREATE DATABASE IF NOT EXISTS test_db;
-- 使用该数据库
USE test_db;
-- 创建一张 'users' 表
CREATE TABLE IF NOT EXISTS users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE,
    age INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入一些测试数据
INSERT INTO users (name, email, age) VALUES
('Alice', 'alice@example.com', 28),
('Bob', 'bob@example.com', 34),
('Charlie', 'charlie@example.com', 22);

Java 项目配置

你需要将 MySQL 的 JDBC 驱动程序添加到你的 Java 项目中,最简单的方式是使用构建工具。

使用 Maven

在你的 pom.xml 文件中添加以下依赖:

Java如何查询MySQL数据?-图2
(图片来源网络,侵删)
<dependencies>
    <!-- MySQL Connector/J 驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.33</version> <!-- 建议使用最新稳定版 -->
    </dependency>
</dependencies>

使用 Gradle

在你的 build.gradle 文件中添加以下依赖:

dependencies {
    // MySQL Connector/J 驱动
    implementation 'com.mysql:mysql-connector-j:8.0.33' // 建议使用最新稳定版
}

核心代码示例

Java 连接 MySQL 并查询数据主要涉及以下几个核心类,它们都在 java.sql 包中:

  • DriverManager: 管理数据库驱动程序。
  • Connection: 代表与数据库的连接。
  • Statement: 用于执行静态 SQL 语句。
  • PreparedStatement: 用于执行预编译 SQL 语句,更安全高效。
  • ResultSet: 表示数据库查询结果集。

a. 基础查询(不带参数)

这种方式适用于 SQL 语句中不包含任何用户输入或变量。

import java.sql.*;
public class BasicQueryExample {
    // 数据库连接信息
    private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db";
    private static final String USER = "root"; // 你的数据库用户名
    private static final String PASS = "your_password"; // 你的数据库密码
    public static void main(String[] args) {
        // try-with-resources 语句,确保资源被自动关闭
        try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT id, name, email, age FROM users")) {
            System.out.println("ID\tName\tEmail\t\tAge");
            System.out.println("------------------------------------");
            // 遍历结果集
            while (rs.next()) {
                // 通过列名获取数据,更健壮
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                int age = rs.getInt("age");
                System.out.printf("%d\t%s\t%s\t%d\n", id, name, email, age);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

b. 预处理查询(带参数)

当你的 SQL 语句需要包含变量时,绝对不要使用字符串拼接("SELECT ... WHERE name = '" + name + "'"),因为这极易导致 SQL 注入 攻击,必须使用 PreparedStatement

import java.sql.*;
public class PreparedStatementExample {
    private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db";
    private static final String USER = "root";
    private static final String PASS = "your_password";
    public static void findUserByName(String searchName) {
        // 使用 ? 作为占位符
        String sql = "SELECT id, name, email, age FROM users WHERE name = ?";
        try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
             // 创建 PreparedStatement 对象
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            // 设置参数,索引从 1 开始
            pstmt.setString(1, searchName);
            // 执行查询
            try (ResultSet rs = pstmt.executeQuery()) {
                System.out.println("Searching for user with name: " + searchName);
                System.out.println("ID\tName\tEmail\t\tAge");
                System.out.println("------------------------------------");
                if (rs.next()) {
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    String email = rs.getString("email");
                    int age = rs.getInt("age");
                    System.out.printf("%d\t%s\t%s\t%d\n", id, name, email, age);
                } else {
                    System.out.println("No user found with that name.");
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        findUserByName("Alice");
    }
}

完整代码示例

这是一个结合了上述所有要素的完整、可运行的示例。

import java.sql.*;
public class MysqlJavaExample {
    // --- 数据库配置 ---
    // 注意: 如果你的 MySQL 版本是 8.0+,可能需要添加时区参数 &serverTimezone=UTC
    private static final String DB_URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";
    private static final String USER = "root";
    private static final String PASS = "your_password";
    public static void main(String[] args) {
        System.out.println("--- 1. 查询所有用户 ---");
        getAllUsers();
        System.out.println("\n--- 2. 根据ID查询用户 ---");
        getUserById(2);
        System.out.println("\n--- 3. 查询年龄大于指定值的用户 ---");
        getUsersByAge(30);
    }
    /**
     * 查询所有用户
     */
    public static void getAllUsers() {
        String sql = "SELECT id, name, email, age FROM users";
        executeQuery(sql, null);
    }
    /**
     * 根据ID查询用户
     * @param userId 用户ID
     */
    public static void getUserById(int userId) {
        // 使用预处理语句防止SQL注入
        String sql = "SELECT id, name, email, age FROM users WHERE id = ?";
        executeQuery(sql, stmt -> stmt.setInt(1, userId));
    }
    /**
     * 查询年龄大于指定值的用户
     * @param minAge 最小年龄
     */
    public static void getUsersByAge(int minAge) {
        String sql = "SELECT id, name, email, age FROM users WHERE age > ?";
        executeQuery(sql, stmt -> stmt.setInt(1, minAge));
    }
    /**
     * 通用的查询执行方法
     * @param sql SQL查询语句
     * @param parameterSetter 参数设置接口,可为null
     */
    private static void executeQuery(String sql, ParameterSetter parameterSetter) {
        // try-with-resources 确保 Connection, PreparedStatement, ResultSet 都被关闭
        try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            // 如果有参数需要设置,则调用设置方法
            if (parameterSetter != null) {
                parameterSetter.setParameters(pstmt);
            }
            System.out.println("Executing SQL: " + pstmt);
            try (ResultSet rs = pstmt.executeQuery()) {
                printResultSet(rs);
            }
        } catch (SQLException e) {
            System.err.println("Database error: " + e.getMessage());
            e.printStackTrace();
        }
    }
    /**
     * 打印结果集
     * @param rs ResultSet对象
     * @throws SQLException
     */
    private static void printResultSet(ResultSet rs) throws SQLException {
        if (!rs.isBeforeFirst()) {
            System.out.println("No records found.");
            return;
        }
        System.out.println("ID\tName\tEmail\t\tAge");
        System.out.println("------------------------------------");
        while (rs.next()) {
            int id = rs.getInt("id");
            String name = rs.getString("name");
            String email = rs.getString("email");
            int age = rs.getInt("age");
            System.out.printf("%d\t%s\t%s\t%d\n", id, name, email, age);
        }
    }
    // 函数式接口,用于设置PreparedStatement的参数
    @FunctionalInterface
    interface ParameterSetter {
        void setParameters(PreparedStatement stmt) throws SQLException;
    }
}

最佳实践与资源管理

a. 使用 try-with-resources (强烈推荐)

try-with-resources 是 Java 7 引入的一个语法糖,它能确保实现了 AutoCloseable 接口(Connection, Statement, ResultSet 都实现了)的资源在代码块执行完毕后被自动关闭,即使发生了异常,这可以防止资源泄露,使代码更简洁、更安全。

// 推荐
try (Connection conn = ...) {
    // ... 使用 conn
} // conn 自动关闭
// 不推荐 (容易出错)
Connection conn = null;
try {
    conn = ...
    // ...
} finally {
    if (conn != null) {
        try { conn.close(); } catch (SQLException e) { /* ... */ }
    }
}

b. 使用连接池

每次查询都创建和销毁连接是非常消耗资源的,在生产环境中,必须使用 连接池

连接池在应用程序启动时预先创建一组数据库连接,当需要连接时从池中获取,使用完后再归还给池,而不是关闭,这极大地提高了性能。

  • 常用连接池: HikariCP (性能最好,是目前的事实标准), Apache DBCP, C3P0。
  • 集成: Spring Boot 等框架通常都内置了对 HikariCP 的支持,只需在配置文件中配置即可。

c. 防止 SQL 注入

永远不要通过拼接字符串来构建 SQL 语句,始终使用 PreparedStatement 和它的 setXxx() 方法来设置参数。PreparedStatement 会对输入进行转义,确保它只被当作数据值,而不是 SQL 代码的一部分。


常见问题与排错

  1. ClassNotFoundException: com.mysql.cj.jdbc.Driver

    • 原因: JDBC 驱动没有添加到项目的 classpath 中。
    • 解决: 确保 mysql-connector-j 依赖已正确添加并重新构建项目(Maven/Gradle clean install)。
  2. SQLException: The server time zone value '�й���ʱ��' is unrecognized

    • 原因: MySQL 8.0+ 默认的时区可能与 JVM 不同步。
    • 解决: 在 JDBC URL 中添加时区参数。
      String DB_URL = "jdbc:mysql://localhost:3306/test_db?serverTimezone=UTC";

      或者,你也可以在 MySQL 配置中修改全局时区。

  3. SQLException: Access denied for user 'root'@'localhost'

    • 原因: 数据库用户名或密码错误,或者该用户没有访问 test_db 数据库的权限。
    • 解决: 检查代码中的 USERPASS 是否正确,可以登录 MySQL 命令行使用 GRANT ALL PRIVILEGES ON test_db.* TO 'root'@'localhost'; 来授予权限。
  4. Communications link failure

    • 原因: MySQL 服务未启动,或者网络无法连接到 localhost:3306
    • 解决: 确保 MySQL 服务正在运行,检查防火墙设置是否阻止了端口。
分享:
扫描分享到社交APP
上一篇
下一篇