目录
- 准备工作
- 安装 MySQL 数据库
- 创建数据库和表
- 添加 MySQL JDBC 驱动依赖
- 核心代码步骤
- 加载 JDBC 驱动
- 建立数据库连接
- 创建
PreparedStatement(防止 SQL 注入) - 设置 SQL 语句参数
- 执行更新操作
- 处理结果 (获取影响的行数)
- 关闭资源
- 完整代码示例
- 单条数据插入
- 批量数据插入 (高效)
- 最佳实践与注意事项
- 使用
try-with-resources自动关闭资源 - 使用
PreparedStatement防止 SQL 注入 - 使用连接池
- 事务管理
- 使用
准备工作
a. 安装 MySQL 数据库
确保你已经安装并运行了 MySQL 数据库,如果没有,请从 MySQL 官网 下载并安装。
b. 创建数据库和表
打开 MySQL 命令行客户端或任何 MySQL GUI 工具(如 MySQL Workbench, Navicat, DBeaver),执行以下 SQL 语句来创建一个数据库和一张用于演示的表。
-- 创建一个名为 `java_test` 的数据库
CREATE DATABASE IF NOT EXISTS java_test;
-- 使用该数据库
USE java_test;
-- 创建一张 `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 -- 创建时间,默认为当前时间
);
-- 查看表结构
DESCRIBE users;
c. 添加 MySQL JDBC 驱动依赖
你的 Java 项目需要知道如何与 MySQL 通信,这就需要 MySQL 的 JDBC 驱动程序,根据你的项目类型,添加方式不同。
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' // 建议使用较新的稳定版本
}
手动下载 JAR 包
如果你不使用构建工具,可以从 Maven 中央仓库 下载 mysql-connector-j-x.x.x.jar 文件,并将其添加到你的项目的类路径中。
核心代码步骤
以下是使用 JDBC 插入数据的标准流程。
a. 加载 JDBC 驱动
对于较新的 JDBC 驱动(4.0+),这一步通常是可选的,因为驱动会自动加载,但显式加载是个好习惯。
Class.forName("com.mysql.cj.jdbc.Driver");
b. 建立数据库连接
使用 DriverManager 的 getConnection() 方法来建立与数据库的连接,你需要提供数据库的 URL、用户名和密码。
- URL 格式:
jdbc:mysql://[主机名]:[端口号]/[数据库名]?[属性]- 主机名: 通常是
localhost或0.0.1 - 端口号: MySQL 默认是
3306 - 属性:
useSSL=false(开发环境可以禁用SSL),serverTimezone=UTC(指定时区)
- 主机名: 通常是
String url = "jdbc:mysql://localhost:3306/java_test?useSSL=false&serverTimezone=UTC"; String username = "root"; // 你的 MySQL 用户名 String password = "your_password"; // 你的 MySQL 密码 Connection conn = DriverManager.getConnection(url, username, password);
c. 创建 PreparedStatement
强烈推荐使用 PreparedStatement 而不是 Statement。PreparedStatement 可以预编译 SQL 语句,并且使用 作为占位符,能有效防止 SQL 注入攻击,并且性能更好。
String sql = "INSERT INTO users (username, password, email) VALUES (?, ?, ?)"; PreparedStatement pstmt = conn.prepareStatement(sql);
d. 设置 SQL 语句参数
使用 setXxx() 方法(如 setString(), setInt())来为 占位符设置值,参数的索引从 1 开始。
pstmt.setString(1, "john_doe"); // 设置第一个 ? 为 'john_doe' pstmt.setString(2, "password123"); // 设置第二个 ? 为 'password123' pstmt.setString(3, "john.doe@example.com"); // 设置第三个 ? 为 'john.doe@example.com'
e. 执行更新操作
对于 INSERT, UPDATE, DELETE 这类不返回结果集的 SQL 语句,使用 executeUpdate() 方法,它会返回一个整数,表示受影响的行数。
int affectedRows = pstmt.executeUpdate();
f. 处理结果
检查 affectedRows 来确认操作是否成功。
if (affectedRows > 0) {
System.out.println("数据插入成功!影响了 " + affectedRows + " 行。");
} else {
System.out.println("数据插入失败。");
}
g. 关闭资源
为了防止资源泄漏,必须关闭所有打开的 JDBC 对象,顺序是:后打开的先关闭 (pstmt -> conn)。
pstmt.close(); conn.close();
完整代码示例
示例 1: 单条数据插入
这是一个完整的、可运行的示例,遵循了上述所有步骤。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class InsertSingleUser {
// 数据库连接信息
private static final String DB_URL = "jdbc:mysql://localhost:3306/java_test?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASS = "your_password";
public static void main(String[] args) {
// SQL 插入语句,使用 ? 作为占位符
String sql = "INSERT INTO users (username, password, email) VALUES (?, ?, ?)";
// 使用 try-with-resources 自动关闭资源
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置参数
pstmt.setString(1, "jane_doe");
pstmt.setString(2, "securepass456");
pstmt.setString(3, "jane.doe@example.com");
// 执行更新
int affectedRows = pstmt.executeUpdate();
// 检查结果
if (affectedRows > 0) {
System.out.println("新用户 'jane_doe' 创建成功!");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
示例 2: 批量数据插入 (高效)
当需要插入大量数据时,一条一条插入效率很低,JDBC 提供了批量操作功能,可以大大提高性能。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class BatchInsertUsers {
private static final String DB_URL = "jdbc:mysql://localhost:3306/java_test?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASS = "your_password";
public static void main(String[] args) {
String sql = "INSERT INTO users (username, password, email) VALUES (?, ?, ?)";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 关闭自动提交,以便进行事务管理
conn.setAutoCommit(false);
// 添加要执行的 SQL 语句到批处理中
for (int i = 1; i <= 10; i++) {
pstmt.setString(1, "user_" + i);
pstmt.setString(2, "password_" + i);
pstmt.setString(3, "user_" + i + "@example.com");
pstmt.addBatch(); // 将参数化的语句添加到批处理中
}
// 执行批处理
int[] results = pstmt.executeBatch();
// 提交事务
conn.commit();
System.out.println("批量插入完成,共插入了 " + results.length + " 条记录。");
} catch (SQLException e) {
e.printStackTrace();
// 如果发生错误,回滚事务
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS)) {
if (conn != null) {
conn.rollback();
System.err.println("发生错误,已回滚事务。");
}
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
}
最佳实践与注意事项
a. 使用 try-with-resources (Java 7+)
如示例中所示,try-with-resources 语句可以自动实现 AutoCloseable 接口的对象(如 Connection, PreparedStatement),当代码块执行完毕后,它们会自动关闭,即使发生异常也能确保资源被释放,是防止资源泄漏的最佳方式。
b. 永远使用 PreparedStatement 来防止 SQL 注入
拼接 SQL 字符串来构建查询是极其危险的,这会让你的应用极易受到 SQL 注入攻击。 错误示范:
// 危险!不要这样做! String username = "admin' OR '1'='1"; String sql = "SELECT * FROM users WHERE username = '" + username + "'";
攻击者可以通过输入特殊的字符串来操纵 SQL 逻辑,从而绕过认证或窃取数据。PreparedStatement 会将数据和 SQL 语句分开处理,从根本上杜绝了这种风险。
c. 使用连接池
每次执行 SQL 操作都创建一个新的连接是非常消耗资源的,在高并发的应用中,性能会急剧下降,解决方案是使用连接池,如 HikariCP (目前性能最好的连接池)、Apache DBCP 或 C3P0。
连接池会在应用启动时创建一组数据库连接,并缓存起来,当需要时,从池中获取一个用完后再放回池中,避免了频繁创建和销毁连接的开销。
使用 HikariCP 的示例:
-
添加依赖 (
pom.xml):<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency> -
代码中创建和使用数据源:
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; // ... 在类加载时或应用启动时初始化数据源 HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/java_test?useSSL=false&serverTimezone=UTC"); config.setUsername("root"); config.setPassword("your_password"); HikariDataSource ds = new HikariDataSource(config); // ... 在需要连接时 try (Connection conn = ds.getConnection()) { // 使用 conn 进行数据库操作 }
d. 事务管理
默认情况下,每条 SQL 语句都是一个独立的事务,对于需要多个步骤作为一个整体执行的业务逻辑(转账操作:从 A 账户扣款,然后向 B 账户存款),你需要手动管理事务。
conn.setAutoCommit(false);// 关闭自动提交conn.commit();// 手动提交事务conn.rollback();// 发生错误时回滚事务
如批量插入示例所示,将多个 addBatch() 操作放在一个事务中,可以保证它们要么全部成功,要么全部失败,保证了数据的一致性。
