ORACLE JDBC的getGeneratedKeys(转载)

本文详细介绍了在Oracle JDBC中使用getGeneratedKeys方法来获取插入记录的自增ID值的方法。针对不同版本的Oracle JDBC驱动,提供了具体的代码示例,并指出了在使用过程中可能遇到的问题及解决方案。
ORACLE JDBC的getGeneratedKeys
对于JDBC 3.0, 使用statement.getGeneratedKeys()可以返回刚刚插入的记录的自动增长的ID值。对于ORACLE,一般是定义一个序列,然后利用序列的nextval来自动给列分配ID值。但是很多人发现,在利用ORACLE JDBC驱动编写的时候,往往会失败。显示“java.sql.SQLException: Unsupported feature”。

其实,对于ORACLE JDBC,只有在10.2.0.1.0版本后的JDBC才支持getGeneratedKeys特性。而且如果使用下列代码:

String sql = "INSERT INTO FOO (NAME) VALUES ('BAR')";
Statement stmt = connection.createStatement();
stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
oracle.sql.ROWID rid = (oracle.sql.ROWID) rs.getObject(1); //getLong and getInt fail

// The following fail
// long l = rid.longValue();
// int i = rid.intValue();

String s = rid.stringValue(); // s equals "AAAXcTAAEAAADXYAAB"

返回的将是ROWID值。可以使用下列代码:

String sql = "INSERT INTO ORDERS (ORDER_ID, CUSTOMER_ID) VALUES (ORDER_ID_SEQ.NEXTVAL, ?)";
String generatedColumns[] = {"ORDER_ID"};
PreparedStatement pstmt = conn.prepareStatement(sql, generatedColumns);
pstmt.setLong(1, customerId);
pstmt.executeUpdate();
ResultSet rs = pstmt.getGeneratedKeys();
rs.next();
// The generated order id
long orderId = rs.getLong(1);

能得到正确的ID值。注意,其中generatedColumns[]表示从哪个列来获取新的ID值。我们也可以使用:

int a[]={1};

PreparedStatement pstmt = conn.prepareStatement(sql, a);

......

来表示第1列是KEY列,我们要获取第1列的新插入的值。

目前(20071219)ORACLE JDBC最新的是11g1,推荐使用。http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/htdocs/jdbc_11...

另外,Jdeveloper 10.1.3.3里面携带的JDBC驱动也不支持getGeneratedKeys。需要进行更新。

