目录
- 环境准备
- 核心步骤
- 完整代码示例
- 最佳实践与高级用法
- 使用
PreparedStatement防止 SQL 注入 - 批量插入以提高性能
- 获取自增主键 ID
- 使用
- 常见问题与解决方案
环境准备
在开始编码之前,请确保你已经准备好以下环境:

- Java 开发环境: 安装了 JDK (JDK 8 或更高版本)。
- MySQL 数据库: 安装并运行着 MySQL 服务器。
- MySQL 驱动: 需要将 MySQL 的 JDBC 驱动程序 (JAR 文件) 添加到你的项目中。
如何添加 MySQL 驱动?
- Maven 项目: 在
pom.xml文件中添加依赖,这是最推荐的方式。<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> <!-- 建议使用较新的版本 --> </dependency> - Gradle 项目: 在
build.gradle文件中添加依赖。implementation 'mysql:mysql-connector-java:8.0.33'
- 手动添加: 从 MySQL 官网 下载 JAR 文件,并将其添加到项目的类路径中。
核心步骤
在 Java 中使用 JDBC 插入数据,通常遵循以下六个核心步骤:
- 加载 JDBC 驱动: 使用
Class.forName()加载 MySQL 驱动类,对于新版本的驱动(JDBC 4.0+),这一步通常是可选的,但显式写出更清晰。 - 建立数据库连接: 使用
DriverManager.getConnection()方法,提供数据库 URL、用户名和密码来创建一个Connection对象。 - 创建
Statement或PreparedStatement对象:Statement: 用于执行静态的 SQL 语句。PreparedStatement: 强烈推荐,用于执行预编译的 SQL 语句,可以防止 SQL 注入,并且对于重复执行的语句性能更好。
- 执行 SQL 插入语句: 调用
Statement或PreparedStatement的executeUpdate()方法来执行INSERT语句,此方法返回一个整数,表示受影响的行数。 - 处理结果: 检查
executeUpdate()的返回值,以确认插入是否成功。 - 关闭资源: 非常重要! 按照创建的相反顺序关闭所有资源 (
ResultSet,Statement,Connection),以避免数据库连接泄漏。
完整代码示例
假设我们有一个名为 employees 的表,结构如下:
CREATE DATABASE my_company;
USE my_company;
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
position VARCHAR(100),
salary DECIMAL(10, 2),
hire_date DATE
);
下面是一个完整的 Java 程序,向 employees 表中插入一条新记录。

