setAutoCommit(false) sybase SET CHAINED command...异常

本文解析了在Sybase ASE数据库中使用SETCHAINED命令时可能遇到的异常情况及其原因,尤其是在J2EE环境中结合连接池使用时的问题。文章详细介绍了如何避免这类异常的发生。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在 Sybase ASE 实际应用中,特别是在 ASE + J2EE 应用中,较容易出现 SET CHAINED command not allowed within multi-statement transaction.的异常(尽管到版本 15.0.1 为止,ASE 并不支持异常机制,但本文为了方便,统一采用“异常”一词)。有的开发人员认为这是 Sybase 数据库的问题;有的认为是多次调用 setAutoCommit() 方法的问题;有的开发人员则认为这是 jConnect 的问题,甚至从 jConnect 的代码上直接屏蔽此异常。 
  然而,SET CHAINED 异常倒底是怎样产生的? 
  一、数据库层 
  首先,让我们看看 set chained。下面的文字片段摘自《ASE 12.5.2 Reference Manual: Commands》,Page 430: 

chained 
begins a transaction just before the first data retrieval or data modification 
statement at the beginning of a session and after a transaction ends. In 
chained mode, Adaptive Server implicitly executes a begin transaction 
command before the following statements: delete, fetch, insert, lock table, 
open, select, and update. You cannot execute set chained within a transaction. 

  从此段文字可以得知,当 set chained on 后,delete、fetch、insert、lock table、open、select 以及 update 语句将自动启动一个事务,并要求显式的完成事务,即明确地调用 commit/rollback。同时,在事务中,不允许设置 chained 模式。 
  下面的 sql 代码片断将说明在数据库层上 SET CHAINED 错误信息是如何产生的。 

1> set chained on 
2> go 
1> set chained on 
2> go 
1> begin tran 
2> go 
1> 

  似乎多次调用 set chained 并不会产生异常。接下来, 

1> set chained on 
2> go 
Msg 226, Level 16, State 1: 
Server 'FLYBEAN', Line 1: 
SET CHAINED command not allowed within multi-statement transaction. 
1> set chained off 
2> go 
Msg 226, Level 16, State 1: 
Server 'FLYBEAN', Line 1: 
SET CHAINED command not allowed within multi-statement transaction. 
1> 

  显然,处于事务环境下,调用 set chained 是会发生异常的,这一点手册上也非常明确的指出了。但为什么前面的片断中两次连续调用 set chained 却不会产生异常呢?请注意文档上这一句:Adaptive Server implicitly executes a begin transaction command before the following statements: 。 
  重建一个数据库连接,从头开始: 

1> set chained on 
2> go 
1> select 1 
2> go 

----------- 


(1 row affected) 
1> set chained on 
2> go 
Msg 226, Level 16, State 1: 
Server 'FLYBEAN', Line 1: 
SET CHAINED command not allowed within multi-statement transaction. 
1> set chained off 
2> go 
Msg 226, Level 16, State 1: 
Server 'FLYBEAN', Line 1: 
SET CHAINED command not allowed within multi-statement transaction. 
1> 

  在执行 select 1 之前,数据库自动启动了一笔事务,因此不能再执行 set chained。接下来,完成隐式启动的事务: 

1> rollback 
2> go 
1> set chained off 
2> go 
1> 

  二、J2EE 层 
  J2EE 应用中,一些轻量级的数据访问层实现采用 Connection 的setAutoCommit(false) + commit()/rollback() 的方式来管理事务。通过对 jConnect 的反编译以及对 spt_mda 数据的分析,可以得知 setAutoCommit(true) = SET CHAINED OFF;setAutoCommit(false) = SET CHAINED ON,下图以顺序图展示调用 setAutoCommit() 方法时,实际发生的交互。 

  另一方面,J2EE 应用中大多采用了连接池。应用在调用  Connection.close() 方法时,实际上并没有真正地关闭连接,而是将连接回收到池中。假设连接的初态是 chained off。如果应用在取得连接后调用该连接的 setAutoCommit(false) 方法来启动事务,在事务完成后,通过 close() 方法回到池中时, 未能恢复到初始状态(即 chained off),则应用多次获取该连接并进行一定操作后,就很有可能出现异常。见下图: 

  通过上面的分析,理解了产生此异常的原因,就很容易避免此异常,即在完成事务后,关闭连接前,显式地调用 setAutoCommit(true)。或许有的程序员会认为麻烦,但别忘记“完壁归赵”是资源借用者的义务。


