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);
}
-
执行查询
:使用
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);
}
-
移动虚拟指针并处理数据
:在复制结果集的数据之前,需要将虚拟指针从结果集第一行上方移动到第一行,这可以通过
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);
}
-
如何处理元数据中的大量信息?
- 可以根据实际需求选择需要的元数据信息,避免获取过多无用数据。
- 对于复杂的元数据处理,可以将其封装成方法或类,提高代码的可维护性。
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();
-
及时关闭资源
:在使用完
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为我们提供了强大而灵活的数据库访问能力,只要我们掌握好相关知识和技巧,就能轻松应对各种数据库操作需求。
Java JDBC结果集与异常处理详解
超级会员免费看
1951

被折叠的 条评论
为什么被折叠?



