25、JDBC 与 Java 数据对象全解析

JDBC 与 Java 数据对象全解析

1. 数据检索与显示

1.1 数据检索

在与数据库管理系统(DBMS)交互时,我们常常需要从数据库中检索特定的数据。可以通过指定列名或列号来获取想要的值,这里以列名为例。 getString() 方法可用于返回指定列的值,并将其赋值给程序中预先声明的字符串变量。

以下是一个示例代码,展示了如何使用 getString() 方法从结果集中获取 FirstName LastName 列的值,并将它们拼接后显示在屏幕上:

String firstName;
String lastName;
String printRow;
boolean records = results.next();
if (!records ) {
    System.out.println( "No data returned");
    return;
}
else
{
    do {
        firstName = results.getString ("FirstName");
        lastName = results.getString ("LastName");
        printRow = firstName + " " + lastName;
        System.out.println(printRow);
    }  while ( results.next() );
}

1.2 关闭数据库连接

在完成数据检索和处理后,需要关闭 ResultSet 实例和与 DBMS 的连接。可以通过调用 close() 方法来实现:

result.close();
dB.close();

虽然在关闭与 DBMS 的连接时, ResultSet 实例会自动关闭,但许多程序员还是倾向于显式地关闭它。

2. 异常处理

2.1 加载驱动时的异常处理

在加载驱动时, Class.forName() 方法可能会抛出 ClassNotFoundException 异常。可以使用 catch 块来捕获该异常,并显示异常描述信息,然后终止程序:

try {
    Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver");
}
catch (ClassNotFoundException e) {
    System.err.println(
        "Unable to load the JDBC/ODBC bridge." + e.getMessage());
    System.exit(1);
}

2.2 连接数据库时的异常处理

在尝试连接到 DBMS 时,可能会出现各种问题,如数据库 URL 不准确、用户 ID 或密码错误等。 getConnection() 方法会抛出 SQLException 异常。可以使用 catch 块来捕获该异常,并显示异常信息,然后终止程序:

String url = "jdbc:odbc:Registration";
String userID = "jim";
String password = "keogh";
Connection dB;
try {
    Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver");
    dB = DriverManager.getConnection(url,userID,password);
}
catch (ClassNotFoundException e) {
    System.err.println(
        "Unable to load the JDBC/ODBC bridge." + e);
    System.exit(1);
}
catch (SQLException e) {
    System.err.println(
        "Cannot connect to the database." + e);
    System.exit(2);
}
finally {
    dB.close();
}

3. 避免连接超时

在实际应用中,当程序尝试连接到 DBMS 时,DBMS 可能不可用,这通常是由于此时对 DBMS 的需求过高导致的。为了避免程序无限期等待,可以设置一个超时时间,当超过该时间后, DriverManager 将停止尝试连接。

3.1 设置超时时间

可以通过调用 DriverManager.setLoginTimeout() 方法来设置超时时间,该方法需要传入等待的秒数:

DriverManager.setLoginTimeout(10); // 设置超时时间为 10 秒

3.2 获取当前超时时间

可以通过调用 DriverManager.getLoginTimeout() 方法来获取当前的超时时间,该方法返回一个整数,表示 DriverManager 等待的秒数:

int timeout = DriverManager.getLoginTimeout();
System.out.println("当前超时时间: " + timeout + " 秒");

4. 不同类型的 Statement 对象

4.1 三种 Statement 对象

有三种类型的 Statement 对象可用于执行查询: Statement PreparedStatement CallableStatement 。它们的特点如下表所示:
| Statement 对象类型 | 特点 |
| — | — |
| Statement | 立即执行查询,查询在 DBMS 执行前会被编译 |
| PreparedStatement | 用于执行预编译的查询,适用于多次执行相似查询的场景 |
| CallableStatement | 用于执行存储过程 |

4.2 Statement 对象的使用

当需要立即执行查询时,可以使用 Statement 对象。通过调用 executeQuery() 方法来执行查询,如果查询是插入、更新或删除行,则使用 executeUpdate() 方法。

