Java 链接数据库与基础增删改查操作详解
一、环境准备
1.1 核心工具与软件
JDK:推荐 JDK 8 及以上版本(本文使用 JDK 11),确保 Java 开发环境正常。
MySQL 数据库:推荐 MySQL 8.0 版本,需提前创建测试数据库和表(下文会提供建表语句)。
开发工具:IntelliJ IDEA(或 Eclipse),用于编写和运行 Java 代码。
数据库连接依赖:MySQL 官方提供的 JDBC 驱动(
mysql-connector-java),用于 Java 程序与 MySQL 通信。
1.2 依赖配置(Maven 方式)
pom.xml 中添加 JDBC 驱动依赖(无需手动下载 JAR 包):<!-- MySQL JDBC 驱动(MySQL 8.0+ 对应此依赖) --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.32</version> <!-- 版本可根据 MySQL 版本调整 --> <scope>runtime</scope></dependency>
1.3 测试数据库与表创建
java_db 和用户表 user:-- 1. 创建数据库(若不存在)CREATE DATABASE IF NOT EXISTS java_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;-- 2. 使用数据库USE java_db;-- 3. 创建用户表(存储用户ID、姓名、年龄、邮箱)CREATE TABLE IF NOT EXISTS user ( id INT PRIMARY KEY AUTO_INCREMENT, -- 主键,自增 name VARCHAR(50) NOT NULL, -- 姓名,非空 age INT, -- 年龄 email VARCHAR(100) UNIQUE -- 邮箱,唯一) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
二、Java 链接数据库的核心:JDBC
mysql-connector-java)。2.1 JDBC 连接数据库的 5 个核心步骤
加载 JDBC 驱动:MySQL 8.0+ 无需显式加载(驱动会自动注册),但需确保依赖已引入。
定义数据库连接 URL:格式为
jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2。useSSL=false:禁用 SSL(开发环境常用,生产环境需根据需求调整)。serverTimezone=UTC:设置时区(MySQL 8.0+ 必须指定,否则会报时区错误)。示例(本地 MySQL):
jdbc:mysql://localhost:3306/java_db?useSSL=false&serverTimezone=UTC关键参数说明:
创建数据库连接:通过
DriverManager.getConnection(url, username, password)获取Connection对象(数据库连接的核心对象)。执行 SQL 语句:通过
Connection创建Statement或PreparedStatement对象,执行 SQL 并处理结果。关闭资源:依次关闭
ResultSet(结果集)、Statement(SQL 执行对象)、Connection(数据库连接),避免资源泄漏。
2.2 基础连接示例(含工具类封装)
import java.sql.*;/**
* JDBC 工具类:封装数据库连接与资源关闭
*/public class JDBCUtils {
// 数据库连接参数(建议放在配置文件中,此处为演示简化)
private static final String URL = "jdbc:mysql://localhost:3306/java_db?useSSL=false&serverTimezone=UTC";
private static final String USERNAME = "root"; // 你的 MySQL 用户名
private static final String PASSWORD = "123456"; // 你的 MySQL 密码
/**
* 获取数据库连接
* @return Connection 对象(若连接失败,抛出 SQLException)
*/
public static Connection getConnection() throws SQLException {
Connection conn = null;
try {
// MySQL 8.0+ 无需显式加载驱动(Driver 类会自动注册)
// Class.forName("com.mysql.cj.jdbc.Driver"); // 旧版本需添加此句
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
System.out.println("数据库连接成功!");
} catch (SQLException e) {
System.out.println("数据库连接失败!");
throw e; // 抛出异常,由调用者处理
}
return conn;
}
/**
* 关闭资源(ResultSet + Statement + Connection)
* @param rs 结果集(可null)
* @param stmt SQL执行对象(可null)
* @param conn 数据库连接(可null)
*/
public static void closeResources(ResultSet rs, Statement stmt, Connection conn) {
// 关闭顺序:先ResultSet,再Statement,最后Connection(避免资源泄漏)
try {
if (rs != null) rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (stmt != null) stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null && !conn.isClosed()) conn.close();
System.out.println("数据库连接已关闭!");
} catch (SQLException e) {
e.printStackTrace();
}
}}注意:实际开发中,URL、USERNAME、PASSWORD应放在配置文件(如jdbc.properties)中,而非硬编码,方便后期维护。
三、基础增删改查(CRUD)操作实现
user 的增删改查操作。本文使用 PreparedStatement(而非 Statement),因为它能防止 SQL 注入,且性能更优(支持 SQL 预编译)。3.1 1. 新增操作(Create)
user 表插入一条新用户数据。import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;public class JDBCCreateDemo {
public static void main(String[] args) {
// 1. 声明需要关闭的资源(Connection、PreparedStatement)
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 2. 获取数据库连接
conn = JDBCUtils.getConnection();
// 3. 定义SQL(? 为占位符,避免SQL注入)
String sql = "INSERT INTO user (name, age, email) VALUES (?, ?, ?)";
// 4. 创建PreparedStatement对象(预编译SQL)
pstmt = conn.prepareStatement(sql);
// 5. 为占位符赋值(参数索引从1开始)
pstmt.setString(1, "张三"); // 第一个?:name
pstmt.setInt(2, 25); // 第二个?:age
pstmt.setString(3, "zhangsan@example.com"); // 第三个?:email
// 6. 执行SQL(新增/修改/删除用executeUpdate(),返回受影响的行数)
int affectedRows = pstmt.executeUpdate();
System.out.println("新增成功,受影响行数:" + affectedRows);
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 7. 关闭资源(调用工具类方法)
JDBCUtils.closeResources(null, pstmt, conn);
}
}}3.2 2. 查询操作(Read)
3.2.1 按 ID 查询单个用户
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;public class JDBCReadOneDemo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null; // 查询操作需要ResultSet存储结果
try {
conn = JDBCUtils.getConnection();
// 定义SQL:根据id查询用户
String sql = "SELECT id, name, age, email FROM user WHERE id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 1); // 查询id=1的用户
// 执行查询(用executeQuery(),返回ResultSet)
rs = pstmt.executeQuery();
// 处理结果集(rs.next()判断是否有下一条数据)
if (rs.next()) {
// 通过列名或列索引获取数据(推荐列名,避免索引变动导致错误)
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String email = rs.getString("email");
// 输出用户信息
System.out.println("查询到的用户:");
System.out.println("ID:" + id + ",姓名:" + name + ",年龄:" + age + ",邮箱:" + email);
} else {
System.out.println("未查询到该用户!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 关闭资源(需传入ResultSet)
JDBCUtils.closeResources(rs, pstmt, conn);
}
}}3.2.2 查询所有用户
// 替换SQL语句String sql = "SELECT id, name, age, email FROM user";// 处理结果集(循环获取所有数据)while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String email = rs.getString("email");
System.out.println("ID:" + id + ",姓名:" + name + ",年龄:" + age + ",邮箱:" + email);}3.3 3. 修改操作(Update)
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;public class JDBCUpdateDemo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JDBCUtils.getConnection();
// 定义SQL:修改id=1的用户年龄和邮箱
String sql = "UPDATE user SET age = ?, email = ? WHERE id = ?";
pstmt = conn.prepareStatement(sql);
// 为占位符赋值
pstmt.setInt(1, 26); // 新年龄
pstmt.setString(2, "zhangsan_new@example.com"); // 新邮箱
pstmt.setInt(3, 1); // 目标用户ID
// 执行修改(executeUpdate()返回受影响行数)
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
System.out.println("修改成功!");
} else {
System.out.println("修改失败,未找到该用户!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResources(null, pstmt, conn);
}
}}3.4 4. 删除操作(Delete)
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;public class JDBCDeleteDemo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = JDBCUtils.getConnection();
// 定义SQL:删除id=1的用户
String sql = "DELETE FROM user WHERE id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 1);
// 执行删除
int affectedRows = pstmt.executeUpdate();
if (affectedRows > 0) {
System.out.println("删除成功!");
} else {
System.out.println("删除失败,未找到该用户!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResources(null, pstmt, conn);
}
}}四、常见问题与优化建议
4.1 常见问题排查
“时区错误”:MySQL 8.0+ 必须在 URL 中添加
serverTimezone=UTC(或Asia/Shanghai)。“连接被拒绝”:检查 MySQL 是否启动、主机地址 / 端口号是否正确、用户是否有权限访问数据库。
“SQL 注入风险”:严禁使用
Statement,必须用PreparedStatement并通过占位符赋值。“资源泄漏”:确保在
finally中关闭所有资源(即使发生异常),或使用 Java 7+ 的try-with-resources语法自动关闭资源。
4.2 代码优化建议
- 使用配置文件存储连接参数:将
URL、USERNAME、PASSWORD放在jdbc.properties中,通过Properties类读取,避免硬编码。properties# jdbc.propertiesjdbc.url=jdbc:mysql://localhost:3306/java_db?useSSL=false&serverTimezone=UTCjdbc.username=rootjdbc.password=123456
读取方式:javapublic static Connection getConnection() throws SQLException, IOException { Properties props = new Properties(); // 读取配置文件(放在src/main/resources目录下) props.load(JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties")); String url = props.getProperty("jdbc.url"); String username = props.getProperty("jdbc.username"); String password = props.getProperty("jdbc.password"); return DriverManager.getConnection(url, username, password);} - 使用 try-with-resources 自动关闭资源:Java 7+ 提供的语法,无需手动在
finally中关闭资源(资源需实现AutoCloseable接口,JDBC 相关类均已实现)。java// 示例:查询操作(自动关闭conn、pstmt、rs)try (Connection conn = JDBCUtils.getConnection(); PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM user WHERE id = ?"); ResultSet rs = pstmt.executeQuery()) { // 业务逻辑...} catch (SQLException e) { e.printStackTrace();} - 使用数据库连接池:频繁创建 / 关闭
Connection会消耗大量资源,实际开发中需使用连接池(如 HikariCP、C3P0),复用连接对象,提升性能。
五、总结
掌握 JDBC 连接数据库的 5 个步骤,理解
Connection、PreparedStatement、ResultSet的作用。实现基础增删改查操作,重点关注
PreparedStatement的占位符用法(防 SQL 注入)。学会封装工具类、处理资源关闭,避免冗余代码和资源泄漏。