Java JDBC Example 深度解析
简介
在Java开发中,与数据库进行交互是非常常见的需求。Java Database Connectivity(JDBC)提供了一种标准的方式来实现Java程序与各种数据库之间的通信。本文将通过详细的示例,深入探讨JDBC的基础概念、使用方法、常见实践以及最佳实践,帮助读者全面掌握JDBC在Java项目中的应用。
目录
- 基础概念
- JDBC 是什么
- JDBC 架构
- 使用方法
- 加载 JDBC 驱动
- 建立数据库连接
- 创建 SQL 语句执行对象
- 执行 SQL 语句
- 处理结果集
- 关闭资源
- 常见实践
- 插入数据
- 查询数据
- 更新数据
- 删除数据
- 最佳实践
- 连接池的使用
- 事务管理
- 防止 SQL 注入
- 小结
- 参考资料
基础概念
JDBC 是什么
JDBC 是 Java 编程语言中用于执行 SQL 语句的 API。它为开发人员提供了一组标准的接口,使得 Java 程序能够与不同类型的数据库(如 MySQL、Oracle、SQL Server 等)进行交互,而无需关心底层数据库的具体实现细节。
JDBC 架构
JDBC 架构主要由以下几个部分组成: - JDBC API:提供给 Java 开发人员使用的接口,包括 Connection、Statement、ResultSet 等,用于与数据库进行交互。 - JDBC Driver API:供驱动开发人员实现的接口,不同的数据库厂商需要根据这些接口来开发自己的 JDBC 驱动。 - JDBC 驱动:负责与具体的数据库进行通信,将 JDBC API 的调用转换为数据库特定的操作。
使用方法
下面通过一个完整的示例来展示 JDBC 的基本使用流程。假设我们使用 MySQL 数据库,首先需要下载并添加 MySQL JDBC 驱动到项目的类路径中。
加载 JDBC 驱动
在 Java 中,我们可以使用 Class.forName()
方法来加载 JDBC 驱动。
try {
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("JDBC驱动加载成功");
} catch (ClassNotFoundException e) {
System.out.println("JDBC驱动加载失败");
e.printStackTrace();
}
建立数据库连接
使用 DriverManager.getConnection()
方法来建立与数据库的连接。需要提供数据库的 URL、用户名和密码。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JdbcExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
System.out.println("数据库连接成功");
// 后续操作
connection.close();
} catch (SQLException e) {
System.out.println("数据库连接失败");
e.printStackTrace();
}
}
}
创建 SQL 语句执行对象
通过 Connection
对象创建 Statement
或 PreparedStatement
对象,用于执行 SQL 语句。Statement
用于执行普通的 SQL 语句,PreparedStatement
用于执行预编译的 SQL 语句,能有效防止 SQL 注入。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
Statement statement = connection.createStatement();
// 后续操作
statement.close();
connection.close();
} catch (SQLException e) {
System.out.println("数据库操作失败");
e.printStackTrace();
}
}
}
执行 SQL 语句
使用 Statement
或 PreparedStatement
的 executeQuery()
方法执行查询语句,executeUpdate()
方法执行插入、更新和删除语句。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
Statement statement = connection.createStatement();
String sql = "SELECT * FROM users";
ResultSet resultSet = statement.executeQuery(sql);
// 处理结果集
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
System.out.println("数据库操作失败");
e.printStackTrace();
}
}
}
处理结果集
使用 ResultSet
对象来处理查询结果。可以通过 next()
方法移动到下一行记录,并使用 getXXX()
方法获取不同类型的列值。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(url, username, password);
Statement statement = connection.createStatement();
String sql = "SELECT * FROM users";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
System.out.println("ID: " + id + ", Name: " + name);
}
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
System.out.println("数据库操作失败");
e.printStackTrace();
}
}
}
关闭资源
在完成数据库操作后,需要关闭 ResultSet
、Statement
和 Connection
对象,以释放资源。
try {
if (resultSet != null) resultSet.close();
if (statement != null) statement.close();
if (connection != null) connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
常见实践
插入数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class InsertExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
String sql = "INSERT INTO users (name, age) VALUES (?,?)";
try (Connection connection = DriverManager.getConnection(url, username, password);
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
preparedStatement.setString(1, "John Doe");
preparedStatement.setInt(2, 30);
int rowsInserted = preparedStatement.executeUpdate();
if (rowsInserted > 0) {
System.out.println("数据插入成功");
}
} catch (SQLException e) {
System.out.println("数据插入失败");
e.printStackTrace();
}
}
}
查询数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class QueryExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
String sql = "SELECT * FROM users WHERE age >?";
try (Connection connection = DriverManager.getConnection(url, username, password);
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
preparedStatement.setInt(1, 25);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age);
}
} catch (SQLException e) {
System.out.println("数据查询失败");
e.printStackTrace();
}
}
}
更新数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class UpdateExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
String sql = "UPDATE users SET age =? WHERE id =?";
try (Connection connection = DriverManager.getConnection(url, username, password);
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
preparedStatement.setInt(1, 35);
preparedStatement.setInt(2, 1);
int rowsUpdated = preparedStatement.executeUpdate();
if (rowsUpdated > 0) {
System.out.println("数据更新成功");
}
} catch (SQLException e) {
System.out.println("数据更新失败");
e.printStackTrace();
}
}
}
删除数据
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DeleteExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
String sql = "DELETE FROM users WHERE id =?";
try (Connection connection = DriverManager.getConnection(url, username, password);
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
preparedStatement.setInt(1, 2);
int rowsDeleted = preparedStatement.executeUpdate();
if (rowsDeleted > 0) {
System.out.println("数据删除成功");
}
} catch (SQLException e) {
System.out.println("数据删除失败");
e.printStackTrace();
}
}
}
最佳实践
连接池的使用
连接池可以预先创建一定数量的数据库连接,并在需要时复用这些连接,从而减少连接创建和销毁的开销,提高系统性能。常见的连接池有 HikariCP、C3P0、DBCP 等。
以下是使用 HikariCP 的示例:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class ConnectionPoolExample {
private static HikariDataSource dataSource;
static {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("password");
dataSource = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void main(String[] args) {
try (Connection connection = getConnection()) {
System.out.println("从连接池获取到连接");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
事务管理
事务是一组不可分割的数据库操作序列,要么全部执行成功,要么全部失败回滚。在 JDBC 中,可以通过 Connection
对象的 setAutoCommit(false)
和 commit()
、rollback()
方法来实现事务管理。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TransactionExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
String sql1 = "INSERT INTO users (name, age) VALUES ('Alice', 28)";
String sql2 = "UPDATE users SET age = age + 1 WHERE name = 'Alice'";
try (Connection connection = DriverManager.getConnection(url, username, password)) {
connection.setAutoCommit(false);
try (PreparedStatement preparedStatement1 = connection.prepareStatement(sql1);
PreparedStatement preparedStatement2 = connection.prepareStatement(sql2)) {
preparedStatement1.executeUpdate();
preparedStatement2.executeUpdate();
connection.commit();
System.out.println("事务执行成功");
} catch (SQLException e) {
connection.rollback();
System.out.println("事务回滚");
e.printStackTrace();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
防止 SQL 注入
使用 PreparedStatement
而不是 Statement
可以有效防止 SQL 注入。PreparedStatement
会对传入的参数进行预编译,将参数值与 SQL 语句分开处理,从而避免恶意 SQL 语句的执行。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class AntiSqlInjectionExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydb";
String username = "root";
String password = "password";
String sql = "SELECT * FROM users WHERE name =?";
String input = "John' OR '1'='1"; // 模拟恶意输入
try (Connection connection = DriverManager.getConnection(url, username, password);
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
preparedStatement.setString(1, input);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
System.out.println("ID: " + id + ", Name: " + name);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
小结
本文详细介绍了 Java JDBC 的基础概念、使用方法、常见实践以及最佳实践。通过示例代码展示了如何进行数据库连接、执行 SQL 语句、处理结果集以及如何优化 JDBC 应用。掌握这些知识,能够帮助开发人员更加高效、安全地实现 Java 程序与数据库的交互,提升项目的质量和性能。
参考资料
- Oracle JDBC 官方文档
- MySQL JDBC 驱动官方文档
- 《Effective Java》第 3 版
- 《Java核心技术》第 10 版