Spring transaction事务之roll back回滚: rollback-for

本文通过实验展示了在Spring框架中如何利用事务管理进行数据库操作,并阐述了不同异常类型对事务回滚的影响。重点分析了受检异常、运行时异常以及配置的rollback-for属性在事务控制流程中的作用。

试验方法:

         写一个单元测试,调用一个service层方法(发生对数据库进行写操作的方法--insert、update、delete)即可.

  applicationContext.xml 样子(如何设置rollback-for)

  1. <!-- 
  2.         spring事务管理 方法一-->  
  3.     <bean id="transactionManager"  
  4.         class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  5.         <property name="dataSource" ref="dataSource" />  
  6.     </bean>  
  7.     <aop:config>  
  8.         <aop:pointcut id="serviceOperation" expression="execution(*  
  9.         com.*.service.*.*(..))" />  
  10.         <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice" />  
  11.     </aop:config>  
  12.   
  13.     <tx:advice id="txAdvice" transaction-manager="transactionManager">  
  14.         <tx:attributes>  
  15.             <tx:method name="insert*" rollback-for="Exception" />  
  16.             <tx:method name="save*" rollback-for="Exception" />  
  17.             <tx:method name="update*" rollback-for="Exception" />  
  18.             <tx:method name="delete*" rollback-for="Exception" />  
  19.             <tx:method name="*" read-only="true" rollback-for="Exception" />  
  20.         </tx:attributes>  
  21.     </tx:advice>  


试验过程:

         定义一个service方法如下:

         public SMSTiming createSMSTiming(SMSTiming smsTiming){

                   SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

                   return s;

         }

 

         定义二个异常(先默认配置TestException为Spring事务回滚异常):

            publicclass MyTestException extends Exception

            publicclass TestException extends Exception

 

         注意看下:每次这个方法的不同处(抛出的异常不同)。

 

测试1

public SMSTiming createSMSTiming(SMSTiming smsTiming){

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       int i = 4/0; //人为产生异常(实际这里抛出了ArithmeticException运行异常)

       return s;

    }

测试1结果:会事务回滚----数据库中未插入新数据。

 

 

测试2

        public SMSTiming createSMSTiming(SMSTiming smsTiming) throws Exception{//受检异常(非运行异常)必须抛出

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       try{

           int i = 4/0; //人为产生异常

       }catch(Exception e){

           thrownew Exception ("");//抛出Exception异常

       }

       return s;

    }

测试2结果:不会事务回滚----数据库中插入新数据。

 

        

测试3

            public SMSTiming createSMSTiming(SMSTiming smsTiming) throws RuntimeException{//运行异常(非受检异常)可以不抛出

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       try{

           int i = 4/0; //人为产生异常

       }catch(Exception e){

           thrownewRuntimeException("");//抛出RuntimeException异常

       }

       return s;

    }

测试3结果:会事务回滚----数据库中未插入新数据

 

测试4

        public SMSTiming createSMSTiming(SMSTiming smsTiming) throws TestException{//受检异常(非运行异常)必须抛出

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       try{

           int i = 4/0; //人为产生异常

       }catch(Exception e){

           thrownewTestException("");//抛出TestException异常

       }

       return s;

    }

测试4结果:会事务回滚----数据库中未插入新数据。

 

测试5

    public SMSTiming createSMSTiming(SMSTiming smsTiming) throws MyTestException{//受检异常(非运行异常)必须抛出

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       try{

           int i = 4/0; //人为产生异常

       }catch(Exception e){

           thrownewMyTestException("");//抛出MyTestException异常

       }

       return s;

    }

 测试5结果:不会事务回滚----数据库中插入新数据。

 

测试6

    public SMSTiming createSMSTiming(SMSTiming smsTiming) throws MyTestException{//受检异常(非运行异常)必须抛出 (注意:此时spring指定配置此异常回滚)

       SMSTiming s= this.getSmsTimingDAO().createSMSTiming(smsTiming);

       try{

           int i = 4/0; //人为产生异常

       }catch(Exception e){

           thrownewMyTestException("");//抛出MyTestException异常

       }

       return s;

    }

 测试6结果:会事务回滚----数据库中未插入新数据。

 

 

试验总结:

测试1、测试3、测试4、测试6会进行事务回滚;测试2、测试5不会进行事务回滚。

 

为什么会这样?因为是异常的类型(受检异常、运行时异常)不同或使用了Springrollback-for配置

 

测试1和测试3是因为抛出了运行时异常,会事务回滚。

 