4.2.1 executeQuery() 示例
String url = "jdbc:odbc:Registration";
String userID = "jim";
String password = "keogh";
Statement dataRequest;
ResultSet results;
Connection dB;
try {
    Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver");
    dB = DriverManager.getConnection(url,userID,password);
}
catch (ClassNotFoundException e) {
    System.err.println(
        "Unable to load the JDBC/ODBC bridge." + e);
    System.exit(1);
}
catch (SQLException e) {
    System.err.println(
        "Cannot connect to the database." + e);
    System.exit(2);
}
try {
    String query = "SELECT * FROM Registration";
    dataRequest = dB.createStatement();
    results = dataRequest.executeQuery (query);
    //Place code here to interact with the ResultSet
}
catch ( SQLException e ){
    System.err.println("SQL error." + e);
    System.exit(3);
}
finally {
    dataRequest.close();
    dB.close();
}
4.2.2 executeUpdate() 示例
String url = "jdbc:odbc:Registration";
String userID = "jim";
String password = "keogh";
Statement dataRequest;
Connection dB;
int rowsUpdated;
try {
    Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver");
    dB = DriverManager.getConnection(url,userID,password);
}
catch (ClassNotFoundException e) {
    System.err.println(
        "Unable to load the JDBC/ODBC bridge." + e);
    System.exit(1);
}
catch (SQLException e) {
    System.err.println("Cannot connect to the database." + e);
    System.exit(2);
}
try {
    String query = "UPDATE Registration SET Enrolled='Y' WHERE StudentID = '123'";
    dataRequest = dB.createStatement();
    rowsUpdated = dataRequest.executeUpdate (query);
    dataRequest.close();
}
catch ( SQLException e ){
    System.err.println("SQL error." + e);
    System.exit(3);
}
finally {
    dataRequest.close();
    dB.close();
}

4.3 PreparedStatement 对象的使用

PreparedStatement 对象适用于多次执行相似查询的场景。在编写 SQL 查询时,可以使用问号作为占位符,用于表示每次执行查询时可能会变化的数据。

4.3.1 执行 PreparedStatement 对象的步骤
  1. 声明 PreparedStatement 对象并赋值:
PreparedStatement pstatement = dB.prepareStatement(query);
  1. 替换问号占位符的值:
pstatement.setString(1, "123");
  1. 执行查询:
ResultSet results = pstatement.executeQuery();
4.3.2 示例代码
String url = "jdbc:odbc:Registration";
String userID = "jim";
String password = "keogh";
ResultSet results;
Connection dB;
try {
    Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver");
    dB = DriverManager.getConnection(url,userID,password);
}
catch (ClassNotFoundException e) {
    System.err.println(
        "Unable to load the JDBC/ODBC bridge." + e);
    System.exit(1);
}
catch (SQLException e) {
    System.err.println(
        "Cannot connect to the database." + e);
    System.exit(2);
}
try {
    String query = "SELECT * FROM Registration WHERE studentID = ?";
    PreparedStatement pstatement = dB.prepareStatement(query);
    pstatement.setString(1, "123");
    results = pstatement.executeQuery();
    //Place code here to interact with the ResultSet
}
catch ( SQLException e ){
    System.err.println("SQL error." + e);
    System.exit(3);
}
finally {
    pstatement.close();
    dB.close();
}

4.4 CallableStatement 对象的使用

CallableStatement 对象用于执行存储过程。存储过程是由程序员编写并存储在 DBMS 中的查询,执行存储过程时只需发送调用命令和存储过程的名称,比执行普通 SQL 查询更快。

4.4.1 调用存储过程的参数

调用存储过程时使用三个参数: IN OUT INOUT
- IN 参数:包含存储过程执行所需的数据,通过调用 setXXX() 方法设置其值。
- OUT 参数:包含存储过程执行后返回的值,需要使用 registerOutParameter() 方法进行注册,然后使用 getXXX() 方法获取其值。
- INOUT 参数:用于向存储过程传递信息并从存储过程中获取信息。

4.4.2 示例代码
String url = "jdbc:odbc:Registration";
String userID = "jim";
String password = "keogh";
String lastOrderNumber;
Connection dB;
try {
    Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver");
    dB = DriverManager.getConnection(url,userID,password);
}
catch (ClassNotFoundException e) {
    System.err.println(
        "Unable to load the JDBC/ODBC bridge." + e);
    System.exit(1);
}
catch (SQLException e) {
    System.err.println(
        "Cannot connect to the database." + e);
    System.exit(2);
}
try {
    String query = "{ CALL LastOrderNumber}";
    CallableStatement cstatement = dB.prepareCall(query);
    cstatement.registerOutParameter(1, Types.VARCHAR);
    cstatement.execute();
    lastOrderNumber = cstatement.getString(1);
}
catch ( SQLException e ){
    System.err.println("SQL error." + e);
    System.exit(3);
}
finally {
    cstatement.close();
    dB.close();
}

4.5 ResultSet 对象的使用

executeQuery() 方法将 SQL 查询发送到 DBMS 进行处理,并返回一个 ResultSet 对象,该对象包含 DBMS 返回的数据。可以使用 ResultSet 对象的方法将数据复制到 Java 集合或变量中进行进一步处理。

4.5.1 结果集的结构

