26、Java JDBC编程:结果集操作与异常处理

Java JDBC结果集与异常处理详解

Java JDBC编程:结果集操作与异常处理

1. 结果集数据处理基础

在Java中使用JDBC(Java Database Connectivity)连接数据库并执行查询后,会得到一个结果集(ResultSet)。结果集是一个虚拟的表格,包含从数据库表中复制的行和列数据。以下是处理结果集的基本步骤:
1. 建立数据库连接 :使用 DriverManager.getConnection() 方法连接到数据库。

try {
    Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver");
    Connection 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. 执行查询 :使用 Statement 对象执行SQL查询,获取结果集。
try {
    String query = "SELECT FirstName,LastName FROM Students";
    Statement dataRequest = dB.createStatement();
    ResultSet results = dataRequest.executeQuery (query);
} catch (SQLException e) {
    System.err.println("SQL error." + e);
    System.exit(3);
}
  1. 移动虚拟指针并处理数据 :在复制结果集的数据之前,需要将虚拟指针从结果集第一行上方移动到第一行,这可以通过 next() 方法实现。
boolean records = results.next();
if (!records ) {
    System.out.println("No data returned");
    System.exit(4);
}
try {
    do {
        String firstName = results.getString ( 1 );
        String lastName = results.getString ( 2 );
        String printRow = firstName + " " + lastName;
        System.out.println(printRow);
    } while (results.next() );
    dataRequest.close();
} catch (SQLException e ) {
    System.err.println("Data display error." + e);
    System.exit(5);
} finally {
    dB.close();
}
2. 虚拟光标定位

ResultSet对象定义了六个方法用于在结果集中移动虚拟光标:
- first() :将虚拟光标移动到结果集的第一行。
- last() :将虚拟光标定位到结果集的最后一行。
- previous() :将虚拟光标移动到上一行。
- absolute(int row) :指定虚拟光标要定位的行号。
- relative(int rows) :从当前行移动指定数量的行,正数表示向前移动,负数表示向后移动。
- getRow() :返回当前行的行号。

为了能够使用这些方法,需要创建一个可滚动的结果集。可以通过向 createStatement() 方法传递以下三个常量之一来实现:
- TYPE_FORWARD_ONLY :虚拟光标只能向下移动,这是默认设置。
- TYPE_SCROLL_INSENSITIVE :虚拟光标可以双向移动,结果集对其他程序对表数据的更改不敏感。
- TYPE_SCROLL_SENSITIVE :虚拟光标可以双向移动,结果集对其他程序对表数据的更改敏感。

以下是一个创建不敏感结果集并移动虚拟光标的示例:

try {
    String query = "SELECT FirstName,LastName FROM Students";
    Statement dataRequest = dB.createStatement(TYPE_SCROLL_INSENSITIVE);
    ResultSet results = dataRequest.executeQuery (query);
    boolean records = results.next();
    if (!records ) {
        System.out.println("No data returned");
        System.exit(4);
    }
    do {
        results.first();
        results.last();
        results.previous();
        results.absolute(10);
        results.relative(-2);
        results.relative(2);
        String firstName = results.getString ( 1 );
        String lastName = results.getString ( 2 );
        String printRow = firstName + " " + lastName;
        System.out.println(printRow);
    } while (results.next() );
    dataRequest.close();
} catch (SQLException e ) {
    System.err.println("Data display error." + e);
    System.exit(5);
} finally {
    dB.close();
}
3. 测试JDBC驱动的可滚动性

有些JDBC驱动可能不支持部分或全部可滚动功能。可以通过以下代码测试当前使用的JDBC驱动是否支持可滚动功能:

boolean forward, insensitive, sensitive;
DatabaseMetaData meta = dB.getMetaData();
forward = meta.supportsResultsSetType(ResultSet.TYPE_FORWARD_ONLY);
insensitive = meta.supportsResultsSetType(ResultSet.TYPE_SCROLL_INSENSITIVE);
sensitive = meta.supportsResultsSetType(ResultSet.TYPE_SCROLL_SENSITIVE);
System.out.println("forward: " + forward);
System.out.println("insensitive: " + insensitive);
System.out.println("sensitive: " + sensitive);
4. 行获取优化

当从数据库管理系统(DBMS)请求行时,JDBC驱动会根据获取大小(fetch size)获取一定数量的行,并丢弃之前获取的行集。需要注意的是,从DBMS获取的行数和结果集中显示的行数可能不同。

可以使用 Statement 对象的 setFetchSize() 方法设置JDBC从DBMS获取的行数,以提高行获取的效率。以下是设置获取大小为500行的示例:

try {
    String query = "SELECT FirstName,LastName FROM Students";
    Statement dataRequest = dB.createStatement(TYPE_SCROLL_INSENSITIVE);
    dataRequest.setFetchSize(500);
    ResultSet results = dataRequest.executeQuery (query);
} catch (SQLException e ){
    System.err.println("SQL error." + e);
    System.exit(3);
} finally {
    dB.close();
}
5. 可更新结果集

除了读取结果集的数据,还可以更新结果集中的数据。要使结果集可更新,需要在创建 Statement 对象时传递 CONCUR_UPDATABLE 常量。

5.1 修改结果集的值

可以使用 updateXXX() 方法修改结果集中当前行的列值,其中 XXX 是要更新列的数据类型。该方法需要两个参数:第一个参数指定要更改值的列(可以使用列号或列名),第二个参数是新的值。

try {
    String query = "SELECT FirstName,LastName FROM Students WHERE FirstName = 'Mary' and LastName = 'Jones'";
    Statement dataRequest = dB.createStatement(ResultSet.CONCUR_UPDATABLE);
    ResultSet results = dataRequest.executeQuery (query);
    boolean records = results.next();
    if (!records ) {
        System.out.println("No data returned");
        System.exit(4);
    }
    results.updateString ("LastName", "Jones");
    results.updateRow();
    dataRequest.close();
} catch (SQLException e ) {
    System.err.println("Data display error." + e);
    System.exit(5);
}
5.2 删除结果集中的行

使用 deleteRow() 方法可以从结果集中删除一行。通常,先将虚拟光标移动到要删除的行,然后调用该方法。

results.deleteRow();
5.3 插入新行到结果集

插入新行到结果集需要三个步骤:
1. 使用 moveToInsertRow() 方法将光标定位到插入行。
2. 使用 updateXXX() 方法为新行的列设置值。
3. 调用 insertRow() 方法插入新行。

try {
    String query = "SELECT FirstName,LastName FROM Students";
    Statement dataRequest = dB.createStatement(ResultSet.CONCUR_UPDATABLE);
    ResultSet results = dataRequest.executeQuery (query);
    boolean records = results.next();
    if (!records ) {
        System.out.println("No data returned");
        System.exit(4);
    }
    results.moveToInsertRow ();
    results.updateString (1, "Tom");
    results.updateString (2, "Smith");
    results.insertRow();
    results.moveToCurrentRow();
    dataRequest.close();
} catch (SQLException e ) {
    System.err.println("Data display error." + e);
    System.exit(5);
} finally {
    dB.close();
}
6. 元数据

元数据是关于数据的数据,如列名、列的数据类型和列的大小。可以通过 DatabaseMetaData 接口访问元数据。

6.1 访问数据库元数据

调用 Connection 类的 getMetaData() 方法可以返回一个 DatabaseMetaData 对象,通过该对象可以获取数据库的各种元数据。以下是一些常用的方法:
| 方法 | 描述 |
| ---- | ---- |
| getDatabaseProductName() | 返回数据库的产品名称 |
| getUserName() | 返回用户的名称 |
| getURL() | 返回数据库的URL |
| getSchemas() | 返回数据库中所有可用的模式名称 |
| getPrimaryKeys() | 返回主键 |
| getProcedures() | 返回存储过程名称 |
| getTables() | 返回数据库中表的名称 |

6.2 访问结果集元数据

调用 ResultSet 对象的 getMetaData() 方法可以返回一个 ResultSetMetaData 对象,用于获取结果集的元数据。以下是一些常用的方法:
| 方法 | 描述 |
| ---- | ---- |
| getColumnCount() | 返回结果集中包含的列数 |
| getColumnName(int number) | 返回指定列号的列名 |
| getColumnType(int number) | 返回指定列号的列的数据类型 |

7. 数据类型映射

在使用 setXXX() getXXX() 方法存储和检索结果集中的数据时, XXX 代表列的数据类型。以下是SQL数据类型和Java数据类型的映射表:
| SQL Type | Java Type |
| ---- | ---- |
| CHAR | String |
| VARCHAR | String |
| LONGVARCHAR | String |
| NUMERIC | java.math.BigDecimal |
| DECIMAL | java.math.BigDecimal |
| BIT | Boolean |
| TINYINT | Byte |
| SMALLINT | Short |
| INTEGER | Integer |
| BIGINT | Long |
| REAL | Float |
| FLOAT | Float |
| DOUBLE | Double |
| BINARY | Byte[] |
| VARBINARY | Byte[] |
| LONGVARBINARY | Byte[] |
| BLOB | java.sql.Blob |
| CLOB | java.sql.Clob |
| ARRAY | java.sql.Array |
| STRUCT | java.sql.Struct |
| REF | java.sql.Ref |
| DATALINK | java.sql.Types |
| DATE | java.sql.Date |
| TIME | java.sql.Time |
| TIMESTAMP | java.sql.Timestamp |

8. 异常处理

JDBC方法会抛出三种类型的异常:
- SQLException :通常反映查询中的SQL语法错误,也可能由于数据库连接问题或细微的编码错误(如尝试访问已关闭的对象)引起。可以使用 getNextException() 方法获取SQL错误的详细信息,使用 getErrorCode() 方法获取特定于供应商的错误代码。
- SQLWarnings :由 Connection 对象从DBMS接收的警告。可以使用 getWarnings() 方法获取警告,使用 getNextWarning() 方法获取后续警告。
- DataTruncation :当数据因截断而丢失时抛出。

以下是处理 SQLException 的示例:

catch (SQLException e ) {
    System.err.println("Data display error." + e);
    System.exit(5);
}

综上所述,Java中使用JDBC操作数据库时,结果集的处理、虚拟光标定位、行获取优化、可更新结果集操作以及异常处理都是非常重要的知识点。掌握这些内容可以帮助开发者更高效地与数据库进行交互。

Java JDBC编程:结果集操作与异常处理

9. 常见问题与解答

为了帮助大家更好地理解和运用上述知识,下面针对一些常见问题进行解答:
1. 如何选择合适的结果集类型?
- 如果只需要按顺序向下遍历结果集,可选择 TYPE_FORWARD_ONLY ,它是默认设置,性能较高。
- 如果需要在结果集中前后移动,且不关心其他程序对数据的更改,可选择 TYPE_SCROLL_INSENSITIVE
- 如果需要结果集实时反映其他程序对数据的更改,则选择 TYPE_SCROLL_SENSITIVE
2. 在更新结果集时,如何确保数据的一致性?
- 在更新结果集之前,最好先对数据进行备份,以防操作失误。
- 对于敏感数据的更新,建议在事务中进行,确保操作的原子性。例如:

try {
    dB.setAutoCommit(false);
    // 执行更新操作
    results.updateString("ColumnName", "NewValue");
    results.updateRow();
    dB.commit();
} catch (SQLException e) {
    dB.rollback();
    System.err.println("Update failed: " + e);
}
  1. 如何处理元数据中的大量信息?
    • 可以根据实际需求选择需要的元数据信息,避免获取过多无用数据。
    • 对于复杂的元数据处理,可以将其封装成方法或类,提高代码的可维护性。
10. 实践案例

为了更直观地展示上述知识的应用,下面给出一个完整的实践案例,该案例实现了从数据库中查询学生信息,更新学生信息,并处理可能出现的异常。

import java.sql.*;

public class JDBCExample {
    public static void main(String[] args) {
        String url = "jdbc:odbc:Registration";
        String userID = "jim";
        String password = "keogh";
        Connection dB = null;
        Statement dataRequest = null;
        ResultSet results = null;

        try {
            // 加载驱动
            Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
            // 建立数据库连接
            dB = DriverManager.getConnection(url, userID, password);

            // 执行查询
            String query = "SELECT FirstName, LastName FROM Students";
            dataRequest = dB.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
            results = dataRequest.executeQuery(query);

            // 移动虚拟指针并处理数据
            boolean records = results.next();
            if (!records) {
                System.out.println("No data returned");
            } else {
                do {
                    String firstName = results.getString(1);
                    String lastName = results.getString(2);
                    System.out.println(firstName + " " + lastName);

                    // 更新数据示例
                    if (firstName.equals("Mary") && lastName.equals("Jones")) {
                        results.updateString("LastName", "Smith");
                        results.updateRow();
                    }
                } while (results.next());
            }
        } catch (ClassNotFoundException e) {
            System.err.println("Unable to load the JDBC/ODBC bridge: " + e);
        } catch (SQLException e) {
            System.err.println("Database error: " + e);
        } finally {
            try {
                if (results != null) results.close();
                if (dataRequest != null) dataRequest.close();
                if (dB != null) dB.close();
            } catch (SQLException e) {
                System.err.println("Error closing resources: " + e);
            }
        }
    }
}
11. 性能优化建议

在使用JDBC进行数据库操作时,性能优化至关重要。以下是一些性能优化的建议:
1. 合理设置Fetch Size :根据数据库的性能和数据量,合理设置 setFetchSize() 的值,避免一次性获取过多或过少的数据。
2. 使用预编译语句 :对于需要多次执行的SQL语句,使用 PreparedStatement 代替 Statement ,可以提高性能和安全性。例如:

String query = "SELECT FirstName, LastName FROM Students WHERE FirstName = ?";
PreparedStatement pstmt = dB.prepareStatement(query);
pstmt.setString(1, "John");
ResultSet results = pstmt.executeQuery();
  1. 及时关闭资源 :在使用完 ResultSet Statement Connection 后,及时关闭它们,避免资源泄漏。可以使用 try-with-resources 语句简化代码:
try (Connection dB = DriverManager.getConnection(url, userID, password);
     Statement dataRequest = dB.createStatement();
     ResultSet results = dataRequest.executeQuery(query)) {
    // 处理结果集
} catch (SQLException e) {
    System.err.println("Database error: " + e);
}
12. 总结与展望

通过本文的介绍,我们详细了解了Java中使用JDBC操作数据库的各个方面,包括结果集的处理、虚拟光标定位、行获取优化、可更新结果集操作、元数据访问、数据类型映射以及异常处理等。这些知识是进行数据库编程的基础,掌握它们可以让开发者更加高效地与数据库进行交互。

随着技术的不断发展,JDBC也在不断改进和完善。未来,我们可以期待更简洁、高效的数据库访问方式,以及更好的性能和安全性。同时,结合其他技术(如ORM框架),可以进一步提高开发效率和代码的可维护性。希望大家在实际开发中能够灵活运用这些知识,不断探索和创新。

下面是一个简单的流程图,展示了使用JDBC进行数据库查询的基本流程:

graph TD;
    A[加载驱动] --> B[建立连接];
    B --> C[执行查询];
    C --> D[处理结果集];
    D --> E[关闭资源];

总之,Java JDBC为我们提供了强大而灵活的数据库访问能力,只要我们掌握好相关知识和技巧,就能轻松应对各种数据库操作需求。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值