Java 连接 MySQL 数据库完整教程
本教程将涵盖以下内容:

- 环境准备: 安装和配置必要的软件。
- 创建数据库和表: 在 MySQL 中准备数据。
- 添加 JDBC 驱动: 将 MySQL 的驱动程序引入你的 Java 项目。
- JDBC 编程基础: 连接、执行查询、处理结果、关闭资源的标准流程。
- 核心代码示例: 提供增、删、改、查 的完整代码。
- 最佳实践: 使用
try-with-resources和 PreparedStatement。 - 项目实战: 一个简单的控制台 CRUD 应用。
第一步:环境准备
在开始之前,请确保你已经安装并配置好了以下软件:
- Java Development Kit (JDK): 确保你的系统上已安装 JDK 8 或更高版本,可以通过
java -version和javac -version命令检查。 - MySQL Server: 确保你的系统上已安装并运行了 MySQL 数据库,可以通过
mysql -u root -p命令登录来验证。 - IDE (集成开发环境): 推荐使用 IntelliJ IDEA 或 Eclipse,它们能极大地简化开发过程。
第二步:创建数据库和表
打开你的 MySQL 客户端(如 MySQL Workbench, Navicat, 或命令行),执行以下 SQL 语句来创建一个数据库和一张用于测试的表。
-- 创建一个名为 `jdbc_demo` 的数据库
CREATE DATABASE IF NOT EXISTS jdbc_demo;
-- 使用这个数据库
USE jdbc_demo;
-- 创建一张 `users` 表
CREATE TABLE IF NOT EXISTS users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL,
email VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入一些测试数据
INSERT INTO users (username, password, email) VALUES
('zhangsan', '123456', 'zhangsan@example.com'),
('lisi', '654321', 'lisi@example.com');
第三步:添加 MySQL JDBC 驱动
Java 程序需要通过一个“桥梁”(即 JDBC 驱动)来与 MySQL 数据库通信,你需要将 MySQL 提供的 JDBC 驱动 JAR 文件添加到你的项目中。
Maven 项目 (推荐)
如果你使用 Maven,这是最简单、最推荐的方式,只需在 pom.xml 文件中添加 MySQL 驱动的依赖。

