目录
- 准备工作
- 安装 Oracle 数据库并创建测试用户和表
- 下载 Oracle JDBC 驱动
- 核心概念:JDBC 驱动类型
- OCI (Oracle Call Interface)
- Thin Driver (纯 Java 驱动)
- 代码实现
- 使用
ojdbc8.jar(JDBC 4.2 / Java 8+)- 传统方式 (
Class.forName) - 现代方式 (自动加载)
- 完整的
try-with-resources示例 (推荐)
- 传统方式 (
- 使用
ojdbc11.jar(JDBC 4.3 / Java 11+)完整示例
(图片来源网络,侵删)
- 使用
- 最佳实践
- 使用连接池 (Connection Pooling)
- 为什么需要连接池?
- 使用 HikariCP (目前性能最好的连接池)
- 使用
try-with-resources - 使用
PreparedStatement防止 SQL 注入 - 处理异常
- 使用连接池 (Connection Pooling)
- 常见问题与解决方案
ClassNotFoundException或NoClassDefFoundErrorInvalid URL formatIO Error: The Network Adapter could not establish connectionORA-01017: invalid username/password; logon denied
准备工作
a. 安装 Oracle 数据库并创建测试用户和表
确保你已经有一个可运行的 Oracle 数据库实例(如 Oracle Express Edition),以 sys 或 system 用户登录,创建一个测试用户和一张表。
-- 1. 创建用户 (请替换 'your_password' 为一个强密码)
CREATE USER java_user IDENTIFIED BY your_password;
-- 2. 授予用户权限
GRANT CONNECT, RESOURCE TO java_user;
-- 3. 切换到新创建的用户
CONN java_user/your_password@localhost:1521/XE -- 注意:XE是OracleXE的默认服务名,根据你的实际情况修改
-- 4. 创建测试表
CREATE TABLE employees (
id NUMBER PRIMARY KEY,
name VARCHAR2(100),
email VARCHAR2(100),
salary NUMBER(10, 2)
);
-- 5. 插入一些测试数据
INSERT INTO employees (id, name, email, salary) VALUES (1, 'Alice', 'alice@example.com', 7000);
INSERT INTO employees (id, name, email, salary) VALUES (2, 'Bob', 'bob@example.com', 8000);
COMMIT;
b. 下载 Oracle JDBC 驱动
你需要从 Oracle 官网下载 JDBC 驱动,根据你的 Java 版本选择合适的版本:
- 对于 Java 8 (JDBC 4.2): 下载
ojdbc8.jar - 对于 Java 11 及以上 (JDBC 4.3): 下载
ojdbc11.jar
下载地址: Oracle JDBC Drivers
下载后,将 .jar 文件添加到你的 Java 项目的类路径中,如果你使用的是 IDE(如 IntelliJ IDEA 或 Eclipse),可以直接将 JAR 文件添加到项目的 Libraries 中。

核心概念:JDBC 驱动类型
Oracle 提供了两种主要的 JDBC 驱动:
a. OCI (Oracle Call Interface)
- 特点: 它是一个本机库(如 Windows 下的
.dll或 Linux 下的.so文件),依赖于 Oracle 客户端软件,它可以使用本机优化(如 OCI 网络协议)。 - URL 格式:
jdbc:oracle:oci:@<database_name> - 缺点: 部署复杂,需要在客户端机器上安装 Oracle 客户端,不适合跨平台部署。
b. Thin Driver (纯 Java 驱动)
- 特点: 100% 用 Java 编写,不依赖任何本地库,它通过标准的 TCP/IP 套接字与数据库通信。
- URL 格式:
jdbc:oracle:thin:@<host>:<port>:<service_name_or_sid> - 优点: 部署简单,只需一个 JAR 文件,是 Web 应用和大多数场景下的首选。
本文将重点介绍 Thin Driver,因为它是最常用和最方便的。
代码实现
使用 ojdbc8.jar (Java 8+)
a. 传统方式 (不推荐,但为了知识完整性)
在 JDBC 4.0 之前,必须显式加载驱动类。
import java.sql.*;
public class OracleJDBCOld {
public static void main(String[] args) {
// 数据库连接信息
String dbURL = "jdbc:oracle:thin:@localhost:1521:XE"; // XE是服务名
String dbUser = "java_user";
String dbPassword = "your_password";
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1. 加载 JDBC 驱动 (旧版方式)
Class.forName("oracle.jdbc.driver.OracleDriver");
// 2. 获取数据库连接
conn = DriverManager.getConnection(dbURL, dbUser, dbPassword);
// 3. 创建 Statement 对象
stmt = conn.createStatement();
// 4. 执行查询
String sql = "SELECT id, name, email FROM employees";
rs = stmt.executeQuery(sql);
// 5. 处理结果集
System.out.println("ID\tName\tEmail");
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 (ClassNotFoundException e) {
System.err.println("找不到 Oracle JDBC 驱动类。");
e.printStackTrace();
} catch (SQLException e) {
System.err.println("数据库连接或查询出错。");
e.printStackTrace();
} finally {
// 6. 关闭资源 (非常重要!)
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
b. 现代方式 (自动加载)
从 JDBC 4.0 (Java 6+) 开始,DriverManager 会自动在类路径中查找所有实现了 java.sql.Driver 接口的类。Class.forName() 变成了可选的。但显式调用仍然是一个好习惯,因为它明确了你的意图。

c. 完整的 try-with-resources 示例 (强烈推荐)
try-with-resources 是 Java 7 引入的一个特性,它可以自动实现 AutoCloseable 接口的对象(如 Connection, Statement, ResultSet)的关闭,使代码更简洁、安全。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class OracleJDBCModern {
// 数据库连接信息,建议放在配置文件中
private static final String DB_URL = "jdbc:oracle:thin:@localhost:1521:XE";
private static final String DB_USER = "java_user";
private static final String DB_PASSWORD = "your_password";
public static void main(String[] args) {
// try-with-resources 会自动关闭 Connection, Statement, 和 ResultSet
try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, name, email FROM employees")) {
System.out.println("ID\tName\tEmail");
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) {
System.err.println("数据库操作失败: " + e.getMessage());
e.printStackTrace();
}
}
}
使用 ojdbc11.jar (Java 11+)
使用 ojdbc11.jar 与 ojdbc8.jar 在代码层面几乎没有区别,主要区别在于驱动类名和 Java 版本兼容性。
- 驱动类名:
oracle.jdbc.OracleDriver(与ojdbc8相同) - JDBC 版本: 支持 JDBC 4.3,
try-with-resources是标准用法。
代码示例与上面的 OracleJDBCModern 完全一致,只需确保你的项目使用的是 Java 11+ 并且依赖的是 ojdbc11.jar。
最佳实践
a. 使用连接池 (Connection Pooling)
为什么需要连接池? 每次应用程序需要与数据库交互时,都创建一个新的连接是一个昂贵的操作(涉及网络握手、身份验证等),连接池通过在应用程序启动时创建一组数据库连接并复用它们,极大地提高了性能和可伸缩性。
使用 HikariCP (推荐) HikariCP 是目前公认性能最高、最可靠的 JDBC 连接池实现。
步骤:
-
添加 HikariCP 依赖 (Maven):
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> <!-- 使用最新版本 --> </dependency> -
配置并使用 HikariCP:
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class OracleJDBCPooled { private static HikariDataSource dataSource; static { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:oracle:thin:@localhost:1521:XE"); config.setUsername("java_user"); config.setPassword("your_password"); // 连接池配置 (可选,推荐设置) config.setDriverClassName("oracle.jdbc.driver.OracleDriver"); config.setMaximumPoolSize(10); // 最大连接数 config.setMinimumIdle(5); // 最小空闲连接数 config.setConnectionTimeout(30000); // 连接超时时间 (ms) config.setIdleTimeout(600000); // 空闲连接超时时间 (ms) config.setMaxLifetime(1800000); // 连接最大存活时间 (ms) dataSource = new HikariDataSource(config); } public static void main(String[] args) { // 从连接池获取连接 try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT name FROM employees WHERE id = 1")) { if (rs.next()) { String name = rs.getString("name"); System.out.println("Employee name: " + name); } } catch (SQLException e) { System.err.println("数据库操作失败: " + e.getMessage()); e.printStackTrace(); } } }
b. 使用 try-with-resources
如上所示,始终使用 try-with-resources 来管理 JDBC 资源,可以防止资源泄漏。
c. 使用 PreparedStatement 防止 SQL 注入
对于任何包含用户输入的 SQL 查询,都应使用 PreparedStatement。
// 错误方式 (易受SQL注入攻击)
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
// 正确方式 (使用PreparedStatement)
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, username); // 设置第一个 '?' 的值
pstmt.setString(2, password); // 设置第二个 '?' 的值
try (ResultSet rs = pstmt.executeQuery()) {
// 处理结果...
}
}
d. 处理异常
总是捕获 SQLException,并根据情况记录日志或向用户显示友好的错误信息。
常见问题与解决方案
a. ClassNotFoundException 或 NoClassDefFoundError
- 原因: JVM 在运行时找不到
oracle.jdbc.driver.OracleDriver类。 - 解决方案: 确保
ojdbcX.jar文件已经正确地添加到了项目的类路径中,检查 IDE 的项目结构或构建工具(Maven/Gradle)的配置。
b. Invalid URL format
- 原因: JDBC URL 格式不正确。
- 解决方案:
- 检查协议:
jdbc:oracle:thin: - 检查主机、端口、服务名/SID:
@host:port:service_name或@host:port:sid,确保端口号和数据库标识符正确。 - 区分 SID 和 Service Name:
- SID:
jdbc:oracle:thin:@localhost:1521:ORCL(旧方式) - Service Name:
jdbc:oracle:thin:@localhost:1521:ORCL.example.com(现代方式,推荐)
- SID:
- 使用 SQL*Plus 或 SQL Developer 的 "Test" 功能可以验证连接字符串是否正确。
- 检查协议:
c. IO Error: The Network Adapter could not establish connection
- 原因: 无法通过网络连接到数据库监听器。
- 解决方案:
- 检查数据库服务: 确认 Oracle 数据库实例(如
ORCL或XE)正在运行。 - 检查监听器: 登录到数据库服务器,运行
lsnrctl status命令,确认监听器正在监听正确的端口(通常是 1521)。 - 检查防火墙: 确保运行 Java 应用程序的机器可以访问数据库服务器的 1521 端口。
- 检查主机名: 如果数据库和应用程序不在同一台机器上,确保
localhost被替换为数据库服务器的实际 IP 地址或主机名。
- 检查数据库服务: 确认 Oracle 数据库实例(如
d. ORA-01017: invalid username/password; logon denied
- 原因: 提供的用户名或密码错误。
- 解决方案: 仔细核对
DB_USER和DB_PASSWORD变量中的值,确保没有拼写错误,检查用户是否被锁定或密码已过期。
