`

Spring事务管理例子

阅读更多
关于spring事务管理以及异常处理的帖子,本论坛争论颇多,各有各的测试代码,也各有各的测试结果,不知道是spring版本的不同还是各测试的例子的不同而导致测试结果出现差异.本人也很想弄清楚spring是如何对Service进行事务管理的,并且还去看了一下spring框架关于事务管理几个相关类的源码,可惜由于本人功力有限,只看懂了皮毛.既然源代码看不懂,那么只有运用例子进行测试,虽然笨了点,不过管是白猫还是黑猫,能捉老鼠就是好猫.:)为引起不必要的争论,本帖子只针对本案例的测试结果进行小结,并保证此测试代码在本人的运行环境绝对正确.

开发环境:
OS:windows 2003 Server
Web Server: jakarta-tomcat-5.0.28
DataBase Server: MS SQL Server 2000
IDE:  Eclipse 3.2.0

测试案例系统结构:
web层<---->Service层<---->DAO层

web层使用struts 1.1,DAO使用的spring的JDBC,spring版本1.2

数据库中有两张表:
student1和Student2,表结构相同:id,name,address.其中id为主键且为自增长型.
student1表中有一条记录:
    id  name       address 
    1   xiaoming    wuhan 
    
student2表中记录为空

测试情形一:
web层捕获异常并处理,DAO层不捕获异常,Service也不捕获异常.

Service层接口:
    public interface StudentManagerService { 
       
        public void  bus_method(); 
    } 

DAO层接口
    public interface StudentDAO { 
       
        public void  deleteStudent1(); 
        public void  insertStudent2(); 
    } 

StudentDAO接口的实现:
    public class StudentDAOImp extends JdbcDaoSupport implements StudentDAO{ 
         //删除student1表中的id=1的记录 
         public void  deleteStudent1(){ 
         JdbcTemplate jt=this.getJdbcTemplate(); 
         jt.update("delete from student1 where id=1");      
       } 
          
         //将student1表中删除的记录插入到student2中,但是此方法实现有错,因为 
       //id字段设置为自增长的,所以在插入记录时我们不能指定值 
          public void  insertStudent2(){ 
           JdbcTemplate jt=this.getJdbcTemplate(); 
                String arg[]=new String[3]; 
           arg[0]="1"; 
                arg[1]="xiaoming"; 
           arg[2]="wuhan"; 
                jt.update("insert student2(id,name,address) values(?,?,?)",arg); 
         } 
     
    }  

StudentManagerService 接口的实现:
 
    public class StudentManagerServiceImp implements StudentManagerService{ 
      private StudentDAO  stdDAO; 
      
      public void setStdDAO(StudentDAO   stdDAO){ 
         this.stdDAO=stdDAO; 
      } 
        
      //此方法为事务型的:删除student1中的记录成功且插入student2的记录也成功, 
     //如果insertStudent2()方法执行失败,那么deleteStudent1()方法也应该会失败 
      public void  bus_method(){ 
        this.stdDAO.deleteStudent1(); 
        this.stdDAO.insertStudent2(); 
      } 
       
    }  

web层:
三个jsp,一个action:
index.jsp ==>首页面.上面仅仅有一个超链接<a herf="test.do">执行</a>
chenggong.jsp ==>Service执行成功后转向的JSP页面
shibai.jsp ====>Service执行失败后转向的JSP页面

action实现:
    public class StudentManagerAction  extends  Action{ 
     
         public ActionForward execute(ActionMapping mapping, ActionForm form, 
        HttpServletRequest request, HttpServletResponse response) { 
             try{ 
                 WebApplicationContext appContext=WebApplicationContextUtils.  
                      getWebApplicationContext(this.getServlet().getServletContext()); 
            StudentManagerService stdm=(StudentManagerService)appContext. 
                                            getBean("stdServiceManager"); 
                stdm.bus_method(); 
                return mapping.findForward("chenggong"); 
         } 
         catch(DataAccessException e){ 
            System.err.println("action execute service exception!"); 
            return mapping.findForward("shibai"); 
          } 
     
        } 
    } 

配置文件:
web.xml
    <?xml version="1.0" encoding="UTF-8"?> 
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> 
      <context-param> 
        <param-name>log4jConfigLocation</param-name> 
        <param-value>/WEB-INF/log4j.properties</param-value> 
      </context-param> 
      <context-param> 
        <param-name>contextConfigLocation</param-name> 
        <param-value>/WEB-INF/applicationContext.xml</param-value> 
      </context-param> 
      <listener> 
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> 
      </listener> 
      <listener> 
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
      </listener> 
      <servlet> 
        <servlet-name>action</servlet-name> 
        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> 
        <init-param> 
          <param-name>config</param-name> 
          <param-value>/WEB-INF/struts-config.xml</param-value> 
        </init-param> 
        <init-param> 
          <param-name>debug</param-name> 
          <param-value>3</param-value> 
        </init-param> 
        <init-param> 
          <param-name>detail</param-name> 
          <param-value>3</param-value> 
        </init-param> 
        <load-on-startup>0</load-on-startup> 
      </servlet> 
      <servlet-mapping> 
        <servlet-name>action</servlet-name> 
        <url-pattern>*.do</url-pattern> 
      </servlet-mapping> 
    </web-app> 

sturts-config.xml
   <struts-config> 
      <action-mappings > 
        <action  input="/index.jsp"  path="/test"  type="test.StudentManagerAction   > 
          <forward name="chenggong" path="/chenggong.jsp" /> 
          <forward name="shibai" path="/shibai.jsp" /> 
        </action> 
      </action-mappings> 
      <message-resources parameter="test.ApplicationResources" /> 
    </struts-config> 

applicationContext.xml
    <?xml version="1.0" encoding="UTF-8"?> 
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> 
    <beans> 
        <bean id="dataSource" 
          class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" > 
          <property name="driverClassName" value="com.microsoft.jdbc.sqlserver.SQLServerDriver"></property> 
          <property name="url" value="jdbc:microsoft:sqlserver://127.0.0.1:1433;databasename=test"></property> 
           <property name="username" value="sa"></property> 
           <property name="password" value="sa"></property> 
        </bean> 
         
         <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
            <property name="dataSource" ref="dataSource"/> 
         </bean> 
             
        <bean id="baseTxProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"  lazy-init="true"> 
            <property name="transactionManager"> 
            <ref bean="transactionManager" /> 
             </property> 
             <property name="transactionAttributes"> 
             <props> 
                 <prop key="*">PROPAGATION_REQUIRED</prop> 
            </props> 
             </property> 
        </bean> 
     
        <bean id="stdServiceManager"  parent="baseTxProxy" > 
            <property name="target"> 
                 <bean class="test.StudentManagerServiceImp">     
                           <property name="stdDAO"> 
                          <ref bean="stdDAO"/> 
                           </property>     
                      </bean>  
            </property>  
        </bean> 
     
        <bean id="stdDAO" class="test.StudentDAOImp"> 
           <property name="dataSource" ref="dataSource"/> 
        </bean> 
    </beans> 

运行程序:启动服务器,并部署.进入index.jsp页面,点击"执行"超链接"---->页面跳向shibai.jsp
查看控制台:打印有:action execute service exception!
查看数据库: student1表中的[1 xiaoming  wuhan] 记录仍然存在,student2表仍然为空.
小结:如果DAO层和Service不捕获异常而在web层捕获异常,web成功捕获异常,spring事务管理成功!

测试情形二:
web层捕获异常并处理,Service捕获异常并处理,DAO层不捕获异常.

修改StudentManagerServiceImp类
    public class StudentManagerServiceImp implements StudentManagerService{ 
      private StudentDAO  stdDAO; 
     
      public void setStdDAO(StudentDAO   stdDAO){ 
         this.stdDAO=stdDAO; 
      } 
        
      //此方法为事务型的,删除student1中的记录成功且插入student2的记录也成功 
     //如果insertStudent2()方法执行失败,那么deleteStudent1()也应该会失败 
      public void  bus_method(){ 
       try{ 
          this.stdDAO.deleteStudent1(); 
          this.stdDAO.insertStudent2(); 
       } 
       catch(DataAccessException de) 
           System.err.println("service execute exception!"); 
        } 
      } 
       
    } 


运行程序:启动服务器,并部署.进入index.jsp页面,点击"执行"超链接"---->页面跳向chenggong.jsp
查看控制台:打印有:service execute exception!
查看数据库: student1表中的[1  xiaoming  wuhan] 记录不存在,student2表仍然为空.
小结:如果Service捕获异常并处理而不向外抛出,web层捕获不到异常,spring事务管理失败!

测试情形(还原表中的数据)三:
web层捕获异常,Service捕获异常,DAO层也捕获异常.
修改StudentDAOImp类代码
    public class StudentDAOImp extends JdbcDaoSupport implements StudentDAO{ 
         //删除student1表中的id=1的记录 
         public void  deleteStudent1(){ 
            try{ 
         JdbcTemplate jt=this.getJdbcTemplate(); 
         jt.update("delete from student1 where id=1"); 
           } 
           catch(DataAccessException e){ 
             System.err.println("dao deleteStudent1 execute exception!"); 
           }      
       } 
          
         //将student1表中删除的记录插入到student2中,但是此方法实现有错,因为 
       //id字段设置为自增长的,所以在插入记录时我们不能指定值 
          public void  insertStudent2(){ 
              try{ 
           JdbcTemplate jt=this.getJdbcTemplate(); 
                String arg[]=new String[3]; 
           arg[0]="1"; 
                arg[1]="xiaoming"; 
           arg[2]="wuhan"; 
                jt.update("insert student2(id,name,address) values(?,?,?)",arg); 
             } 
             catch(DataAccessException  e){ 
                System.err.println("dao insertStudent2  execute exception!"); 
     
             } 
         } 
     
    }  

运行程序:启动服务器,并部署.进入index.jsp页面,点击"执行"超链接"---->页面跳向chenggong.jsp
查看控制台:打印有:dao insertStudent2  execute exception!
查看数据库: student1表中的 1,xiaoming,wuhan 记录不存在,student2表仍然为空.
小结如果DAO的每一个方法自己捕获异常并处理而不向外抛出,Service层捕获不到异常,Web层同样捕获不到异常,spring事务管理失败!

测试情形四:
还原数据库中的数据
还原StudentDAOImp类中的方法为测试情形一中的实现web层捕获异常Service抛出的自定义异常StudentManagerException Service捕获DataAccessException并抛出StudentManagerException,StudentManagerException为DataAccessException的子类DAO层不捕获异常

修改StudentManagerServiceImp类的实现:
    public class StudentManagerServiceImp implements StudentManagerService{ 
      private StudentDAO  stdDAO; 
      
      public void setStdDAO(StudentDAO   stdDAO){ 
         this.stdDAO=stdDAO; 
      } 
        
      //此方法为事务型的,删除student1中的记录成功且插入student2的记录也成功 
    //如果insertStudent2()方法执行失败,那么deleteStudent1()也应该会失败 
      public void  bus_method() throws StudentManagerException{ 
       try{ 
          this.stdDAO.deleteStudent1(); 
          this.stdDAO.insertStudent2(); 
       } 
       catch(DataAccessException de) 
           System.err.println("service execute exception!"); 
         throw new StudentManagerException();//StudentManagerException类继承DataAcce                          
	          //ssException异常 
        } 
      } 
    } 

修改StudentManagerAction
    public class StudentManagerAction  extends  Action{ 
     
         public ActionForward execute(ActionMapping mapping, ActionForm form, 
        HttpServletRequest request, HttpServletResponse response) { 
             try{ 
                 WebApplicationContext appContext=WebApplicationContextUtils.  
                      getWebApplicationContext(this.getServlet().getServletContext()); 
            StudentManagerService stdm=(StudentManagerService)appContext. 
                                            getBean("stdServiceManager"); 
                stdm.bus_method(); 
                return mapping.findForward("chenggong"); 
         } 
         catch(StudentManagerException e){ 
            System.err.println("action execute service exception!"); 
            return mapping.findForward("shibai"); 
          } 
     
        } 
    } 

运行程序:启动服务器,并部署.进入index.jsp页面,点击"执行"超链接"---->页面跳向shibai.jsp
查看控制台:打印有:service execute exception! action execute service exception!
查看数据库:student1表中的 [1,xiaoming,wuhan] 记录仍然存在,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层捕获,这样我们就可以手动编码的灵活实现通过业务方法执行的成功和失败来向用户转发不同的页面. 
分享到:
评论

相关推荐

    spring事务案例分析.zip

    本主题将深入探讨“Spring事务案例分析.zip”中的关键知识点,包括Spring事务管理及其在实际项目中的应用。 首先,我们来了解什么是Spring事务管理。在分布式系统或数据库操作中,事务管理是确保数据一致性和完整性...

    spring 事务管理例子(TransactionProxyFactoryBean代理机制 和 tx/aop)

    总结来说,本例子通过`TransactionProxyFactoryBean`和`@Transactional`展示了如何在Spring中实现声明式事务管理,这两种方式都利用了AOP来封装事务逻辑,使代码更加整洁,降低了事务管理的复杂性。在实际应用中,...

    Spring的事务管理小案例

    在本文中,我们将深入探讨Spring框架中的事务管理。Spring是一个广泛应用的Java企业级应用开发框架,它提供...如果你想要深入了解,可以参考提供的博客链接或其他相关资料,进一步学习Spring事务管理的细节和最佳实践。

    Spring事务管理的jar包

    Spring事务管理分为编程式事务管理和声明式事务管理两种方式。编程式事务管理通过使用PlatformTransactionManager接口的begin、commit、rollback等方法直接控制事务的生命周期,这种方式灵活但容易导致代码过于繁琐...

    Spring 事务简单完整例子

    本文将深入探讨在Spring框架中如何管理事务,以“Spring 事务简单完整例子”为出发点,结合标签“spring,事务,jdbc事务”,我们将详细解释Spring事务管理的原理和实践。 首先,Spring提供了两种事务管理方式:编程...

    spring事务管理

    为了更好地理解Spring事务管理的实际应用,可以通过以下简单的例子进行演示: ```java @Service public class AccountService { @Autowired private AccountRepository accountRepository; @Transactional...

    spring 自定义事务管理器,编程式事务,声明式事务@Transactional使用

    在Spring框架中,事务管理是核心功能之一,它确保了数据操作的一致性和完整性。本教程将深入探讨如何在Spring中实现自定义事务管理器...这将加深你对Spring事务管理的理解,帮助你在实际项目中更加熟练地运用这些技术。

    Spring事务例子

    通过运行这些例子,你将对Spring事务管理有更深入的理解,知道何时选择编程式还是声明式事务,以及如何有效地处理事务异常。 总之,Spring的事务管理是其强大功能的一部分,无论是编程式还是声明式,都能帮助开发者...

    spring事务管理.rar

    Spring事务管理是Spring框架的核心特性之一,它提供了一种强大且灵活的方式来管理应用程序中的事务边界。在企业级Java应用中,事务处理是确保数据一致性、完整性和可靠性的关键部分。本篇文章将深入探讨Spring的事务...

    SPRING事务机制DEMO

    Spring事务管理提供了一种声明式的方式来控制事务边界,使得开发者无需显式地调用开始、提交或回滚事务。 在Spring中,有以下两种事务管理方式: 1. **编程式事务管理**:开发者需要手动调用`...

    spring 注解事务管理

    Spring事务管理主要分为两种方式:编程式事务管理和声明式事务管理。编程式事务管理是通过编写代码来控制事务的开始、提交、回滚等操作,而声明式事务管理则是通过配置或注解来定义事务边界,更加直观和易于使用。 ...

    Spring事务管理A方法内部调用B方法的回滚问题测试代码

    在Spring框架中,事务管理是核心特性之一,用于确保数据操作的一致性和完整性。当一个方法(A方法)内部调用另一个方法(B方法)时,可能会遇到事务控制...这个示例代码对于理解和调试Spring事务管理的问题非常有帮助。

    Spring中事务的传播属性详解

    本文将详细介绍Spring中的事务传播属性,并通过具体的例子来解释每种传播行为的特点。 #### 二、事务传播属性概述 事务传播行为(Propagation)定义了当一个事务方法被另一个事务方法调用时的行为。在Spring中,...

    Spring事务优缺点及使用详解.docx

    Spring事务管理提供了统一的事务处理模型,使得开发者无需关注具体数据库访问技术的事务细节,简化了事务控制代码,提高了代码的可读性和可维护性。无论是使用注解还是AOP配置,都能有效地管理和协调事务,确保应用...

    spring 事务基于注解模式

    Spring事务管理分为编程式和声明式两种。编程式事务管理通过编程的方式(如使用`TransactionTemplate`或直接调用`PlatformTransactionManager`)来控制事务的开始、提交、回滚等操作。而声明式事务管理则是在配置...

    Spring2.5实现事务管理(本地事务、分布式事务).doc

    Spring 2.5 实现事务管理(本地事务、分布式事务) Spring 框架提供了对事务管理的支持,它可以使得事务的管理变得更加简洁和灵活。事务管理是指在多个操作中维持一致性的机制,它可以确保在多个操作中,如果某个...

    spring 事务(6中配置完全降解)

    本篇文章将详细解析Spring中的六种事务配置方法,帮助开发者深入理解并掌握Spring事务的运用。 1. **基于XML的事务配置** Spring支持通过XML配置来管理事务,这是最基础的配置方式。在`spring`的配置文件中,我们...

    spring 简单实例 事务回滚

    总之,这个“spring简单实例 事务回滚”案例为我们提供了一个学习Spring事务管理的好起点。通过理解如何配置事务管理器,使用`@Transactional`注解,以及异常处理机制,我们可以更好地掌握Spring如何保证数据的一致...

Global site tag (gtag.js) - Google Analytics