杰瑞科技汇

Oracle数据库与Java如何高效交互?

  1. 核心概念:JDBC 和 Oracle 驱动
  2. 环境准备:下载必要的软件和驱动
  3. 核心步骤:Java 连接 Oracle 数据库的详细代码
  4. 最佳实践:使用连接池和 try-with-resources
  5. 完整示例:一个可运行的完整项目
  6. 常见问题与解决方案

核心概念:JDBC 和 Oracle 驱动

JDBC (Java Database Connectivity)

JDBC 是 Java 提供的一套标准 API,用于执行 SQL 语句,它定义了如何从 Java 应用程序连接到各种数据库、执行查询、获取结果等,你可以把它想象成 Java 和数据库之间的“通用翻译官”。

Oracle数据库与Java如何高效交互?-图1
(图片来源网络,侵删)

Oracle JDBC 驱动

JDBC 只是一个标准,而 Oracle 驱动是实现这个标准的“具体翻译官”,Oracle 提供了官方的 JDBC 驱动程序(也称为 JDBC Driver 或 JDBC Thin Driver),它负责将 Java 的 JDBC 调用转换成 Oracle 数据库能够理解的协议。

Oracle 驱动主要有两种:

  • Thin Driver (瘦驱动):这是最常用的一种,它是一个纯 Java 驱动,不依赖 Oracle 客户端库,可以直接通过网络连接到数据库服务器,对于大多数应用来说,这是首选。
  • OCI (Oracle Call Interface) Driver (OCI 驱动):它需要本地库(oci.dllliboci.so),因此依赖于 Oracle 客户端安装,它通常用于需要高级功能(如高级数据类型、更高性能)的场景,但在配置上比 Thin 驱动复杂。

我们这里主要讲解最常用、最方便的 Thin Driver


环境准备

在开始编码之前,你需要准备好以下三样东西:

Oracle数据库与Java如何高效交互?-图2
(图片来源网络,侵删)

a) 安装并启动 Oracle 数据库

确保你有一台可以访问的 Oracle 数据库服务器(本地安装的 Oracle 11g, 12c, 19c, 21c 等),并且数据库服务已经启动,你需要知道以下连接信息:

  • 主机名localhost (如果是本地) 或数据库服务器的 IP 地址。
  • 端口:默认的 Oracle 端口是 1521
  • SID (System Identifier) 或 Service Name
    • SID:是数据库实例的唯一标识,是较老的概念,格式如 ORCL
    • Service Name:是 Oracle 推荐的新方式,格式可以更灵活,如 ORCLCDB
    • 你需要根据你的数据库配置来确定使用哪一个,连接字符串的格式会略有不同。

b) 下载 Oracle JDBC 驱动 (JAR 文件)

你需要从 Oracle 官网下载 JDBC 驱动的 JAR 包。

  1. 访问 Oracle 官方下载页面:Oracle JDBC Drivers
  2. 根据你的数据库版本选择合适的驱动,新版本的驱动可以兼容旧版本的数据库。
  3. 下载文件通常是一个 ZIP 压缩包,ojdbc8.jar (适用于 Java 8) 或 ojdbc11.jar (适用于 Java 11+)。
  4. 关键一步:将下载的 ojdbcX.jar 文件添加到你的 Java 项目的 Classpath 中。
    • 如果你使用 Maven:这是最推荐的方式,可以避免手动管理 JAR 文件。
    • 如果你使用 IDE (如 IntelliJ IDEA 或 Eclipse):右键项目 -> Build Path / Project Structure -> Libraries -> Add External JARs...,然后选择你下载的 ojdbcX.jar
    • 如果你使用命令行编译:使用 -cp 参数指定 JAR 文件路径,java -cp ".;ojdbc8.jar" YourMainClass (Windows) 或 java -cp ".:ojdbc8.jar" YourMainClass (Linux/macOS)。

c) 创建数据库用户和表

为了方便测试,我们可以在 Oracle 数据库中创建一个简单的用户和一张表。

-- 连接到你的数据库(使用 SQL*Plus 或其他工具)
-- 以 sysdba 身份登录
-- conn / as sysdba
-- 创建一个新用户(如果不存在)
CREATE USER java_user IDENTIFIED BY password123;
-- 授予该用户基本的数据库权限
GRANT CONNECT, RESOURCE TO java_user;
-- 切换到新用户
-- conn java_user/password123
-- 创建一张测试表
CREATE TABLE employees (
    id          NUMBER PRIMARY KEY,
    name        VARCHAR2(100) NOT NULL,
    email       VARCHAR2(100),
    hire_date   DATE
);
-- 插入一些测试数据
INSERT INTO employees (id, name, email, hire_date) VALUES (1, '张三', 'zhangsan@example.com', TO_DATE('2025-01-15', 'YYYY-MM-DD'));
INSERT INTO employees (id, name, email, hire_date) VALUES (2, '李四', 'lisi@example.com', TO_DATE('2025-05-20', 'YYYY-MM-DD'));
COMMIT;