当然,也可以使用第三方的JDBC驱动,如i-net software的Oranxo驱动,支持getGeneratedKeys,而且驱动程序的体积更小。
<think>好的,我现在需要解释一下用户提供的Java代码片段:`ResultSet rs = pstm.getGeneratedKeys();`。首先,我得理解这段代码的作用和它在JDBC中的使用场景。用户之前已经询问过关于事务处理的部分,现在转向获取生成的主键,这可能是他们在处理数据库插入操作时需要获取自动生成的主键值,比如自增ID。 首先,我需要回忆JDBC中`getGeneratedKeys()`方法的相关知识。这个方法属于`PreparedStatement`接口,当执行一个插入语句后,如果数据库支持生成主键,比如MySQL的AUTO_INCREMENT或者PostgreSQL的SERIAL类型,这个方法可以返回生成的主键值。用户可能在使用`INSERT`语句后,想要获取刚刚插入记录的主键,以便进行后续操作,比如关联其他表的数据。 接下来,我需要确认这个方法的使用前提。通常,为了能够获取生成的主键,执行`executeUpdate()`或`execute()`方法时,需要指定`Statement.RETURN_GENERATED_KEYS`参数,或者在创建PreparedStatement时指定需要返回生成的键。例如: ```java PreparedStatement pstm = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ``` 或者,在创建语句时指定列名: ```java PreparedStatement pstm = conn.prepareStatement(sql, new String[] {"id"}); ``` 如果用户没有正确配置这些参数,`getGeneratedKeys()`可能返回空的ResultSet,或者不同的数据库驱动有不同的行为。这点需要注意,可能需要提醒用户确保正确设置了返回生成键的参数。 然后,`ResultSet rs = pstm.getGeneratedKeys();`会返回一个结果集,即使只生成了一个键,结果集也会包含至少一行数据。用户需要遍历这个结果集来获取键值。通常,使用`rs.next()`移动到第一行,然后通过`rs.getInt(1)`或者根据列的类型来获取值。 此外,不同的数据库在处理生成键时可能有不同的行为。比如,某些数据库可能返回多个列,或者键的类型可能不是整数。需要提醒用户注意处理这些情况,确保代码的兼容性。 还需要考虑异常处理。`getGeneratedKeys()`可能会抛出SQLException,用户应该在try-catch块中处理可能的异常,并在finally块中关闭ResultSet、Statement和Connection,避免资源泄漏。 另外,可能存在的常见问题包括:忘记指定RETURN_GENERATED_KEYS导致结果集为空,或者错误地处理结果集导致没有正确获取到键值。需要强调正确使用这些参数的重要性,并提供示例代码说明如何正确获取键值。 最后,结合用户之前的询问,他们可能在处理一个包含事务的插入操作,获取生成的主键后进行后续的关联操作,比如插入到其他表中。因此,在事务中正确处理生成的主键是关键,如果在事务提交前获取键值,需要确保事务成功提交后键值有效,或者在回滚时这些键值不会被保留。 总结来说,我需要分步骤解释这段代码的作用、使用前提、如何正确获取生成键、异常处理和资源释放,以及常见问题和最佳实践。同时,结合用户之前的事务处理代码,说明在事务上下文中使用生成键的注意事项。</think>--- ### **代码解析:** ```java ResultSet rs = pstm.getGeneratedKeys(); // 获取由 INSERT 操作自动生成的主键值 ``` --- ### **分步说明:** #### **1. 方法作用** - **`getGeneratedKeys()`** 是 `PreparedStatement` 接口的方法,用于获取数据库自动生成的主键值(如自增 ID、序列值等)。 - **典型场景**: 执行 `INSERT` 语句后,需要立即获取新插入记录的主键(例如用户注册后获取用户 ID)。 --- #### **2. 使用前提** - **必须在执行 INSERT 时声明需要返回生成的主键**,否则可能返回空结果集。 - **方式 1**:创建 `PreparedStatement` 时指定参数 ```java // 显式要求返回生成的主键 PreparedStatement pstm = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); ``` - **方式 2**:执行 `executeUpdate()` 时指定参数 ```java // 仅适用于 Statement(非 PreparedStatement) Statement stmt = conn.createStatement(); int rows = stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); ``` --- #### **3. 获取主键值** - **ResultSet 结构**: 每行对应一个生成的主键(即使只插入一条记录,结果集也可能包含多列,具体依赖数据库驱动)。 - **示例代码**: ```java try (ResultSet rs = pstm.getGeneratedKeys()) { if (rs.next()) { int generatedId = rs.getInt(1); // 获取第一列的值(通常是主键) System.out.println("生成的主键值:" + generatedId); } } ``` --- ### **关键注意事项** #### **1. 数据库兼容性** - **并非所有数据库都支持此功能**: - 支持:MySQL(`AUTO_INCREMENT`)、PostgreSQL(`SERIAL` 或 `IDENTITY`)、Oracle(通过 `RETURNING` 子句)。 - 不支持:需手动查询序列(如 Oracle 未使用 `RETURNING`)。 #### **2. 复合主键处理** - 若表有复合主键(多列联合主键),结果集可能返回多列值,需按列索引或列名读取: ```java rs.getInt("id"); rs.getString("uuid"); ``` #### **3. 事务中的使用** - **事务提交前**:生成的主键仅在当前连接可见,其他会话无法获取。 - **事务回滚**:未提交的事务中生成的主键会被丢弃。 --- ### **完整流程示例** ```java String sql = "INSERT INTO users (name, email) VALUES (?, ?)"; try (Connection conn = dataSource.getConnection(); PreparedStatement pstm = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { // 设置参数 pstm.setString(1, "张三"); pstm.setString(2, "zhangsan@example.com"); // 执行 INSERT int affectedRows = pstm.executeUpdate(); // 获取生成的主键 try (ResultSet rs = pstm.getGeneratedKeys()) { if (rs.next()) { int userId = rs.getInt(1); System.out.println("新用户 ID:" + userId); } } } catch (SQLException e) { e.printStackTrace(); } ``` --- ### **常见问题** #### **1. 结果集为空?** - **可能原因**: - 未声明 `RETURN_GENERATED_KEYS`。 - 数据库表未设置自增主键或序列。 - 执行的 SQL 不是 `INSERT` 语句。 #### **2. 获取错误的主键列?** - **解决方法**: 明确指定列名(部分数据库驱动支持): ```java PreparedStatement pstm = conn.prepareStatement(sql, new String[] {"id"}); ``` #### **3. 性能影响** - **额外开销**:获取主键需要数据库额外操作,高并发场景需评估对性能的影响。 --- ### **扩展:替代方案** - **手动查询序列**(适用于 Oracle): ```sql INSERT INTO orders (id, product) VALUES (order_seq.NEXTVAL, 'Phone'); SELECT order_seq.CURRVAL FROM dual; -- 显式查询当前序列值 ``` - **使用 RETURNING 子句**(PostgreSQL): ```sql INSERT INTO products (name) VALUES ('Laptop') RETURNING id; ``` --- ### **最佳实践** 1. **始终关闭 ResultSet** 使用 `try-with-resources` 确保资源释放,避免内存泄漏。 2. **防御性检查** 检查 `rs.next()` 是否为 `true`,避免空指针异常。 3. **明确主键列名** 指定列名比依赖列索引更可靠(如 `new String[] {"id"}`)。 --- 通过上述方法,您可以安全高效地获取数据库自动生成的主键值,确保后续业务逻辑的连贯性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值