DBMS 返回的数据存储在结果集中,可以将结果集看作一个虚拟表,由行和列组成,类似于电子表格。结果集还包含元数据,如列名、列大小和数据类型等信息。

4.5.2 移动虚拟指针

可以使用虚拟指针来遍历结果集中的每一行,通过调用 ResultSet 对象的方法来移动指针。初始时,虚拟指针位于第一行上方,需要使用 next() 方法将其移动到第一行。如果存在下一行, next() 方法返回 true ,否则返回 false ,表示已到达结果集的末尾。

4.5.3 复制数据

可以使用 getXXX() 方法将当前行的某一列的数据复制到 Java 集合或变量中, XXX 表示列的数据类型。例如, getString() 方法用于复制字符串类型的列数据。

4.5.4 示例代码
String url = "jdbc:odbc:Registration";
String userID = "jim";
String password = "keogh";
String printRow;
String firstName;
String lastName;
Statement dataRequest;
ResultSet results;
Connection dB;
try {
    Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver");
    dB = DriverManager.getConnection(url,userID,password);
}
catch (ClassNotFoundException e) {
    System.err.println(
        "Unable to load the JDBC/ODBC bridge." + e);
    System.exit(1);
}
catch (SQLException e) {
    System.err.println(
        "Cannot connect to the database." + e);
    System.exit(2);
}
try {
    String query = "SELECT FirstName,LastName FROM Students";
    dataRequest = dB.createStatement();
    results = dataRequest.executeQuery (query);
}
catch ( SQLException e ){
    System.err.println("SQL error." + e);
    System.exit(3);
}
boolean records = results.next();
if (!records ) {
    System.out.println("No data returned");
    System.exit(4);
}
try {
    do {
        firstName = results.getString ( 1 );
        lastName = results.getString ( 2 );
        printRow = firstName + " " + lastName;
        System.out.println(printRow);
    } while (results.next() );
    dataRequest.close();
}

4.6 整体流程图

graph TD;
    A[开始] --> B[加载驱动];
    B --> C{是否成功加载?};
    C -- 是 --> D[连接数据库];
    C -- 否 --> E[处理 ClassNotFoundException 异常并退出];
    D --> F{是否成功连接?};
    F -- 是 --> G[执行查询];
    F -- 否 --> H[处理 SQLException 异常并退出];
    G --> I[处理结果集];
    I --> J[关闭连接];
    J --> K[结束];
    E --> K;
    H --> K;

通过以上内容,我们详细介绍了 JDBC 与 Java 数据对象的相关知识,包括数据检索、异常处理、避免连接超时以及不同类型的 Statement 对象和 ResultSet 对象的使用方法。希望这些内容对大家在使用 Java 与数据库进行交互时有所帮助。

5. 实际应用案例分析

5.1 学生信息查询系统示例