使用 Statement (不推荐,仅作演示)
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.SQLException;
public class InsertDataWithStatement {
// 数据库连接信息
private static final String DB_URL = "jdbc:mysql://localhost:3306/my_company?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASS = "your_password"; // 替换为你的数据库密码
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
// 1. 加载 JDBC 驱动 (对于 JDBC 4.0+ 可选)
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 建立数据库连接
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
// 3. 创建 Statement 对象
System.out.println("Creating statement...");
stmt = conn.createStatement();
// 4. 执行 SQL 插入语句
// 注意:字符串中的单引号需要转义为 ''
String sql = "INSERT INTO employees (name, position, salary, hire_date) " +
"VALUES ('张三', '软件工程师', 8500.50, '2025-10-26')";
int rowsAffected = stmt.executeUpdate(sql);
// 5. 处理结果
if (rowsAffected > 0) {
System.out.println(rowsAffected + " row(s) inserted successfully.");
}
} catch (ClassNotFoundException e) {
System.err.println("MySQL JDBC Driver not found.");
e.printStackTrace();
} catch (SQLException e) {
System.err.println("SQL Error: " + e.getMessage());
e.printStackTrace();
} finally {
// 6. 关闭资源
// 确保在任何情况下都会关闭资源
try {
if (stmt != null) stmt.close();
if (conn != null) conn.close();
System.out.println("Database connection closed.");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
使用 PreparedStatement (强烈推荐)
这是更安全、更高效的写法。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class InsertDataWithPreparedStatement {
// 数据库连接信息
private static final String DB_URL = "jdbc:mysql://localhost:3306/my_company?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASS = "your_password"; // 替换为你的数据库密码
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 1. 加载 JDBC 驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 建立数据库连接
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
// 3. 创建 SQL 语句模板,使用 ? 作为占位符
String sql = "INSERT INTO employees (name, position, salary, hire_date) VALUES (?, ?, ?, ?)";
// 4. 创建 PreparedStatement 对象
System.out.println("Creating prepared statement...");
pstmt = conn.prepareStatement(sql);
// 5. 设置参数 (索引从 1 开始)
pstmt.setString(1, "李四");
pstmt.setString(2, "产品经理");
pstmt.setDouble(3, 9500.00);
pstmt.setDate(4, new java.sql.Date(System.currentTimeMillis())); // 使用当前日期
// 6. 执行 SQL 插入语句
int rowsAffected = pstmt.executeUpdate();
// 7. 处理结果
if (rowsAffected > 0) {
System.out.println(rowsAffected + " row(s) inserted successfully.");
}
} catch (ClassNotFoundException e) {
System.err.println("MySQL JDBC Driver not found.");
e.printStackTrace();
} catch (SQLException e) {
System.err.println("SQL Error: " + e.getMessage());
e.printStackTrace();
} finally {
// 8. 关闭资源
try {
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
System.out.println("Database connection closed.");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
最佳实践与高级用法
使用 PreparedStatement 防止 SQL 注入
PreparedStatement 的最大优势是它能有效防止 SQL 注入攻击,它将 SQL 语句和数据分开处理,数据库驱动会自动对 占位符设置的参数进行转义,从而确保恶意输入不会被当作 SQL 代码执行。
对比:
Statement(危险):sql = "INSERT ... VALUES ('" + userInput + "')";userInput是'); DROP TABLE employees; --,整个表都会被删除。PreparedStatement(安全):pstmt.setString(1, userInput);,无论userInput是什么,它都会被当作一个字符串值,而不是 SQL 命令的一部分。
批量插入以提高性能
当需要插入大量数据时,逐条插入效率很低,使用 PreparedStatement 的批处理功能可以极大地提高性能。

// ... (前面的连接代码相同)
String sql = "INSERT INTO employees (name, position, salary) VALUES (?, ?, ?)";
pstmt = conn.prepareStatement(sql);
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
try {
// 添加多个任务到批处理中
for (int i = 1; i <= 1000; i++) {
pstmt.setString(1, "员工_" + i);
pstmt.setString(2, "职位_" + i);
pstmt.setDouble(3, 5000 + i * 10);
pstmt.addBatch(); // 将语句添加到批处理中
}
// 执行批处理
int[] results = pstmt.executeBatch();
System.out.println("Batch inserted " + results.length + " rows.");
// 提交事务
conn.commit();
System.out.println("Transaction committed.");
} catch (SQLException e) {
// 发生错误时回滚事务
conn.rollback();
System.err.println("Transaction rolled back.");
e.printStackTrace();
} finally {
// 恢复自动提交
conn.setAutoCommit(true);
// ... (关闭资源)
}
获取自增主键 ID
在插入数据后,通常需要获取新记录的自增主键 ID。PreparedStatement 提供了 getGeneratedKeys() 方法来实现这一点。
// ... (前面的连接代码相同)
String sql = "INSERT INTO employees (name, position) VALUES (?, ?)";
pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); // 关键参数
pstmt.setString(1, "王五");
pstmt.setString(2, "设计师");
pstmt.executeUpdate();
// 获取生成的键
try (ResultSet generatedKeys = pstmt.getGeneratedKeys()) {
if (generatedKeys.next()) {
// 获取第一列 (通常是 ID)
long newId = generatedKeys.getLong(1);
System.out.println("New employee inserted with ID: " + newId);
} else {
throw new SQLException("Failed to get ID, no ID obtained.");
}
}
常见问题与解决方案
-
java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver- 原因: MySQL 驱动 JAR 文件没有被正确添加到项目的类路径中。
- 解决: 检查你的构建工具(Maven/Gradle)配置,或者手动确保 JAR 文件在
CLASSPATH中。
-
java.sql.SQLException: Access denied for user 'root'@'localhost'- 原因: 数据库用户名或密码错误,或者该用户没有连接到指定数据库的权限。
- 解决: 检查
USER和PASS变量是否正确,登录 MySQL 命令行检查用户权限。
-
java.sql.SQLException: The server time zone value '�й���ʱ��' is unrecognized or represents more than one time zone.- 原因: JDBC 连接 URL 中没有指定时区,而服务器和客户端的默认时区不一致。
- 解决: 在数据库 URL 后面添加时区参数。
DB_URL = "jdbc:mysql://localhost:3306/my_company?serverTimezone=UTC";
-
java.sql.SQLException: No suitable driver found for jdbc:mysql://...- 原因: 驱动类没有被加载,或者 URL 格式不正确。
- 解决: 确保
Class.forName("com.mysql.cj.jdbc.Driver");被执行,URL 的格式正确(jdbc:mysql://...)。