<dependencies>
<!-- MySQL Connector/J 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version> <!-- 建议使用最新稳定版 -->
</dependency>
</dependencies>
添加后,Maven 会自动下载并管理这个依赖。
手动下载 JAR
- 访问 MySQL Connector/J 下载页面。
- 选择 "Platform Independent" (平台独立) 的 ZIP Archive 下载并解压。
- 在你的 IDE 中,右键点击项目 -> "Build Path" / "Project Structure" -> "Libraries" -> "Add External JARs...",然后选择解压后的
mysql-connector-j-8.0.xx.jar文件。
第四步:JDBC 编程基础
所有 JDBC 操作都遵循一个固定的模式:
- 加载驱动: 告诉 JVM 使用哪个数据库驱动。
- 获取连接: 使用数据库 URL、用户名和密码建立与数据库的连接。
- 创建语句: 通过连接对象创建
Statement或PreparedStatement对象,用于执行 SQL。 - 执行 SQL: 调用
executeQuery()(用于查询) 或executeUpdate()(用于增、删、改) 方法。 - 处理结果集: 如果是查询操作,遍历
ResultSet对象来获取数据。 - 关闭资源: 按照相反的顺序关闭
ResultSet、Statement和Connection。(非常重要!)
第五步:核心代码示例
数据库连接信息
建议将这些信息放在一个单独的常量类或配置文件中,这里为了方便,我们直接写在代码里。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil {
// 数据库 URL
// 格式: jdbc:mysql://[主机名]:[端口号]/[数据库名]?[参数]
// useSSL=false (或 true) 用于禁用/启用 SSL 连接,根据你的 MySQL 版本配置
// useUnicode=true&characterEncoding=UTF-8 确保支持中文等特殊字符
private static final String URL = "jdbc:mysql://localhost:3306/jdbc_demo?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8";
private static final String USER = "root"; // 你的 MySQL 用户名
private static final String PASSWORD = "your_password"; // 你的 MySQL 密码
// 获取数据库连接
public static Connection getConnection() {
Connection connection = null;
try {
// 1. 加载驱动 (对于新版驱动,这步可以省略,但保留它更明确)
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 获取连接
connection = DriverManager.getConnection(URL, USER, PASSWORD);
System.out.println("数据库连接成功!");
} catch (ClassNotFoundException e) {
System.err.println("MySQL JDBC Driver not found.");
e.printStackTrace();
} catch (SQLException e) {
System.err.println("Connection Failed. Check output console");
e.printStackTrace();
}
return connection;
}
// 关闭数据库连接
public static void closeConnection(Connection conn) {
if (conn != null) {
try {
conn.close();
System.out.println("数据库连接已关闭。");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
查询数据
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class SelectExample {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1. 获取连接
conn = DBUtil.getConnection();
// 2. 创建 Statement
stmt = conn.createStatement();
// 3. 执行查询
String sql = "SELECT id, username, email FROM users";
rs = stmt.executeQuery(sql);
// 4. 处理结果集
System.out.println("ID\tUsername\tEmail");
System.out.println("----------------------------------");
while (rs.next()) {
// 通过列名获取数据,更推荐
int id = rs.getInt("id");
String username = rs.getString("username");
String email = rs.getString("email");
System.out.println(id + "\t" + username + "\t\t" + email);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 5. 关闭资源 (非常重要!)
// 关闭顺序: ResultSet -> Statement -> Connection
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) DBUtil.closeConnection(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
插入数据
import java.sql.Connection;
import java.sql.Statement;
public class InsertExample {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
conn = DBUtil.getConnection();
stmt = conn.createStatement();
String sql = "INSERT INTO users (username, password, email) VALUES ('wangwu', 'password123', 'wangwu@example.com')";
// executeUpdate() 返回受影响的行数
int affectedRows = stmt.executeUpdate(sql);
if (affectedRows > 0) {
System.out.println("成功插入 " + affectedRows + " 行数据。");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (stmt != null) stmt.close();
if (conn != null) DBUtil.closeConnection(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
更新数据
import java.sql.Connection;
import java.sql.Statement;
public class UpdateExample {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
conn = DBUtil.getConnection();
stmt = conn.createStatement();
String sql = "UPDATE users SET email = 'new_lisi@example.com' WHERE username = 'lisi'";
int affectedRows = stmt.executeUpdate(sql);
if (affectedRows > 0) {
System.out.println("成功更新 " + affectedRows + " 行数据。");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (stmt != null) stmt.close();
if (conn != null) DBUtil.closeConnection(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
删除数据
import java.sql.Connection;
import java.sql.Statement;
public class DeleteExample {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
conn = DBUtil.getConnection();
stmt = conn.createStatement();
String sql = "DELETE FROM users WHERE username = 'wangwu'";
int affectedRows = stmt.executeUpdate(sql);
if (affectedRows > 0) {
System.out.println("成功删除 " + affectedRows + " 行数据。");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (stmt != null) stmt.close();
if (conn != null) DBUtil.closeConnection(conn);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
第六步:最佳实践
使用 try-with-resources (Java 7+)
手动关闭资源 (try-catch-finally) 很繁琐且容易出错,Java 7 引入了 try-with-resources 语句,它可以自动实现资源的关闭,任何实现了 AutoCloseable 接口的类都可以使用它。
重构后的查询代码 (更推荐):
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class SelectExampleWithTryWithResources {
public static void main(String[] args) {
// try-with-resources 会自动关闭 conn, stmt, rs
try (Connection conn = DBUtil.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, username, email FROM users")) {
System.out.println("ID\tUsername\tEmail");
System.out.println("----------------------------------");
while (rs.next()) {
int id = rs.getInt("id");
String username = rs.getString("username");
String email = rs.getString("email");
System.out.println(id + "\t" + username + "\t\t" + email);
}
} catch (SQLException e) {
e.printStackTrace();
}
// conn, stmt, rs 在这里会自动被关闭,无需手动操作
}
}
使用 PreparedStatement 防止 SQL 注入
Statement 在拼接 SQL 字符串时容易引发 SQL 注入攻击。PreparedStatement 是预编译的 SQL 语句,它使用 作为占位符,可以更安全、更高效地处理参数。
使用 PreparedStatement 的插入示例:
import java.sql.Connection;
import java.sql.PreparedStatement;
public class InsertWithPreparedStatement {
public static void main(String[] args) {
String sql = "INSERT INTO users (username, password, email) VALUES (?, ?, ?)";
// try-with-resources 确保 PreparedStatement 和 Connection 被自动关闭
try (Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置参数 (索引从 1 开始)
pstmt.setString(1, "zhaoliu");
pstmt.setString(2, "safe_password");
pstmt.setString(3, "zhaoliu@example.com");
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
System.out.println("成功插入 " + affectedRows + " 行数据。");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
第七步:项目实战 - 简单的 CRUD 控制台应用
这是一个综合运用上述知识的简单示例,用户可以通过命令行对 users 表进行增删改查。
import java.sql.*;
import java.util.Scanner;
public class SimpleCRUDApp {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("\n--- 用户管理系统 ---");
System.out.println("1. 查看所有用户");
System.out.println("2. 添加新用户");
System.out.println("3. 更新用户信息");
System.out.println("4. 删除用户");
System.out.println("5. 退出");
System.out.print("请选择操作: ");
int choice = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
switch (choice) {
case 1:
listUsers();
break;
case 2:
addUser(scanner);
break;
case 3:
updateUser(scanner);
break;
case 4:
deleteUser(scanner);
break;
case 5:
System.out.println("感谢使用,再见!");
scanner.close();
return;
default:
System.out.println("无效的选择,请重新输入。");
}
}
}
private static void listUsers() {
String sql = "SELECT id, username, email FROM users";
try (Connection conn = DBUtil.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
System.out.println("\n--- 用户列表 ---");
System.out.println("ID\tUsername\tEmail");
System.out.println("----------------------------------");
while (rs.next()) {
System.out.println(rs.getInt("id") + "\t" + rs.getString("username") + "\t\t" + rs.getString("email"));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private static void addUser(Scanner scanner) {
System.out.print("输入用户名: ");
String username = scanner.nextLine();
System.out.print("输入密码: ");
String password = scanner.nextLine();
System.out.print("输入邮箱: ");
String email = scanner.nextLine();
String sql = "INSERT INTO users (username, password, email) VALUES (?, ?, ?)";
try (Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, username);
pstmt.setString(2, password);
pstmt.setString(3, email);
pstmt.executeUpdate();
System.out.println("用户添加成功!");
} catch (SQLException e) {
e.printStackTrace();
}
}
private static void updateUser(Scanner scanner) {
System.out.print("输入要更新的用户ID: ");
int id = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
System.out.print("输入新邮箱: ");
String newEmail = scanner.nextLine();
String sql = "UPDATE users SET email = ? WHERE id = ?";
try (Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, newEmail);
pstmt.setInt(2, id);
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
System.out.println("用户信息更新成功!");
} else {
System.out.println("未找到ID为 " + id + " 的用户。");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
private static void deleteUser(Scanner scanner) {
System.out.print("输入要删除的用户ID: ");
int id = scanner.nextInt();
String sql = "DELETE FROM users WHERE id = ?";
try (Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, id);
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
System.out.println("用户删除成功!");
} else {
System.out.println("未找到ID为 " + id + " 的用户。");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
总结与进阶
- 核心: JDBC 是 Java 连接数据库的标准 API,其流程是固定的:加载驱动 -> 获取连接 -> 创建语句 -> 执行 SQL -> 处理结果 -> 关闭资源。
- 安全: 永远不要在 Java 代码中拼接 SQL 字符串来执行用户输入的数据,必须使用
PreparedStatement来防止 SQL 注入。 - 简洁: 优先使用
try-with-resources来管理数据库资源,它能避免资源泄漏,使代码更简洁、更健壮。 - 进阶:
- 连接池: 在实际生产环境中,频繁地创建和关闭连接是非常消耗性能的,应该使用数据库连接池,如 HikariCP (目前性能最佳)、C3P0、Druid 等,它们可以复用连接,大大提高应用性能。
- ORM 框架: 对于复杂的业务应用,通常会使用 ORM (Object-Relational Mapping) 框架,如 MyBatis 或 Hibernate,它们将数据库表映射为 Java 对象,让你可以用面向对象的方式操作数据库,从而简化开发,提高代码可维护性。
希望这份详尽的教程能帮助你顺利掌握 Java 与 MySQL 的交互!