假设我们要开发一个学生信息查询系统,根据学生 ID 查询学生的基本信息。我们可以使用 PreparedStatement 对象来实现这个功能。以下是具体的实现步骤:
1. 加载驱动并建立连接

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class StudentQuerySystem {
    public static void main(String[] args) {
        String url = "jdbc:odbc:Registration";
        String userID = "jim";
        String password = "keogh";
        Connection dB = null;
        PreparedStatement pstatement = null;
        ResultSet results = null;
        try {
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
            dB = DriverManager.getConnection(url, userID, password);
        } catch (ClassNotFoundException e) {
            System.err.println("Unable to load the JDBC/ODBC bridge." + e);
            System.exit(1);
        } catch (SQLException e) {
            System.err.println("Cannot connect to the database." + e);
            System.exit(2);
        }
  1. 准备 SQL 查询语句
        try {
            String query = "SELECT FirstName, LastName, Age FROM Students WHERE StudentID = ?";
            pstatement = dB.prepareStatement(query);
  1. 设置查询参数
            // 假设要查询学生 ID 为 123 的信息
            pstatement.setString(1, "123");
  1. 执行查询并处理结果
            results = pstatement.executeQuery();
            if (results.next()) {
                String firstName = results.getString("FirstName");
                String lastName = results.getString("LastName");
                int age = results.getInt("Age");
                System.out.println("学生姓名: " + firstName + " " + lastName + ", 年龄: " + age);
            } else {
                System.out.println("未找到该学生信息。");
            }
        } catch (SQLException e) {
            System.err.println("SQL error." + e);
            System.exit(3);
        } finally {
            try {
                if (results != null) results.close();
                if (pstatement != null) pstatement.close();
                if (dB != null) dB.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

5.2 存储过程调用示例

假设数据库中有一个存储过程 GetStudentCount 用于统计学生的数量,我们可以使用 CallableStatement 对象来调用这个存储过程。以下是具体的实现步骤:
1. 加载驱动并建立连接

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Types;

public class StudentCountProcedure {
    public static void main(String[] args) {
        String url = "jdbc:odbc:Registration";
        String userID = "jim";
        String password = "keogh";
        Connection dB = null;
        CallableStatement cstatement = null;
        try {
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
            dB = DriverManager.getConnection(url, userID, password);
        } catch (ClassNotFoundException e) {
            System.err.println("Unable to load the JDBC/ODBC bridge." + e);
            System.exit(1);
        } catch (SQLException e) {
            System.err.println("Cannot connect to the database." + e);
            System.exit(2);
        }
  1. 准备调用存储过程的 SQL 语句
        try {
            String query = "{CALL GetStudentCount(?)}";
            cstatement = dB.prepareCall(query);
  1. 注册输出参数
            cstatement.registerOutParameter(1, Types.INTEGER);
  1. 执行存储过程并获取结果
            cstatement.execute();
            int studentCount = cstatement.getInt(1);
            System.out.println("学生总数: " + studentCount);
        } catch (SQLException e) {
            System.err.println("SQL error." + e);
            System.exit(3);
        } finally {
            try {
                if (cstatement != null) cstatement.close();
                if (dB != null) dB.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

6. 性能优化建议

6.1 合理使用 PreparedStatement

如前文所述, PreparedStatement 适用于多次执行相似查询的场景。通过预编译查询语句,可以减少每次查询时的编译开销,提高查询性能。例如,在一个学生信息管理系统中,经常需要根据不同的学生 ID 查询学生信息,使用 PreparedStatement 可以显著提高查询效率。

6.2 及时关闭资源

在使用完 ResultSet Statement Connection 对象后,要及时调用 close() 方法关闭这些资源。否则,可能会导致资源泄漏,影响系统性能。可以使用 try-with-resources 语句来确保资源的正确关闭,示例如下:

try (Connection dB = DriverManager.getConnection(url, userID, password);
     PreparedStatement pstatement = dB.prepareStatement(query);
     ResultSet results = pstatement.executeQuery()) {
    // 处理结果集
    while (results.next()) {
        // 处理每一行数据
    }
} catch (SQLException e) {
    e.printStackTrace();
}

6.3 批量操作

如果需要插入、更新或删除大量数据,可以使用批量操作来提高性能。 Statement PreparedStatement 都支持批量操作,通过调用 addBatch() 方法将多个 SQL 语句添加到批处理中,然后调用 executeBatch() 方法一次性执行这些语句。以下是一个批量插入数据的示例:

try (Connection dB = DriverManager.getConnection(url, userID, password);
     PreparedStatement pstatement = dB.prepareStatement("INSERT INTO Students (FirstName, LastName) VALUES (?, ?)")) {
    for (int i = 0; i < 100; i++) {
        pstatement.setString(1, "FirstName" + i);
        pstatement.setString(2, "LastName" + i);
        pstatement.addBatch();
    }
    int[] results = pstatement.executeBatch();
    System.out.println("插入的记录数: " + results.length);
} catch (SQLException e) {
    e.printStackTrace();
}

6.4 性能优化流程图

graph TD;
    A[开始] --> B[合理使用 PreparedStatement];
    B --> C[及时关闭资源];
    C --> D[使用批量操作];
    D --> E[结束];

7. 总结

7.1 关键知识点回顾

  • 数据检索与显示 :通过 ResultSet 对象和 getString() 等方法从数据库中检索数据并显示。
  • 异常处理 :使用 try-catch 块捕获并处理 ClassNotFoundException SQLException 等异常。
  • 避免连接超时 :通过 DriverManager.setLoginTimeout() 方法设置连接超时时间。
  • 不同类型的 Statement 对象 Statement 用于立即执行查询, PreparedStatement 用于预编译查询, CallableStatement 用于执行存储过程。
  • ResultSet 对象 :用于处理数据库返回的结果集,通过虚拟指针和 getXXX() 方法复制数据。

7.2 应用场景总结

  • Statement 对象 :适用于一次性执行的简单查询。
  • PreparedStatement 对象 :适用于多次执行相似查询的场景,如根据不同条件查询学生信息。
  • CallableStatement 对象 :适用于执行存储过程,如统计学生数量、计算订单总金额等。

通过掌握这些知识和技巧,我们可以更加高效地使用 Java 与数据库进行交互,开发出性能优良、稳定可靠的数据库应用程序。在实际开发中,要根据具体的业务需求选择合适的 Statement 对象和优化策略,以提高系统的性能和可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值