第一步:环境准备
在编写代码之前,您需要准备好两样东西:
-
MySQL 数据库服务:
- 确保您的计算机上已经安装并运行了 MySQL 数据库。
- 创建一个用于连接的数据库和一个测试用户,您可以在 MySQL 命令行客户端中执行以下 SQL:
-- 创建一个名为 'java_test_db' 的数据库 CREATE DATABASE java_test_db; -- 创建一个新用户 'java_user',密码为 'your_password',并授予其对 'java_test_db' 的所有权限 CREATE USER 'java_user'@'localhost' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON java_test_db.* TO 'java_user'@'localhost'; FLUSH PRIVILEGES;
-
MySQL JDBC 驱动程序 (Connector/J):
- 这是 Java 连接 MySQL 的桥梁,您需要下载它。
- 下载地址:MySQL Connector/J 官方下载页面
- 如何选择:选择 Platform Independent (平台独立) 的 ZIP 压缩包。
- 如何使用:
- 下载后,解压 ZIP 文件。
- 在解压后的文件夹中,找到一个名为
mysql-connector-j-8.0.xx.jar(版本号可能不同) 的文件。 - 将这个 JAR 文件添加到您的 Java 项目的类路径(Classpath)中。
在不同工具中添加 JAR 的方法:
-
IDE (如 IntelliJ IDEA, Eclipse):
- 右键点击您的项目。
- 选择 "Open Module Settings" (IntelliJ) 或 "Properties" (Eclipse)。
- 找到 "Libraries" 或 "Java Build Path" -> "Libraries"。
- 点击 "Add" 或 "Attach JARs...",然后选择您刚刚下载的
mysql-connector-j-8.0.xx.jar文件。
-
Maven 项目: 在您的
pom.xml文件中添加以下依赖项,Maven 会自动下载和管理驱动。<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> <!-- 建议使用最新稳定版 --> </dependency> -
Gradle 项目: 在您的
build.gradle文件中添加以下依赖项。implementation 'com.mysql:mysql-connector-j:8.0.33' // 建议使用最新稳定版
第二步:Java 连接代码示例
这里提供一个完整的、可运行的 Java 示例,包含连接、查询、插入数据和关闭资源的所有步骤。
我们将使用 JDBC 4.0+ 的标准方式,这种方式更简洁,推荐使用。
完整代码示例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class MySQLConnector {
// --- 数据库连接信息 ---
// 请根据您的实际情况修改这些值
private static final String DB_URL = "jdbc:mysql://localhost:3306/java_test_db?useSSL=false&serverTimezone=UTC";
private static final String USER = "java_user";
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)) {
System.out.println("数据库连接成功!");
// --- 示例1:创建表并插入数据 ---
createAndInsertData(conn);
// --- 示例2:查询数据 ---
queryData(conn);
} catch (SQLException e) {
System.err.println("数据库连接或操作失败!");
e.printStackTrace();
}
}
/**
* 创建表并插入示例数据
*/
private static void createAndInsertData(Connection conn) throws SQLException {
String createTableSQL = "CREATE TABLE IF NOT EXISTS users (" +
"id INT AUTO_INCREMENT PRIMARY KEY," +
"name VARCHAR(50) NOT NULL," +
"email VARCHAR(50) NOT NULL UNIQUE" +
")";
// 使用 try-with-resources 自动关闭 Statement
try (Statement stmt = conn.createStatement()) {
System.out.println("\n--- 正在创建表 'users' ---");
stmt.execute(createTableSQL);
System.out.println("表 'users' 创建成功或已存在。");
// 插入数据
String insertSQL1 = "INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')";
String insertSQL2 = "INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com')";
int rowsAffected1 = stmt.executeUpdate(insertSQL1);
int rowsAffected2 = stmt.executeUpdate(insertSQL2);
System.out.println("插入了 " + rowsAffected1 + " 行数据。");
System.out.println("插入了 " + rowsAffected2 + " 行数据。");
}
}
/**
* 查询并打印 'users' 表中的所有数据
*/
private static void queryData(Connection conn) throws SQLException {
String selectSQL = "SELECT id, name, email FROM users";
// 使用 try-with-resources 自动关闭 Statement 和 ResultSet
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(selectSQL)) {
System.out.println("\n--- 查询 'users' 表数据 ---");
System.out.println("ID\tName\tEmail");
while (rs.next()) { // 遍历结果集
// 通过列名获取数据,更健壮
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
System.out.println(id + "\t" + name + "\t" + email);
}
}
}
}
代码详解
-
加载驱动 (隐式):
- 在 JDBC 4.0 之后,当
DriverManager尝试建立连接时,它会自动在类路径中查找META-INF/services/java.sql.Driver文件,并加载其中指定的驱动类(如com.mysql.cj.jdbc.Driver),我们不再需要显式地调用Class.forName("com.mysql.cj.jdbc.Driver")。 - 如果您使用的是非常古老的 Java 版本或 JDBC 驱动,可能需要这行代码,但现在可以省略。
- 在 JDBC 4.0 之后,当
-
建立连接 (
DriverManager.getConnection):- 这是获取连接的核心方法。
DB_URL:数据库的统一资源定位符。jdbc:mysql://:协议和子协议。localhost:3306:数据库服务器的地址和端口。/java_test_db:要连接的数据库名。?useSSL=false&serverTimezone=UTC:是重要的连接参数。useSSL=false:对于本地开发,可以禁用 SSL 连接以避免警告。serverTimezone=UTC:指定服务器时区,避免时区不匹配的警告。
- **
USER和PASS:数据库的用户名和密码。
-
创建语句 (
Statement/PreparedStatement):Statement:用于执行静态的 SQL 语句,有 SQL 注入的风险。PreparedStatement:强烈推荐使用,它用于执行预编译的 SQL 语句。- 安全性:它使用参数化查询( 占位符),可以有效地防止 SQL 注入攻击。
- 性能:如果同一条 SQL 语句需要多次执行,数据库可以预编译它,提高执行效率。
- 示例:
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
-
执行查询 (
executeQuery/executeUpdate):executeQuery(String sql):用于执行SELECT查询,返回一个ResultSet对象。executeUpdate(String sql):用于执行INSERT,UPDATE,DELETE或CREATE等不返回结果集的语句,返回一个整数,表示受影响的行数。
-
处理结果集 (
ResultSet):ResultSet代表一个数据集,它有一个指向当前行的光标。rs.next():将光标移动到下一行,如果下一行存在,则返回true,否则返回false,通常用while (rs.next())来遍历所有数据。rs.getInt("id"),rs.getString("name"):根据列名或列索引获取当前行中特定列的值。
-
关闭资源 (
try-with-resources):Connection,Statement,ResultSet都是 JDBC 资源,它们占用数据库连接和内存。- 必须在使用后关闭它们,否则会导致资源泄漏。
try-with-resources语句(try (Resource res = ...))是 Java 7 引入的一个特性,它会自动在try块结束时关闭实现了AutoCloseable接口(这些 JDBC 资源都实现了)的资源,这是现代 Java 中处理资源的最佳实践。
第三步:最佳实践和注意事项
-
使用连接池:
- 在实际应用中,频繁地创建和销毁数据库连接是非常消耗性能的。
- 解决方案是使用连接池,连接池在启动时预先创建一组数据库连接,应用程序需要时从池中获取,用完后归还给池,而不是直接关闭。
- 常用的连接池库有:
- HikariCP:目前性能最好的连接池,Spring Boot 2.x 的默认选择。
- Apache DBCP
- C3P0
-
将配置信息外部化:
- 不要将数据库 URL、用户名和密码等敏感信息硬编码在 Java 代码中。
- 应该将它们放在配置文件中(如
application.properties或config.yml),然后通过代码读取,使用java.util.Properties类。
-
始终使用
PreparedStatement:- 除非您有充分的理由,否则请始终优先使用
PreparedStatement来执行 SQL,以防止 SQL 注入漏洞。
- 除非您有充分的理由,否则请始终优先使用
-
处理异常:
SQLException是受检异常,必须处理(使用try-catch或throws)。- 打印堆栈跟踪 (
e.printStackTrace()) 对于调试很有帮助,但在生产环境中,应该使用日志框架(如 SLF4J + Logback/Log4j2)来记录错误。
-
注意时区问题:
- 连接 URL 中指定
serverTimezone是一个好习惯,可以避免很多因时区不同导致的问题,UTC 是一个通用的选择。
- 连接 URL 中指定