测试4和测试5、测试6分别抛出受检异常TestException、MyTestException,那为什么测试4和测试6会事务回滚呢?

因为是我们在Spring事务配置中指定了此异常(指定rollback-for)。



又一篇文章:

昨天同事说Spring的Transactional必须添加rollbackFor = Exception.class,否则事务不会回滚。但我原来试过,抛出RuntimeException时是会回滚的。于是查看Spring的Transactional的API文档,发现下面这段:

If no rules are relevant to the exception, it will be treated like DefaultTransactionAttribute (rolling back on runtime exceptions).

后面又试了下发现,如果不添加rollbackFor等属性,Spring碰到Unchecked Exceptions都会回滚,不仅是RuntimeException,也包括Error。


<think> 首先,用户的问题是关于达梦数据库(DAMENG Database)的查询报错:“Could not roll back JDBC transaction; nested exception is java.sql.SQLException: Connection is closed”。我需要提供解决方案。 从系统级指令中,我有一些引用:引用[1]、[2]、[3]描述了类似的错误,但涉及JDBC事务回滚失败,原因是连接已关闭。这些引用似乎是关于Spring框架中事务处理的错误,例如在MySQL或Hibernate中。 用户明确说这些引用仅供参考,并不是真正的需求。我应该基于用户的问题和我的回答来提供解决方案。用户的问题是针对达梦数据库的。 关键点: - 错误信息:在回滚JDBC事务时连接已关闭。 - 这通常发生在数据库连接在事务完成前被关闭了。 常见原因: 1. 连接泄漏:在事务中使用了连接,但没有正确关闭。 2. 超时:数据库连接超时或被服务器关闭。 3. 资源管理不当:在事务处理过程中连接被提前释放。 4. 配置问题:连接池配置不合理,如超时时间设置过短。 解决方案步骤: 1. **检查连接管理**: - 确保在事务完成后正确关闭数据库连接。 - 使用连接池(如HikariCP、DBCP)来管理连接。 - 避免在事务中手动关闭连接。 2. **检查连接池配置**: - 增加连接的超时时间。 - 设置合理的空闲连接超时和最大连接数。 - 对于达梦数据库,检查JDBC驱动版本是否兼容。 3. **检查事务代码**: - 确保事务边界正确设置。 - 使用Spring的@Transactional注解或编程式事务管理。 - 确保在事务提交或回滚前连接是活动的。 4. **日志和调试**: - 启用详细日志记录,查看连接关闭的时机。 - 检查是否有异常导致连接提前关闭。 5. **数据库端检查**: - 检查达梦数据库的连接超时设置。 - 确保数据库服务器没有主动断开空闲连接。 - 更新JDBC驱动到最新版本。 6. **特定于达梦数据库的解决方案**: - 达梦数据库可能有自己的JDBC驱动和配置参数。 - 参考达梦官方文档或社区支持。 - 检查驱动是否支持Spring事务管理。 由于用户提到了Spring异常,我假设他们使用的是Spring框架。因此,解决方案应涉及Spring事务管理。 结构化回答: - 用中文回答。 - 结构清晰:先解释问题,然后提供步骤。 - 使用行内LaTeX数学表达式(如果需要),但这里可能不需要太多数学。 - 独立公式单独成段(如果适用)。 - 在引用末尾添加引用标识[^1]、[^2]等,但用户提供的引用是示例,我应该基于问题引用。 用户提供的引用是示例性的,我需要引用它们,因为它们与错误相关: - [^1]:Could not roll back JDBC transaction; connection closed. - [^2]:Could not commit JDBC transaction; connection closed. - [^3]:Could not roll back Hibernate transaction; rollback failed. 在回答中,我可以引用这些作为类似问题的例子。 最终,生成相关问题作为结尾。 回答大纲: 1. **问题解释**:简要解释错误原因。 2. **解决方案**:列出步骤。 3. **代码示例**(可选):如果适用,提供代码片段。 4. **相关问题**:生成相关问题。 代码示例:如果需要,提供Java代码来正确管理事务。 确保回答真实可靠:基于一般JDBC和Spring事务知识。 </think>### 达梦数据库报错 "Could not roll back JDBC transaction; nested exception is java.sql.SQLException: Connection is closed" 的解决方案 这个错误通常发生在使用达梦数据库(DM Database)的JDBC连接时,事务回滚过程中检测到数据库连接已被关闭。这源于资源管理不当、连接泄漏或配置问题,导致事务无法正常完成回滚操作[^1]。以下是针对达梦数据库的逐步解决方案。解决方案基于JDBC事务管理和Spring框架的最佳实践,确保您的代码稳定可靠。 #### 1. **检查连接管理和事务边界** - **原因分析**:事务处理过程中,连接被提前关闭(例如,由于代码错误或超时)。达梦数据库的JDBC驱动(如`DmJdbcDriver`)在连接关闭后无法执行回滚- **解决方案**: - 使用连接池(如HikariCP或Druid)管理数据库连接,避免手动关闭连接。 -事务代码中,确保在`commit()`或`rollback()`之后才释放连接。 - 示例代码(使用Spring的`@Transactional`注解): ```java import org.springframework.transaction.annotation.Transactional; import org.springframework.jdbc.core.JdbcTemplate; @Service public class YourService { private final JdbcTemplate jdbcTemplate; public YourService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Transactional public void executeTransaction() { try { // 执行数据库操作,例如查询或更新 jdbcTemplate.update("UPDATE your_table SET column = ? WHERE condition", value); } catch (Exception e) { // 异常时自动回滚Spring处理) throw e; // 抛出异常以触发rollback } } } ``` - 如果使用编程式事务,确保在`finally`块中检查连接状态: ```java import java.sql.Connection; import javax.sql.DataSource; import org.springframework.transaction.support.TransactionTemplate; public void manualTransaction(DataSource dataSource) { TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.execute(status -> { Connection conn = dataSource.getConnection(); try { // 执行SQL操作 // ... // 手动提交或回滚 if (success) { conn.commit(); } else { conn.rollback(); } } catch (SQLException e) { status.setRollbackOnly(); // 标记回滚 throw new RuntimeException("Transaction failed", e); } finally { if (conn != null && !conn.isClosed()) { conn.close(); // 仅在操作完成后关闭 } } return null; }); } ``` #### 2. **优化连接池配置** - **原因分析**:连接池超时设置过短,导致达梦数据库在事务处理中自动断开空闲连接[^2]。 - **解决方案**: - 在连接池配置中增加`maxLifetime`、`idleTimeout` 和 `connectionTimeout` 值(建议设置为30秒以上)。 - 检查达梦JDBC驱动版本(推荐使用官方最新版,如`dm8-jdbc-driver`)。 - 示例配置(Spring Boot中`application.properties`): ```properties # 达梦数据库连接配置 spring.datasource.url=jdbc:dm://localhost:5236/your_database spring.datasource.username=your_username spring.datasource.password=your_password spring.datasource.driver-class-name=dm.jdbc.driver.DmDriver # HikariCP连接池配置(超时时间调整) spring.datasource.hikari.max-lifetime=60000 # 单位毫秒(60秒) spring.datasource.hikari.idle-timeout=30000 # 单位毫秒(30秒) spring.datasource.hikari.connection-timeout=10000 # 单位毫秒(10秒) ``` #### 3. **检查数据库和驱动兼容性** - **原因分析**:达梦数据库服务器或JDBC驱动版本不兼容,导致连接异常关闭。 - **解决方案**: - 升级达梦数据库JDBC驱动到最新版本(从[达梦官网](https://www.dameng.com/)下载)。 - 检查数据库服务器的超时设置(如`INI`配置文件中的`IDLE_TIME`参数),确保它大于连接池超时。 - 测试连接有效性:在连接池中添加`test-on-borrow`或`test-on-return`选项。 #### 4. **日志和调试** - **原因分析**:异常日志可能隐藏了连接关闭的根本原因(如网络中断或资源耗尽)。 - **解决方案**: - 启用Spring事务和JDBC的详细日志(在`logback.xml`中添加): ```xml <logger name="org.springframework.jdbc" level="DEBUG"/> <logger name="org.springframework.transaction" level="DEBUG"/> ``` - 监控连接状态:使用工具(如Druid的监控台)检查连接泄漏。 - 捕获异常:在代码中添加错误处理,记录连接关闭的堆栈信息。 #### 5. **预防措施** - 使用事务重试机制(如Spring Retry)处理临时性错误。 - 定期检查数据库连接的健康状态。 - 参考达梦官方文档的[事务管理章节](https://eco.dameng.com/document/dm/zh-cn/pm/jdbc.html)。 如果以上步骤仍无法解决,请提供更多上下文(如完整错误日志或代码片段)以便进一步分析。这个错误常见于资源管理问题,通过优化配置和代码,通常可以避免[^3]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值