浏览 4484 次
锁定老帖子 主题:Spring事务处理探究
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-12-08
最后修改:2008-12-13
http://www.iteye.com/topic/34867一文以后想写的,而且前段时间与jbpm整合的时候才发现自己的事务设置一直有问题,根本无法回滚(汗,都是网络上照抄的,没测试),再加上它的使用的spring版本1.2,所以觉得有必要自己再测试,加深影像。
本文主要是在看了 关于spring声明式事务管理异常处理的测试和小结对事务有任何不清楚的,可以参考一下这个 http://bhsc-happy.iteye.com/blog/288983 开发环境: 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这个运行时异常,不过在业务层中也有可能抛出一些其他的运行时异常,那么这时候在一些简单的项目中,我们直接用下面的方式也未尝不可: try{ ……. ……… ……..(可能会有其他运行时异常,并不仅仅是DataAccessException) }catch(Exception e){ log.error(“error”); throw new CheckedException(“error”); //抛出受检查异常 } 然后在Action层捕捉这个受检查异常 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层,最好抛出),不然很有可能造成事务处理失败。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-03-16
我谈谈我的看法
首先我觉得异常肯定不应该在DAO层捕获,而应该在Action中(业务异常在Service中捕获,并再次抛出异常)捕获。道理很简单,如果是发生错误,必须得让用户知道。如果是业务逻辑发生错误,那么应该在service捕获,并对异常封装再抛出,这样做的好处是1、让action知道业务出错,2,事务回滚。如果是数据库类型的异常,service不用捕获,因为该种类异常spring会自动捕获并封装 事务回滚的一般原因是DAO在和数据库交互的时候有异常发生。而且如果使用了SPERING的话,spring一般会把很多异常封装成为Runtime异常,所以spring的事务可以回滚。 spring对事务的封装是基于AOP 大体的实现是【具体的记得不是很清楚了,很久没有搞过spring】 tran=beginTransaction//开启事务 try{ serviceA{ Dao.save(obj)//可能抛出异常 } }catch(runtimeException e){ tran.rollbace//回滚,并标志事务已经结束 throw Exception//向上抛出异常 } tran.commit//提交事务(若事务没有结束) |
|
返回顶楼 | |