---------------------------

环境: 

数据库:
sybase 15 

连接池:c3p0 


问题: 

获取Connection之后,设置autoCommit为false; 

发现使用后连接池没有释放链接。 

开始以为是连接池的bug,后来发现在log中,连接池已经释放链接的方法, 

但是没有释放成功。 


分析: 

相同的代码,换成其他的数据库都没有问题,时候后连接都能被释放。 

初步判断是数据库的问题。 


解决方案: 


求助完成的谷歌大神,找到解决方案 

使用完Connection后,需要把autoCommit重新设定为true。 


具体是什么原因,目前还不清楚 


<think>好的,我现在要解释用户提供的代码片段:`conn.setAutoCommit(false); i = pstm.executeUpdate(); conn.commit();`。首先,我需要确认每个语句的作用,并理解它们如何一起工作。 首先,用户可能对数据库事务处理不太熟悉,所以需要从基础开始。`conn.setAutoCommit(false)` 这里设置自动提交为false,意味着需要手动控制事务。自动提交默认是开启的,所以关闭后必须显式提交或回滚。这一步可能用户是想将多个操作作为一个事务来处理,确保原子性。 接下来,`i = pstm.executeUpdate();` 执行更新操作,比如INSERT、UPDATE或DELETE,返回受影响的行数。这里需要注意,当自动提交关闭时,执行更新不会立即生效,必须等待commit。用户可能希望执行多个操作后再一起提交,或者在出错时回滚,保证数据一致性。 然后是`conn.commit();`,提交事务,使所有之前的操作生效。如果中间有任何异常,应该执行回滚,避免部分操作生效。用户可能没有提到异常处理,但实际应用中必须考虑,否则可能导致数据不一致或连接未正确释放。 现在需要考虑用户可能的疑问:为什么要手动管理事务?自动提交的优缺点?commit的作用是什么?如果没有commit会怎样?还有,是否需要处理异常,回滚事务? 可能需要提到事务的ACID特性,特别是原子性和一致性。手动提交允许将多个操作作为一个整体,要么全部成功,要么全部失败。例如,转账操作需要扣款和入账同时成功,否则回滚。 另外,用户可能对executeUpdate的返回值i感兴趣,即受影响的行数,这在判断操作是否成功时很有用。例如,如果i为0,可能意味着没有行被更新,可能需要处理这种情况。 需要提醒的是,在设置自动提交为false后,必须确保在操作结束后提交或回滚,否则连接可能一直保持未提交状态,导致数据库锁或其他问题。同时,资源管理也很重要,应该在finally块中关闭连接和语句,并处理异常情况。 可能用户没有展示完整的代码,比如异常处理部分,所以需要补充说明最佳实践,比如使用try-catch-finally块,确保在异常时回滚,并正确释放资源。 此外,不同数据库对事务的支持可能有所不同,但基本的JDBC操作是通用的。需要确认是否所有数据库都遵循相同的事务行为,或者是否有特殊情况需要注意。 总结思考步骤:首先理解每个语句的作用,然后结合事务的概念解释整体流程,强调手动提交的必要性,异常处理的重要性,以及资源管理的注意事项。确保用户明白为什么需要这样写代码,以及可能遇到的问题和解决方案。</think>以下是您提供的代码片段的详细解释: --- ### **代码解析:** ```java conn.setAutoCommit(false); // 关闭自动提交,开启事务 i = pstm.executeUpdate(); // 执行 SQL 更新操作(如 INSERT/UPDATE/DELETE) conn.commit(); // 手动提交事务 ``` --- ### **分步说明:** #### **1. 关闭自动提交,开启事务** ```java conn.setAutoCommit(false); ``` - **作用**: 关闭 JDBC 的默认自动提交模式,开启事务控制。 - **关键点**: - **默认行为**:JDBC 默认 `autoCommit = true`,每条 SQL 会立即提交到数据库生效。 - **事务场景**: 当需要将多个操作(如多次更新)组合成一个原子性操作时,需关闭自动提交。 - **示例**: 银行转账需同时执行“扣款”和“入账”两条 SQL,必须保证同时成功或失败。 --- #### **2. 执行 SQL 更新操作** ```java i = pstm.executeUpdate(); ``` - **作用**: 执行 `INSERT`/`UPDATE`/`DELETE` 等非查询 SQL,返回受影响的行数(如删除了 3 行则 `i=3`)。 - **注意事项**: - **事务未提交**:此时数据库的修改仅对当前连接可见,其他会话无法看到这些变更。 - **返回值意义**: `i > 0` 表示操作影响了数据;`i = 0` 表示 SQL 语法正确但未匹配到数据(如 `UPDATE` 条件不满足)。 --- #### **3. 手动提交事务** ```java conn.commit(); ``` - **作用**: 将所有暂存的修改永久写入数据库,事务结束。 - **关键点**: - **原子性保证**:提交后所有操作生效,若提交失败(如网络中断),数据库会自动回滚。 - **提交后状态**: 事务结束后,连接会恢复默认的 `autoCommit = true` 状态(部分数据库驱动行为)。 --- ### **事务处理的完整流程** ```java try { conn.setAutoCommit(false); // 开启事务 // 执行多个操作(如多次 executeUpdate) pstm1.executeUpdate(); // 操作1 pstm2.executeUpdate(); // 操作2 conn.commit(); // 提交事务 } catch (SQLException e) { if (conn != null) { conn.rollback(); // 失败时回滚(需 try-catch 嵌套处理) } } finally { // 关闭资源(PreparedStatement、Connection) } ``` --- ### **常见问题与注意事项** #### **1. 为什么必须显式提交?** - **数据一致性**: 事务中可能包含多个操作,显式提交确保所有操作全部成功后才持久化。 *示例*:订单创建需同时写入订单表和库存表,若库存更新失败需取消订单。 #### **2. 未提交的修改对其他会话可见吗?** - **数据库隔离级别决定**: - 默认级别(如 `READ_COMMITTED`):其他会话看不到未提交的修改。 - `READ_UNCOMMITTED` 级别:可能读到“脏数据”(一般不推荐)。 #### **3. 忘记提交或回滚的后果** - **资源泄漏**: 数据库会持有锁和未提交的修改,可能导致连接耗尽或死锁。 - **数据丢失**: 程序崩溃时,未提交的修改会被数据库自动回滚。 #### **4. 性能影响** - **长事务风险**: 事务开启后应尽快完成操作,避免长时间占用数据库资源。 *优化建议*:事务范围仅包含必要的 SQL 操作。 --- ### **扩展:JDBC 事务的四个特性(ACID)** | **特性** | **描述** | **JDBC 实现方式** | |----------------|-------------------------------------------------------------------------|---------------------------------------------------------------------------------| | **原子性** | 事务中的操作要么全部成功,要么全部失败。 | `conn.commit()` 提交全部操作,`conn.rollback()` 撤销所有未提交操作。 | | **一致性** | 事务完成后数据库从一个有效状态转移到另一个有效状态。 | 通过业务逻辑约束(如外键、触发器)和事务控制共同保证。 | | **隔离性** | 事务之间互不干扰。 | 通过设置 `conn.setTransactionIsolation(level)` 控制(如 `READ_COMMITTED`)。 | | **持久性** | 事务提交后修改永久生效。 | 提交后数据写入磁盘(依赖数据库的 WAL 日志和刷盘机制)。 | --- ### **最佳实践** 1. **始终在 finally 块中释放资源** ```java try { // 事务操作 } finally { if (pstm != null) pstm.close(); if (conn != null) conn.close(); // 关闭连接(归还到连接池) } ``` 2. **捕获异常并回滚** ```java try { conn.setAutoCommit(false); // 执行操作 conn.commit(); } catch (SQLException e) { try { if (conn != null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } ``` 3. **合理设置事务隔离级别** ```java // 避免脏读,但允许不可重复读 conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); ``` --- 通过以上步骤,您可以确保数据库操作的原子性和一致性,同时避免资源泄漏和性能问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值