目录
- 准备工作
- 安装 MySQL 数据库
- 创建数据库和表
- 添加测试数据
- Java 项目配置
- 添加 MySQL JDBC 驱动依赖
- 使用 Maven/Gradle 配置
- 核心代码示例
- 基础查询(不带参数)
- 预处理查询(防止 SQL 注入,带参数)
- 读取查询结果
- 完整代码示例
一个完整的可运行示例
(图片来源网络,侵删) - 最佳实践与资源管理
- 使用
try-with-resources(推荐) - 使用连接池
- 防止 SQL 注入
- 使用
- 常见问题与排错
准备工作
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 文件中添加以下依赖:

<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 代码的一部分。
常见问题与排错
-
ClassNotFoundException: com.mysql.cj.jdbc.Driver- 原因: JDBC 驱动没有添加到项目的 classpath 中。
- 解决: 确保
mysql-connector-j依赖已正确添加并重新构建项目(Maven/Gradle clean install)。
-
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 配置中修改全局时区。
-
SQLException: Access denied for user 'root'@'localhost'- 原因: 数据库用户名或密码错误,或者该用户没有访问
test_db数据库的权限。 - 解决: 检查代码中的
USER和PASS是否正确,可以登录 MySQL 命令行使用GRANT ALL PRIVILEGES ON test_db.* TO 'root'@'localhost';来授予权限。
- 原因: 数据库用户名或密码错误,或者该用户没有访问
-
Communications link failure- 原因: MySQL 服务未启动,或者网络无法连接到
localhost:3306。 - 解决: 确保 MySQL 服务正在运行,检查防火墙设置是否阻止了端口。
- 原因: MySQL 服务未启动,或者网络无法连接到