核心步骤:Java 连接代码

以下是使用 Java 连接 Oracle 数据库并执行查询的标准步骤。

Oracle数据库与Java如何高效交互?-图3
(图片来源网络,侵删)

步骤 1:加载驱动

虽然在新版本的 JDBC (JDBC 4.0+) 中,这一步通常是可选的(驱动会自动注册),但显式加载仍然是一个好习惯。

Class.forName("oracle.jdbc.OracleDriver");

步骤 2:建立连接

使用 DriverManager.getConnection() 方法,并提供正确的连接 URL、用户名和密码。

连接 URL 格式

  • 使用 SID: jdbc:oracle:thin:@<hostname>:<port>:<SID> jdbc:oracle:thin:@localhost:1521:ORCL
  • 使用 Service Name (推荐): jdbc:oracle:thin:@//<hostname>:<port>/<service_name> jdbc:oracle:thin:@//localhost:1521/ORCLCDB
String dbUrl = "jdbc:oracle:thin:@//localhost:1521/ORCLCDB";
String dbUser = "java_user";
String dbPassword = "password123";
Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword);

步骤 3:创建 Statement

通过 Connection 对象创建一个 StatementPreparedStatement 对象,用于执行 SQL 语句。强烈推荐使用 PreparedStatement,因为它可以防止 SQL 注入攻击,并且性能更好。

String sql = "SELECT id, name, email, hire_date FROM employees WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 1); // 设置第一个参数为 id=1

步骤 4:执行查询

使用 executeQuery() 方法执行查询,返回一个 ResultSet 对象。

ResultSet rs = pstmt.executeQuery();

步骤 5:处理结果集

遍历 ResultSet 对象,获取查询结果。

while (rs.next()) {
    // 通过列名获取数据,更安全、可读性更好
    int id = rs.getInt("id");
    String name = rs.getString("name");
    String email = rs.getString("email");
    Date hireDate = rs.getDate("hire_date");
    System.out.println("ID: " + id + ", Name: " + name + ", Email: " + email + ", Hire Date: " + hireDate);
}

步骤 6:关闭资源

非常重要! 在使用完 ResultSetStatementConnection 后,必须将它们关闭,以释放数据库资源,顺序是:先关闭 ResultSet,再关闭 Statement,最后关闭 Connection

rs.close();
pstmt.close();
conn.close();

最佳实践:使用连接池和 try-with-resources

直接使用 DriverManager 获取连接在每次请求时都会创建一个新的物理连接,性能很差,在高并发应用中,必须使用连接池

连接池

连接池(如 HikariCP, DBCP, C3P0)预先创建一组数据库连接,并将它们放入池中,当应用需要连接时,从池中获取一个,用完后归还给池,而不是销毁,这极大地提高了性能。

HikariCP 是目前性能最好的连接池,是 Spring Boot 的默认选择。

try-with-resources 语句

从 Java 7 开始,引入了 try-with-resources 语句,它可以自动实现 AutoCloseable 接口的资源(如 Connection, Statement, ResultSet)在 try 块执行完毕后自动关闭,即使发生异常也能保证关闭,从而避免了资源泄漏。

结合连接池和 try-with-resources 的现代写法:

// 1. 配置 HikariCP 连接池
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:oracle:thin:@//localhost:1521/ORCLCDB");
config.setUsername("java_user");
config.setPassword("password123");
// 其他优化配置...
config.setMaximumPoolSize(10);
HikariDataSource ds = new HikariDataSource(config);
// 2. 使用 try-with-resources 获取连接和执行操作
String sql = "SELECT id, name, email FROM employees";
try (Connection conn = ds.getConnection();
     PreparedStatement pstmt = conn.prepareStatement(sql);
     ResultSet rs = pstmt.executeQuery()) {
    while (rs.next()) {
        System.out.println("Name: " + rs.getString("name") + ", Email: " + rs.getString("email"));
    }
} catch (SQLException e) {
    e.printStackTrace();
} // conn, pstmt, rs 会在这里自动关闭

