Spring2.5版本下的事务异常处理机制

啥是异常?

异常是程序运行过程当中出现的错误。本文拿实际项目中的应用来简单说明一下事务处理中的异常机制。Java语言的异常处理框架,是Java语言健壮性的一个重要体现。java

开始以前,先给你们上一张java中异常架构图:spring

说明以下:编程

Thorwable类全部异常和错误的超类,有两个子类ErrorException,分别表示错误和异常。
其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常,这两种异常有很大的区别,也称之为不检查异常(Unchecked Exception)
和检查异常(Checked Exception)。架构

  • 运行时异常 ---> UncheckedException
  • 非运行时异常--->CheckedException

 

一、Error和 Exception

  • Error是程序没法处理的错误,好比OutOfMemoryErrorThreadDeath等。这些异常发生时,Java虚拟机(JVM)通常会选择线程终止。框架

  • Exception是程序自己能够处理的异常,这种异常分两大类,运行时异常非运行时异常。程序中应当尽量去处理这些异常。性能

二、运行时异常和非运行时异常

运行时异常都是RuntimeException类及其子类异常,如编码

  • NullPointerException、
  • IndexOutOfBoundsException

这些异常是不检查异常(UncheckedException),程序中能够选择捕获处理,也能够不处理。这些异常通常是由程序逻辑错误引发的,程序应该从逻辑角度尽量避免这类异常的发生。编译是能够经过的spa

非运行时异常是除RuntimeException之外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,若是不处理,程序就不能编译经过。如IOException、SQLException等以及用户自定义的Exception异常,通常状况下不自定义检查异常。线程

如何捕获?

Java异常的捕获和处理是一个不容易把握的事情,若是处理不当,不但会让程序代码的可读性大大下降,并且致使系统性能低下,甚至引起一些难以发现的错误。设计

Java异常处理涉及到五个关键字,分别是:trycatchfinallythrowthrows

try、catch、finally三个语句块应注意的问题

trycatchfinally三个语句块均不能单独使用,三者能够组成 try...catch...finallytry...catchtry...finally三种结构,catch语句能够有一个或多个,finally语句最多一个。

try、catch、finally三个代码块中变量的做用域为代码块内部,分别独立而不能相互访问。若是要在三个块中均可以访问,则须要将变量定义到这些块的外面。

多个catch块时候,只会匹配其中一个异常类并执行catch块代码,而不会再执行别的catch块,而且匹配catch语句的顺序是由上到下。

throw、throws关键字

throw关键字是用于方法体内部,用来抛出一个Throwable类型的异常。

若是抛出了检查异常,则还应该在方法头部声明方法可能抛出的异常类型。该方法的调用者也必须检查处理抛出的异常。若是全部方法都层层上抛获取的异常,最终JVM会进行处理,处理也很简单,就是打印异常消息和堆栈信息。

若是抛出的是Error或RuntimeException,则该方法的调用者可选择处理该异常。有关异常的转译会在下面说明。

当抛出的是运行时异常,程序能够选择处理和不处理

public void insertCustomTable(List list,TransactionStatus transactionStatus ){
		try {
			
			//FIELD_READ_RULE若是为null,则设置默认值0;
			for(int i=0;i<list.size();i++){
				Map custom=(Map)list.get(i);
				if(custom.get("FIELD_READ_RULE")==null || custom.get("FIELD_READ_RULE")==""){
					custom.put("FIELD_READ_RULE", 0);
				}
				
				//是否主键 ,转为0:否,1:是
				String isFieldKey = custom.get("IS_FIELD_KEY").toString();
				custom.put("IS_FIELD_KEY", isFieldKey);
			}
			fillManagerDomain.insertCustom(list);
		} catch (Exception e) {
			log.info("任务建立时,插入表DSP_OFL_CUSTOMER失败,insertCustomTable()!"+e);
//			transactionStatus.isRollbackOnly();
			throw new RuntimeException();
		}
	}

抛出的是非运行时异常时,程序必需要处理,能够继续往外抛,或者捕获

public void insertCustomTable(List list,TransactionStatus transactionStatus ){
		try {
			
			//FIELD_READ_RULE若是为null,则设置默认值0;
			for(int i=0;i<list.size();i++){
				Map custom=(Map)list.get(i);
				if(custom.get("FIELD_READ_RULE")==null || custom.get("FIELD_READ_RULE")==""){
					custom.put("FIELD_READ_RULE", 0);
				}
				
				//是否主键 ,转为0:否,1:是
				String isFieldKey = custom.get("IS_FIELD_KEY").toString();
				custom.put("IS_FIELD_KEY", isFieldKey);
			}
			fillManagerDomain.insertCustom(list);
		} catch (SQLException e) {
			e.printStackTrace();
//			transactionStatus.isRollbackOnly();
			throw new SQLException();
		}
	}

throws关键字用于方法体外部的方法声明部分,用来声明方法可能会抛出某些异常。仅当抛出了检查异常,该方法的调用者才必须处理或者从新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣通常在catch块中打印一下堆栈信息作个勉强处理。下面给出一个简单例子,

Throwable类中的经常使用方法

  • getCause():返回抛出异常的缘由。若是 cause 不存在或未知,则返回 null。
  • getMessage():返回异常的消息信息。
  • printStackTrace():对象的堆栈跟踪输出至错误输出流,做为字段 System.err 的值。

异常处理的通常原则

能处理就早处理,抛出不去还不能处理的就想法消化掉或者转换为RuntimeException处理。由于对于一个应用系统来讲,抛出大量异常是有问题的,应该从程序开发角度尽量的控制异常发生的可能。

