Java 数据库操作完整指南 (实例详解)
目录
- 引言:为什么需要 Java 连接数据库?
- 核心概念:JDBC (Java Database Connectivity)
- 什么是 JDBC?
- JDBC 四大核心组件
- 准备工作:环境配置
- 安装 JDK
- 添加数据库驱动
- 第一步:建立数据库连接
- 加载驱动
- 获取连接
- 代码实例
- 第二步:执行 SQL 语句 (核心操作)
Statement- 执行静态 SQL- 增、删、改 代码实例
- 查询 代码实例
PreparedStatement- 执行动态 SQL (推荐)- 为什么推荐使用
PreparedStatement? - 增、删、改 代码实例
- 查询 代码实例
- 为什么推荐使用
ResultSet- 处理查询结果
- 第三步:处理事务
- 什么是事务?
- ACID 特性
- 事务管理代码实例
- 第四步:资源释放 (至关重要)
- 为什么必须释放资源?
try-with-resources语句 (最佳实践)
- 完整项目示例:一个简单的用户管理系统
- 数据库表设计
- 项目结构
- 完整代码实现
- 现代替代方案:JPA / Hibernate
JDBC vs. JPA/Hibernate
(图片来源网络,侵删) - 总结与最佳实践
引言:为什么需要 Java 连接数据库?
在大多数企业级应用中,数据都需要被持久化存储,数据库是存储和管理数据的标准方式,Java 作为一种强大的后端语言,必须具备与数据库交互的能力,以实现数据的增、删、改、查,从而构建动态、功能丰富的应用程序。
核心概念:JDBC (Java Database Connectivity)
什么是 JDBC?
JDBC (Java Database Connectivity) 是 Java API 的一部分,它定义了一套标准,允许 Java 程序与各种关系型数据库(如 MySQL, Oracle, SQL Server)进行交互,你可以把它想象成 Java 和数据库之间的“翻译官”或“桥梁”。
JDBC 四大核心组件
- Driver (驱动):由数据库厂商提供,负责将 JDBC 的调用转换为特定数据库的底层协议。
mysql-connector-java.jarMySQL 的 JDBC 驱动。 - Connection (连接):代表与数据库的一个会话,通过连接,你可以向数据库发送命令并接收结果。
- Statement / PreparedStatement:用于向数据库发送 SQL 语句的“载体”。
Statement: 用于执行静态的、不带参数的 SQL。PreparedStatement: 用于执行动态的、带参数的 SQL,强烈推荐使用,因为它更安全、更高效。
- ResultSet (结果集):当执行查询语句后,数据库返回的结果被封装在
ResultSet对象中,它像一个指向数据行的“光标”,你可以遍历它来获取所有查询到的数据。
准备工作:环境配置
安装 JDK
确保你的系统已经安装了 Java Development Kit (JDK),并配置了 JAVA_HOME 和 PATH 环境变量。
添加数据库驱动
你需要下载你所使用的数据库的 JDBC 驱动程序(通常是一个 .jar 文件),并将其添加到你的 Java 项目的类路径中。

- 对于 Maven 项目:在
pom.xml文件中添加依赖。<!-- MySQL 8.x 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> <!-- 使用合适的版本 --> </dependency> - 对于普通 Java 项目:将下载的
.jar文件复制到项目的lib目录下,并添加到构建路径中。
第一步:建立数据库连接
这是所有数据库操作的第一步。
加载驱动
// 对于 MySQL 8.x
Class.forName("com.mysql.cj.jdbc.Driver");
// 对于 MySQL 5.x
// Class.forName("com.mysql.jdbc.Driver");
注意:从 JDBC 4.0 (Java 6) 开始,驱动加载步骤通常是可选的,因为驱动会自动注册,但显式写出可以确保代码的清晰和兼容性。
获取连接
使用 DriverManager 类的 getConnection() 方法。
String url = "jdbc:mysql://localhost:3306/your_database_name?useSSL=false&serverTimezone=UTC"; String user = "your_username"; String password = "your_password"; Connection connection = DriverManager.getConnection(url, user, password);
- URL 格式:
jdbc:mysql://主机名:端口号/数据库名?参数localhost: 数据库所在的主机地址。3306: MySQL 的默认端口号。your_database_name: 你要连接的数据库。useSSL=false: 禁用 SSL(用于本地开发)。serverTimezone=UTC: 设置服务器时区,避免警告。
代码实例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBConnection {
private static final String URL = "jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASSWORD = "password";
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("找不到 JDBC 驱动类。");
e.printStackTrace();
} catch (SQLException e) {
System.err.println("数据库连接失败。");
e.printStackTrace();
}
return connection;
}
}
第二步:执行 SQL 语句
这是数据库操作的核心,分为更新(增删改)和查询。

