Spring事务处理探究
开发环境:
OS:windows XP
Web Server: jakarta-tomcat-5.0.28
DataBase Server: MS SQL Server 2000 (打了SP3补丁)
IDE: MyEclipse 6.0.1
测试案例系统结构:
web层<---->Service
层<---->DAO
层
web层就是Struts2,DAO
使用hibernate -3.3.1.GA-dist.zip,spring
是spring
-framework-2.5.5
数据库表和它一样吧:
student1和Student2,表结构相同:id,name,address.其中id为主键且为自增长型.
student1表中有一条记录:
测试情形一:
web层捕获异常并处理,DAO
层不捕获异常,Service
也不捕获异常.
Service
层接口:
- public interface Student1Service {
- void addStudent1(Student1 stu);
- }
- public interface StudentSService {
- void addStudent2(Student2 stu) throws SaveException;
- }
Service 实现
- public void addStudent1(Student1 stu) {
- stufDAO.insertStuF(stu);
- }
- public void addStudent2(Student2 stu) throws SaveException {
- String[] aa={"ww","ww","ww"};
- for(int i=0;i<5;i++){ //出错
- System.out.println(aa[i]);
- }
- stusDAO.insertStuS(stu);
- }
DAO 层接口
- public interface StudentFDAO {
- void insertStuF(Student1 stu);
- }
- public interface StudentSDAO {
- void insertStuS(Student2 stu);
- }
DAO 实现
- public void insertStuF(Student1 stu) {
- getHibernateTemplate().save (stu);
- }
- public void insertStuS(Student2 stu) {
- getHibernateTemplate().save (stu);
- }
Action
- public String execute() throws Exception{
- Student1 sti=new Student1(stu1Name,stu1Address);
- Student2 stu=new Student2(stu1Name,stu1Address);
- try{
- studentfService.addStudent1(sti);
- studentsService.addStudent2(stu);
- }catch(DataAccessException e){
- System.out.println(“error”);
- return “failer”:
- }
- return SUCCESS;
- }
JSP
- <form action="testaction.action" method="POST">
- <table>
- <tr><td>名:</td><td><input type="text" value="stu1Name" name="stu1Name"></td></tr>
- <tr><td>地址:</td><td><input type="text" value="stu1Address" name="stu1Address"></td></tr>
- <Tr><td></td><td><input type="submit" value="提交" style="width:80px"></td></Tr>
- </table>
- </form>
配置文件
- <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
- <property name="sessionFactory" ref="sessionFactory"/>
- </bean>
- <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
- <property name="transactionManager" ref="transactionManager"/>
- <property name="transactionAttributes">
- <props>
- <prop key="*">PROPAGATION_REQUIRED</prop>
- <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
- </props>
- </property>
- </bean>
- <!-- 定义BeanNameAutoProxyCreator-->
- <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
- <property name="beanNames">
- <list>
- <value>st1Service</value>
- <value>stsService</value>
- </list>
- </property>
- <property name="interceptorNames">
- <list>
- <value>transactionInterceptor</value>
- </list>
- </property>
- </bean>
运行程序:启动服务器,并部署.进入index.jsp页面,点击提交
查看数据库: 两个表中都没有新增数据。
小结:如果DAO
层和Service
不捕获异常而在web层捕获异常,web成功捕获异常(抛不抛出都行),spring
事务管理成功!
二、Service
层捕捉异常,DAO
不捕捉异常(下面就简单一点了,直接给结论把)
1.如果,你在Service
捕捉异常而不抛出异常,Action层捕捉不到异常,那么事物处理失败。
2.如果在Service
捕捉异常并且抛出异常,那么,如果抛出的异常是Checked异常(自定义的),不会回滚。也正好验证了Spring
事物里的这段话:默认情况下,事物只会出现运行异常(runtime exception)时回滚,而出现受阻异常(checked exception)时不回滚。
3.如果抛出的异常是RuntimeChecked异常(自定义的),那么事物处理成功。
所以如果你的抛出的异常是CheckException,你配置事物的时候必须加上-Exception参数,指定出现Checked异常时回滚,也就是PROPAGATION_REQUIRED,-Exception。
三、DAO
捕获异常
如果DAO
捕捉异常而不抛出异常,Action层捕捉不到异常,Service
捕获不到异常。
运行程序:启动服务器,并部署.进入index.jsp页面,点击提交
查看数据库:student1表中新曾数据,Student2表中都没有数据。事务处理失败。
小结:
前面的文章中提到,如果DAO
的每一个方法不捕获异常,Service
层捕获DataAccessException异常并抛出自己定义异常(自定义异常为DataAccessException的子类),Web层可以捕获到异常,spring
事务管理成功!
然后在总结中说:
1.spring
在进行声明时事务管理时,通过捕获Service
层方法的DataAccessException来提交和回滚事务的,而Service
层方法的DataAccessException又是来自调用DAO
层方法所产生的异常.
2.我们一般在写DAO
层代码时,如果继承JdbcDaoSupport 类,并使用此类所实现的JdbcTemplate来执行数据库操作,此类会自动把低层的SQLException转化成DataAccessException以及DataAccessException的子类.
3.一般在Service
层我们可以自己捕获DAO
方法所产成的DataAccessException,然后再抛出一个业务方法有意义的异常(ps:此异常最好继承DataAccessException),然后在Web层捕获,这样我们就可以手动编码的灵活实现通过业务方法执行的成功和失败来向用户转发不同的页面
其实这到不是因为自定义异常为DataAccessException的子类,而是DataAccessException是
RuntimeException,那么它的子类也是Runtime,所以抛出异常是会回滚的。所以你的自定义异常继承自
RuntimeException也可以。
再发表一些个人看发吧,在Spring
中所有数据库操作异常都转换为DataAccessException这个运行时异常,不过在业务层中也有可能抛出一些其他的运行时异常,那么这时候在一些简单的项目中,我们直接用下面的方式也未尝不可:
Java代码
- try{
- …….
- ………
- ……..(可能会有其他运行时异常,并不仅仅是DataAccessException)
- }catch(Exception e){
- log.error(“error”);
- throw new CheckedException(“error”); //抛出受检查异常
- }
然后在Action层捕捉这个受检查异常
Java代码
- try{
- service.method();
- }catch(CheckedException e){
- }
这个异常,它是可以被调用者正确处理的。返回相应的提示。当然,如果抛出CheckException,那么Spring
声明式事物的时候就应该加上-Exception参数,前面已经提到。
当然在一些业务逻辑比较复杂,并且要根据复杂的逻辑返回不同的试图的时候,这样整个的try{}catch(){}就不合适了,这时候应该将不正常的事件流转换为运行时异常,并且在方法声明中详细的说明该方法可能会抛出的unChekced异常。由调用者自己去决定是否捕获unChecked异常
,返回相应的视图。
总结:
1.一般DAO
中不捕捉异常也不抛出异常
2.至于Service
层和Action层,是在service
层抛出CheckException然后由action捕捉,还是在service
中全部采用unCheckException,然后在action中有选择的捕捉或者都不捕捉就要看你自己的选择了, 个人认为逻辑比较简单的可以选择前者,比较复杂就选择后者。
3.禁忌捕捉异常而不做任何处理(如果是dao
层或者service
层,最好抛出),不然很有可能造成事务处理失败。