注意HikariDataSource 本身不需要在每次使用后关闭,它通常作为应用的全局单例存在,在应用关闭时再销毁。


完整示例 (Maven 项目)

这是一个使用 Maven 和 HikariCP 的完整、可运行的示例。

a) pom.xml

创建一个 Maven 项目,并添加以下依赖。

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>oracle-java-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- Oracle JDBC Driver -->
        <dependency>
            <groupId>com.oracle.database.jdbc</groupId>
            <artifactId>ojdbc11</artifactId>
            <version>21.9.0.0</version>
        </dependency>
        <!-- HikariCP Connection Pool -->
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>5.0.1</version>
        </dependency>
    </dependencies>
</project>

b) OracleJdbcDemo.java

src/main/java 目录下创建这个类。

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Date;
public class OracleJdbcDemo {
    // 数据库连接信息 - 最好放在配置文件中
    private static final String DB_URL = "jdbc:oracle:thin:@//localhost:1521/ORCLCDB";
    private static final String DB_USER = "java_user";
    private static final String DB_PASSWORD = "password123";
    public static void main(String[] args) {
        // 1. 创建 HikariCP 连接池
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(DB_URL);
        config.setUsername(DB_USER);
        config.setPassword(DB_PASSWORD);
        // 性能优化配置
        config.setMaximumPoolSize(10);
        config.setMinimumIdle(5);
        config.setConnectionTimeout(30000); // 30 seconds
        config.setIdleTimeout(600000); // 10 minutes
        config.setMaxLifetime(1800000); // 30 minutes
        HikariDataSource dataSource = new HikariDataSource(config);
        // 2. 使用 try-with-resources 获取连接并执行查询
        // 查询所有员工
        queryAllEmployees(dataSource);
        // 查询特定ID的员工
        queryEmployeeById(dataSource, 1);
        // 3. 在应用关闭时关闭数据源
        dataSource.close();
    }
    public static void queryAllEmployees(HikariDataSource dataSource) {
        String sql = "SELECT id, name, email, hire_date FROM employees";
        System.out.println("--- Querying All Employees ---");
        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                Date hireDate = rs.getDate("hire_date");
                System.out.printf("ID: %d, Name: %s, Email: %s, Hire Date: %s%n", id, name, email, hireDate);
            }
        } catch (SQLException e) {
            System.err.println("Error querying employees: " + e.getMessage());
        }
    }
    public static void queryEmployeeById(HikariDataSource dataSource, int employeeId) {
        String sql = "SELECT id, name, email FROM employees WHERE id = ?";
        System.out.printf("--- Querying Employee with ID: %d ---%n", employeeId);
        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, employeeId);
            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    String email = rs.getString("email");
                    System.out.printf("Found Employee -> ID: %d, Name: %s, Email: %s%n", id, name, email);
                } else {
                    System.out.println("No employee found with ID: " + employeeId);
                }
            }
        } catch (SQLException e) {
            System.err.println("Error querying employee by ID: " + e.getMessage());
        }
    }
}

常见问题与解决方案

问题 1:java.lang.ClassNotFoundException: oracle.jdbc.OracleDriver

原因:JDBC 驱动的 JAR 文件没有被添加到 Classpath 中。 解决方案:检查你的构建工具(Maven/Gradle)或 IDE 是否正确引入了 ojdbcX.jar

问题 2:java.sql.SQLException: No suitable driver found for jdbc:oracle:thin:@...

原因

  1. 驱动未加载(虽然少见)。
  2. JDBC URL 格式错误(SIDService Name 混用,或拼写错误)。 解决方案:仔细检查 JDBC URL 的格式是否与你的数据库配置匹配。

问题 3:java.sql.SQLException: Io 异常: The Network Adapter could not establish the connection

原因:网络问题,Java 应用无法连接到指定的主机和端口。 解决方案

  1. 检查主机名和端口号是否正确。
  2. 确保数据库监听器正在运行,在 Oracle 服务器上,可以使用 lsnrctl status 命令检查。
  3. 检查防火墙是否阻止了 1521 端口。

问题 4:java.sql.SQLException: ORA-01017: invalid username/password; logon denied

原因:用户名或密码错误。 解决方案:确认你使用的用户名和密码是正确的,并且该用户有权限连接到数据库。

问题 5:java.sql.SQLException: ORA-00942: table or view does not exist

原因:SQL 语句中表名或视图名拼写错误,或者当前用户没有访问该表的权限。 解决方案:检查表名拼写是否正确,并确认用户是否有 SELECT 权限。

分享:
扫描分享到社交APP
上一篇
下一篇