Spring MVC 4编程式事务:父类方法抛异常时子类事务回滚方案
如何让RoleDao的createRole()在method1/method2抛出异常时回滚事务?
咱们先拆解当前代码的核心问题:你的method1()和method2()捕获数据库相关异常后,会包装成RuntimeException抛出,但createRole()的catch块只处理InvalidResultSetAccessException和DataAccessException——这俩都是RuntimeException的子类,根本抓不到你抛出的顶层RuntimeException。结果就是,当method1/method2抛出异常时,createRole()里的rollback()代码根本不会执行,事务可能因为未被正确处理而出现数据不一致的问题。
下面给你几个可行的解决方案,按健壮性排序:
方案一:用finally块确保事务收尾(最佳实践)
编程式事务最稳妥的写法是借助finally块,不管代码正常执行还是抛出异常,都能保证事务不会悬挂或泄漏资源:
public int createRole() { TransactionStatus status = null; try { TransactionDefinition def = new DefaultTransactionDefinition(); status = transactionManager.getTransaction(def); this.method1(); this.method2(); transactionManager.commit(status); } catch (Exception e) { // 捕获所有异常,触发回滚 if (status != null) { transactionManager.rollback(status); } throw new RuntimeException(e); // 包装后继续向上抛出,不吞掉异常 } finally { // 兜底检查:如果事务还未完成,强制回滚 if (status != null && !status.isCompleted()) { transactionManager.rollback(status); } } }
这里把status变量提到try块外,避免回滚时出现空指针异常,finally块的兜底逻辑能彻底杜绝事务资源泄漏。
方案二:调整catch块,捕获RuntimeException
如果不想用finally块,也可以直接修改createRole()的catch块,明确捕获method1/method2抛出的RuntimeException:
public int createRole() { TransactionStatus status = null; try { TransactionDefinition def = new DefaultTransactionDefinition(); status = transactionManager.getTransaction(def); this.method1(); this.method2(); transactionManager.commit(status); } catch (RuntimeException e) { // 精准捕获method1/method2抛出的包装异常 if (status != null) { transactionManager.rollback(status); } throw e; // 直接抛出原始异常,保留错误栈 } }
方案三:简化异常传递逻辑
其实你可以把GenericDao里的异常捕获逻辑去掉——既然最终还是要抛出异常,不如直接让Spring的原始DataAccessException(本身就是unchecked异常)向上传递,这样代码会更简洁:
// 修改GenericDao,移除不必要的异常包装 public class GenericDao{ public int method1() { // 直接执行数据库操作,异常由Spring自动包装并向上传递 .......... ......... } public int method2() { .......... ......... } } // 此时createRole()可以直接捕获DataAccessException public int createRole() { TransactionStatus status = null; try { TransactionDefinition def = new DefaultTransactionDefinition(); status = transactionManager.getTransaction(def); this.method1(); this.method2(); transactionManager.commit(status); } catch (DataAccessException e) { if (status != null) { transactionManager.rollback(status); } throw new RuntimeException(e); } }
内容的提问来源于stack exchange,提问作者James Mwaiponya
相关产品推荐
相关产品推荐