Statement - 执行静态 SQL
增、删、改 代码实例
import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;
public class StatementUpdateExample {
public static void main(String[] args) {
String sql = "INSERT INTO users (name, email) VALUES ('John Doe', 'john.doe@example.com')";
// 使用 try-with-resources 自动关闭 Statement 和 Connection
try (Connection conn = DBConnection.getConnection();
Statement stmt = conn.createStatement()) {
// executeUpdate() 返回受影响的行数
int affectedRows = stmt.executeUpdate(sql);
System.out.println("成功插入 " + affectedRows + " 行数据。");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
查询 代码实例
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class StatementQueryExample {
public static void main(String[] args) {
String sql = "SELECT id, name, email FROM users";
try (Connection conn = DBConnection.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) { // executeQuery() 用于查询
System.out.println("ID\tName\tEmail");
System.out.println("--------------------");
// 遍历结果集
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);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
PreparedStatement - 执行动态 SQL (推荐)
PreparedStatement 预先编译 SQL 语句,并使用 作为占位符,它可以防止 SQL 注入攻击,并且对于重复执行的 SQL,效率更高。
增、删、改 代码实例
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class PreparedStatementUpdateExample {
public static void main(String[] args) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
try (Connection conn = DBConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置参数 (索引从 1 开始)
pstmt.setString(1, "Jane Smith");
pstmt.setString(2, "jane.smith@example.com");
int affectedRows = pstmt.executeUpdate();
System.out.println("成功插入 " + affectedRows + " 行数据。");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
查询 代码实例
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class PreparedStatementQueryExample {
public static void main(String[] args) {
String sql = "SELECT * FROM users WHERE id = ?";
try (Connection conn = DBConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置参数
pstmt.setInt(1, 1); // 查询 id 为 1 的用户
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
System.out.println("找到用户: ID=" + rs.getInt("id")
+ ", Name=" + rs.getString("name"));
} else {
System.out.println("未找到 ID 为 1 的用户。");
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
ResultSet - 处理查询结果
ResultSet 提供了多种方法来获取不同类型的数据:
getInt(columnName): 获取整数getString(columnName): 获取字符串getDate(columnName): 获取日期getBoolean(columnName): 获取布尔值- 也可以使用列索引,如
getString(1)。
第三步:处理事务
事务是一组 SQL 操作,它们要么全部成功,要么全部失败,这对于保证数据一致性至关重要(银行转账)。
ACID 特性
- 原子性: 事务是一个不可分割的工作单位。
- 一致性: 事务必须使数据库从一个一致性状态变到另一个一致性状态。
- 隔离性: 一个事务的执行不能被其他事务干扰。
- 持久性: 一个事务一旦提交,它对数据库中数据的改变就是永久的。
事务管理代码实例
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TransactionExample {
public static void main(String[] args) {
Connection conn = null;
try {
conn = DBConnection.getConnection();
// 关闭自动提交,开启手动事务
conn.setAutoCommit(false);
// 转账操作:从 A 账户扣款,给 B 账户加款
String sql1 = "UPDATE accounts SET balance = balance - 100 WHERE name = 'Alice'";
String sql2 = "UPDATE accounts SET balance = balance + 100 WHERE name = 'Bob'";
try (PreparedStatement pstmt1 = conn.prepareStatement(sql1);
PreparedStatement pstmt2 = conn.prepareStatement(sql2)) {
pstmt1.executeUpdate();
// 模拟一个错误
// int x = 1 / 0; // 如果取消注释,事务将回滚
pstmt2.executeUpdate();
// 如果所有操作都成功,提交事务
conn.commit();
System.out.println("转账成功!");
} catch (SQLException e) {
// 如果发生异常,回滚事务
conn.rollback();
System.err.println("转账失败,事务已回滚。");
e.printStackTrace();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
// 恢复自动提交模式
conn.setAutoCommit(true);
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
第四步:资源释放 (至关重要)`
数据库连接、Statement 和 ResultSet 都是宝贵的系统资源,使用完毕后必须关闭,否则会导致连接泄露,最终使应用程序崩溃。
为什么必须释放资源?
数据库服务器能同时建立的连接数是有限的,如果程序不关闭连接,这些连接会被一直占用,直到程序结束,导致其他用户无法连接。
try-with-resources 语句 (最佳实践)
从 Java 7 开始,推荐使用 try-with-resources 语句,它会在 try 块执行完毕后,自动关闭实现了 AutoCloseable 接口的对象(如 Connection, Statement, ResultSet),无需手动调用 close()。
示例 (前面所有例子都已使用此模式)
// try-with-resources 会自动 conn, stmt, rs
try (Connection conn = DBConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
// ... 执行操作 ...
} catch (SQLException e) {
// ... 异常处理 ...
}
// conn, pstmt, rs 都已被自动关闭
完整项目示例:一个简单的用户管理系统
数据库表设计
CREATE DATABASE IF NOT EXISTS javadb;
USE javadb;
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
项目结构
UserManagementSystem/
├── src/
│ └── com/
│ └── example/
│ ├── dao/
│ │ └── UserDAO.java
│ ├── model/
│ │ └── User.java
│ └── Main.java
├── lib/
│ └── mysql-connector-java-8.0.28.jar (手动添加时)
└── pom.xml (Maven 项目)
完整代码实现
User.java (模型类)
package com.example.model;
import java.util.Date;
public class User {
private int id;
private String name;
private String email;
private Date createdAt;
// Getters and Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Date getCreatedAt() { return createdAt; }
public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; }
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", createdAt=" + createdAt +
'}';
}
}
UserDAO.java (数据访问对象)
package com.example.dao;
import com.example.model.User;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class UserDAO {
// 添加用户
public void addUser(User user) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
try (Connection conn = DBConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, user.getName());
pstmt.setString(2, user.getEmail());
pstmt.executeUpdate();
System.out.println("用户添加成功: " + user.getName());
} catch (SQLException e) {
e.printStackTrace();
}
}
// 根据ID查找用户
public User getUserById(int id) {
String sql = "SELECT * FROM users WHERE id = ?";
User user = null;
try (Connection conn = DBConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, id);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
user.setCreatedAt(rs.getTimestamp("created_at"));
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return user;
}
// 获取所有用户
public List<User> getAllUsers() {
List<User> users = new ArrayList<>();
String sql = "SELECT * FROM users";
try (Connection conn = DBConnection.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
user.setCreatedAt(rs.getTimestamp("created_at"));
users.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
}
Main.java (主程序)
package com.example;
import com.example.dao.UserDAO;
import com.example.model.User;
import java.util.List;
public class Main {
public static void main(String[] args) {
UserDAO userDAO = new UserDAO();
// 1. 添加一个新用户
User newUser = new User();
newUser.setName("Alice");
newUser.setEmail("alice@example.com");
userDAO.addUser(newUser);
// 2. 获取并显示所有用户
System.out.println("\n--- 所有用户列表 ---");
List<User> allUsers = userDAO.getAllUsers();
for (User user : allUsers) {
System.out.println(user);
}
// 3. 根据ID查找用户
System.out.println("\n--- 查找ID为1的用户 ---");
User foundUser = userDAO.getUserById(1);
if (foundUser != null) {
System.out.println(foundUser);
} else {
System.out.println("未找到该用户。");
}
}
}
现代替代方案:JPA / Hibernate
虽然 JDBC 是基础,但在现代开发中,直接编写大量的 JDBC 代码会变得繁琐且容易出错,出现了 ORM (Object-Relational Mapping) 框架。
- JPA (Java Persistence API): Java EE 提供的一套 ORM 规范,它定义了接口。
- Hibernate: 是 JPA 规范最流行的实现,你通过操作 Java 对象(如
User对象),Hibernate 会自动将其转换为 SQL 语句并执行。
JPA/Hibernate 的优势:
- 减少样板代码: 无需手动编写
INSERT,UPDATE,DELETE语句。 - 提高开发效率: 更关注业务逻辑,而非 SQL 细节。
- 数据库无关性: 更换数据库(如从 MySQL 换成 PostgreSQL)时,代码改动很小。
- 强大的查询功能: 提供 HQL (Hibernate Query Language) 和 Criteria API。
对于大型项目,强烈建议学习并使用 JPA/Hibernate 或 MyBatis 等框架。
总结与最佳实践
- 始终使用
PreparedStatement: 除非有特殊需求,否则永远不要使用Statement,它能有效防止 SQL 注入,并且性能更好。 - 永远使用
try-with-resources: 自动管理资源,避免内存和连接泄露。 - 合理使用事务: 对于需要保证数据一致性的操作(如转账、下单),务必使用事务。
- 使用 DAO 模式: 将数据库访问逻辑与业务逻辑分离,使代码结构更清晰、更易于维护。
- 考虑使用 ORM 框架: 对于复杂项目,JPA/Hibernate 可以极大地提高开发效率和代码质量。
- 配置连接池: 在生产环境中,不要直接使用
DriverManager,应使用如 HikariCP, C3P0 等连接池来管理数据库连接,以提高性能和稳定性。
希望这份详细的指南能帮助你掌握 Java 数据库操作!