对于检查异常,若是不能行之有效的处理,还不如转换为RuntimeException抛出。这样也让上层的代码有选择的余地――可处理也可不处理。

对于一个应用系统来讲,应该有本身的一套异常处理框架,这样当异常发生时,也能获得统一的处理风格,将优雅的异常信息反馈给用户。

Java异常总结

Java将异常区分为Error与Exception,Error是程序无力处理的错误,Exception是程序能够处理的错误。异常处理是为了程序的健壮性。

异常能处理就处理,不能处理就抛出,最终没有处理的异常JVM会进行处理。

异常能够传播,也能够相互转译,但应该根据须要选择合理的异常转译的方向。

对于一个应用系统,设计一套良好的异常处理体系很重要。这一点在系统设计的时候就应该考虑到。

Exception通常分为Checked异常Runtime异常,全部RuntimeException类及其子类的实例被称为Runtime异常,不属于该范畴的异常则被称为CheckedException

事务如何处理?

开始以前,先给你们两个概念

编程式事务:所谓编程式事务指的是经过编码方式实现事务,即相似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate

声明式事务:管理创建在AOP之上的。其本质是对方法先后进行拦截,而后在目标方法开始以前建立或者加入一个事务,在执行完目标方法以后根据执行状况提交或者回滚事务。声明式事务最大的优势就是不须要经过编程的方式管理事务,这样就不须要在业务逻辑代码中掺琐事务管理的代码,只需在配置文件中作相关的事务规则声明(或经过基于@Transactional注解的方式),即可以将事务规则应用到业务逻辑中。

      显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。

声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就能够得到彻底的事务支持。和编程式事务相比,声明式事务惟一不足地方是,后者的最细粒度只能做用到方法级别,没法作到像编程式事务那样能够做用到代码块级别。可是即使有这样的需求,也存在不少变通的方法,好比,能够将须要进行事务管理的代码块独立为方法等等。

回归spring2.5版本,咱们采用配置方式来实现事务管理,其实这种方式是一种类编程式事务的管理:

为了避嫌,已作模糊处理,以下图所示:

事务模板jdbcTrasactionTemplate 必须进行声明,见以下图:

事务模板声明以后,须要service层进行引用,方可开启事务管理功能,见以下图:

事务异常机制如何?

Spring被事务管理的方法,须要抛出非检查异常,即运行期异常才能进行回滚

对非检查型类异常能够不用捕获,而检查型异常则必须用try语句块进行处理或者把异常交给上级方法处理总之就是必须写代码处理它。因此必须在service捕获异常,而后再次抛出,这样事务方才起效。

在spring的事务管理环境下,使用unckeckedException能够极大地简化异常的处理,只须要在事务层声明可能抛出的异常(这里的异常能够是自定义的unckecked exception体系),在全部的中间层都只是须要简单throws便可,不须要捕捉和处理,直接到最高层,好比UI层再进行异常的捕捉和处理

声明这个service全部方法须要事务管理。每个业务方法开始时都会打开一个事务。

Spring默认状况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked若是遇到checked意外就不回滚。

如何改变默认规则:

  • 1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)

  • 2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)

  • 3 不须要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

注意:

(1) 若是异常被try{}catch{}了,事务就不回滚了,若是想让事务回滚必须再往外抛try{}catch{throw RuntimeException}。

(2) 若是不想抛出非检查类型异常,即运行期异常,咱们能够在catch中,直接显示的回滚事务,例如:

public void insertCustomTable(List list,TransactionStatus transactionStatus ){
		try {
			
			//FIELD_READ_RULE若是为null,则设置默认值0;
			for(int i=0;i<list.size();i++){
				Map custom=(Map)list.get(i);
				if(custom.get("FIELD_READ_RULE")==null || custom.get("FIELD_READ_RULE")==""){
					custom.put("FIELD_READ_RULE", 0);
				}
				
				//是否主键 ,转为0:否,1:是
				String isFieldKey = custom.get("IS_FIELD_KEY").toString();
				custom.put("IS_FIELD_KEY", isFieldKey);
			}
			fillManagerDomain.insertCustom(list);
		} catch (Exception e) {
			log.info("任务建立时,插入表DSP_OFL_CUSTOMER失败,insertCustomTable()!"+e);
			transactionStatus.isRollbackOnly();
//			throw new RuntimeException();
		}
	}

这种方式,也能够回滚事务.

若是既没有向上抛出异常,也没有显示回滚,那么结果就一种,数据入库,事务失效.

总结,

一个统一的异常层次结构对于提供服务抽象是必需的。 最重要的就是org.springframework.dao.DataAccessException以及其子类了。 须要强调的是Spring的异常机制重点在于应用编程模型。与SqlException和其余数据存取API不一样的是: Spring的异常机制是为了让开发者使用最少, 最清晰的代码。DataAccessException和其余底层异常都是非检查性异常(unchecked exception)。 spring的原则之一就是基层异常就应该是非检查性异常. 缘由以下:

  • 基层异常一般来讲是不可恢复的。
  • 检查性异常将会下降异常层次结构的价值.若是底层异常是检查性的, 那么就须要在全部地方添加catch语句进行捕获。
  • try/catch代码块冗长混乱,并且不增长多少价值。使用检查异常理论上很好, 可是实际上好象并不如此。Hibernate3也将从检查性异常转为非检查性异常。

    很是感谢各位审阅本博文,文章说明有不合适的地方,还请各位能及时提出建议与意见!

    完

相关文章
相关标签/搜索