使用@Transactional:即使抛出异常,也会提交事务

时间:2013-03-14 08:29:11

标签: spring struts2 mybatis

我正在使用struts2 - spring 3.2.2和mybatis。

首先,我的要求是:

制作一个交易管理实用程序,其中

  1. 如果在当前事务的其他语句中抛出任何异常,则必须回滚事务。 (其他数据库操作可以通过各种类来执行,即不在同一类中。)
  2. 按照要求,我创建了简单的程序。 我的applicationContext.xml文件:


    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:aop="http://www.springframework.org/schema/aop"
         xmlns:tx="http://www.springframework.org/schema/tx"
         xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <tx:annotation-driven transaction-manager="transactionManager"/>
            <!-- Initialization for data source -->
       <bean id="dataSource" 
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
          <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
          <property name="url" value="jdbc:sqlserver://localhost;database=master;integratedSecurity=true;"/>
          <property name="username" value="Jaydeep"/>
          <property name="password" value="Acty#System123"/>
       </bean>
    
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
    
            <property name='mapperLocations' value='classpath*:test/xml/*.xml' />
        </bean>
    
        <bean class='org.mybatis.spring.mapper.MapperScannerConfigurer'>
          <property name='basePackage' value='test.dao' />
        </bean>
    
        <bean id='sqlSession' class='org.mybatis.spring.SqlSessionTemplate'>
          <constructor-arg index='0' ref='sqlSessionFactory' />
        </bean>
    
       <bean id="transactionManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
        </bean>
    
       <bean id="serviceProvider"
          class="DataServiceProvider">
          <property name="sqlSession"  ref="sqlSession" />    
       </bean>
    
        <bean id="updutil" class="MyUpdateUtil">
            <property name="serviceProvider" ref="serviceProvider"></property>
        </bean>
    
    </beans>
    

    // DataServiceProvider.java

    import org.apache.ibatis.session.SqlSession;
    import test.dao.DepartmentMapper;
    import test.dao.EmployeeMapper;
    
    public class DataServiceProvider {
        private SqlSession sqlSession;
        public DepartmentMapper getDeptMapper() {
            if(sqlSession != null)
                return sqlSession.getMapper(DepartmentMapper.class);
            else
                System.out.println("session null");
            return null;
        }
        public EmployeeMapper getEmpMapper() {
            if(sqlSession != null)
                return sqlSession.getMapper(EmployeeMapper.class);
            else
                System.out.println("session null");
            return null;
        }
    
        public SqlSession getSqlSession() {
            return sqlSession;
        }
    
        public void setSqlSession(SqlSession sqlSession) {
            this.sqlSession = sqlSession;
        }
    }
    

    //接口:fooService.java

    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import org.springframework.transaction.annotation.Transactional;
    import test.model.Department;
    
    @Transactional
    public interface fooService {
    
        public void update(boolean isThrow) throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException;
    
        public void insert(Department dept) throws IOException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException;
    
        public void select() throws IOException;
    }
    

    //将在其中执行所有与数据库相关的操作的实用程序类 //MyUpdateUtil.java

    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.math.BigDecimal;
    import java.util.List;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.transaction.annotation.Transactional;
    import test.model.Department;
    import test.model.Employee;
    import test.model.EmployeeExample;
    
    @Transactional
    public class MyUpdateUtil implements fooService {
    
        public void update(boolean isThrow) throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException  {
            System.out.println("Updating...................Transaction alive? ..." + StartTransAction.isActive());
            if(isThrow)
                throw new RuntimeException("simulate Error condition") ;
            Employee record = new Employee();
            EmployeeExample example = new EmployeeExample();
            example.createCriteria().andDeptidEqualTo(1L);
    
            record.setEmpid(1L);
            record.setDeptid(1L);
            record.setEmpname("jaydeep");
            record.setSalary(BigDecimal.valueOf(2000));
    
            getServiceProvider().getEmpMapper().updateByExampleWithBLOBs(record, example);
        }
        @Transactional
        public void insert(Department dept) throws IOException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
            System.out.println("Inserting....................Transaction alive? .." + StartTransAction.isActive());
            getServiceProvider().getDeptMapper().insert(dept);
        }
    
        public void select() throws IOException {
            System.out.println("Dept Info");
            List<Department> deptList = getServiceProvider().getDeptMapper().selectByExampleWithBLOBs(null);
            for(Department d : deptList) {
                System.out.println("Dept ID: " + d.getDeptid());
                System.out.println("Dept Name: " + d.getDeptname());
            }
            System.out.println("Emp Info");
            List<Employee> empList = getServiceProvider().getEmpMapper().selectByExampleWithBLOBs(null);
            for(Employee e : empList) {
                System.out.println("Emp ID: " + e.getEmpid());
                System.out.println("Dept ID: " + e.getDeptid());
                System.out.println("Emp Name: " + e.getEmpname());
            }
        }   
        @Autowired(required=true)
        private DataServiceProvider serviceProvider;
        public DataServiceProvider getServiceProvider() {
            return serviceProvider;
        }
    
        public void setServiceProvider(DataServiceProvider serviceProvider) {
            this.serviceProvider = serviceProvider;
        }
    }
    

    //以及从jsp页面点击链接时执行的主要操作.... //StartTransAction.java

    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.util.Random;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import test.model.Department;
    
    import com.opensymphony.xwork2.ActionSupport;
    
    
    public class StartTransAction extends ActionSupport {
        private static final long serialVersionUID = 1L;
    
        public String execute() throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IOException {      
            Department dept = new Department();
            dept.setDeptid(Long.valueOf(String.valueOf(new Random().nextInt(500))));
            dept.setDeptname("esb");
            System.out.println("before Insert..................Transaction alive? ...." + isActive());
            try {
                updutil.insert(dept);
    
                System.out.println("After Insert..................Transaction alive? ...." + isActive());
                updutil.select();
                updutil.update(true);
            } catch (Exception e) {
                System.out.println(e.toString());
            }finally{
                System.out.println("After Update.................Transaction alive? ....." + isActive());
                updutil.select();
            }
            return SUCCESS;
        }
        @Autowired
        fooService updutil;
    
        public fooService getUpdutil() {
            return new MyUpdateUtil();
        }
        public void setUpdutil(fooService updutil) {
            this.updutil = updutil;
        }
        private DataSourceTransactionManager transactionManager;
    
        public DataSourceTransactionManager getTransactionManager() {
            return transactionManager;
        }
        public void setTransactionManager(
                DataSourceTransactionManager transactionManager) {
            this.transactionManager = transactionManager;
        }   
    
        public static boolean isActive() throws IOException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException  {
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
             Class tsmClass = contextClassLoader.loadClass("org.springframework.transaction.support.TransactionSynchronizationManager");
             Boolean isActive = (Boolean) tsmClass.getMethod("isActualTransactionActive", null).invoke(null, null);
             return isActive;
        }
    }
    

    现在,当我运行程序时......输出是这样的:

    插入之前

    .................交易活着? .....假 插入..................交易活着? ....真
    插入................交易活着? ......假
    部门信息
    部门ID:1
    部门名称:si
    部门编号:251
    部门名称:esb
    部门ID:293
    部门名称:esb

    Emp Info
    Emp ID:1
    部门ID:1
    Emp名称:s
    更新.................交易活着? .....真
    java.lang.RuntimeException:模拟错误情况
    更新后.................交易活着? .....假
    部门信息
    部门ID:1
    部门名称:si
    部门编号:251
    部门名称:esb
    部门ID:293
    部门名称:esb

    Emp Info
    Emp ID:1
    部门ID:1
    Emp名称:s

    <小时/> 更大胆的部分是插入的新记录。

    抛出运行时异常后,我需要回滚插入的记录。但是如输出所示,即使抛出异常,记录也会被提交。 我们可以看到,事务也在update()方法中继续。

    请帮助我实现这一目标。我尝试了很多,但没有工作。 如果可能的话,请给我上述问题的工作代码......


    编辑: 我完全替换了上面的代码。

    现在我有以下文件:

    // applicationContext.xml中:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:aop="http://www.springframework.org/schema/aop"
         xmlns:tx="http://www.springframework.org/schema/tx"
         xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <tx:annotation-driven transaction-manager="transactionManager"/>
           <!-- bean id="serviceProvider" class="DataServiceProvider"></bean-->
            <!-- Initialization for data source -->
       <bean id="dataSource" 
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
          <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
          <property name="url" value="jdbc:sqlserver://localhost;database=master;integratedSecurity=true;"/>
          <property name="username" value="Jaydeep"/>
          <property name="password" value="Acty#System123"/>
    
       </bean>
    
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name='mapperLocations' value='classpath*:test/xml/*.xml' />
        </bean>
    
        <bean class='org.mybatis.spring.mapper.MapperScannerConfigurer'>
          <property name='basePackage' value='test.dao' />
          <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
        </bean>
    
    
        <bean id='sqlSession' class='org.mybatis.spring.SqlSessionTemplate'>
          <constructor-arg index='0' ref='sqlSessionFactory' />
        </bean>
    
       <bean id="transactionManager"
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
            <property name="nestedTransactionAllowed" value="true" />
            <property name="validateExistingTransaction" value="true" />
        </bean>
    
        <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
            <constructor-arg index='0' ref='sqlSessionFactory' />
        </bean>
    
        <bean id="myService" class="service.MyService">
            <property name="sqlSessionTemplate" ref="sqlSessionTemplate" />
        </bean>
    </beans>
    

    // StartTransAction.java

    package com.acty;
    import java.lang.reflect.InvocationTargetException;
    import service.MyService;
    
    import com.opensymphony.xwork2.ActionSupport;
    
    public class StartTransAction extends ActionSupport {
        private static final long serialVersionUID = 1L;
    
        public String execute(){
            myService.startOperations();
            return SUCCESS;
        }
    
        public static boolean isActive() {
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
             Class tsmClass = null;
            try {
                tsmClass = contextClassLoader.loadClass("org.springframework.transaction.support.TransactionSynchronizationManager");
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
             Boolean isActive = null;
            try {
                isActive = (Boolean) tsmClass.getMethod("isActualTransactionActive", null).invoke(null, null);
            } catch (IllegalAccessException | IllegalArgumentException
                    | InvocationTargetException | NoSuchMethodException
                    | SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
             return isActive;
        }
    
        private MyService myService;
    
        public void setMyService(MyService myService) {
            this.myService = myService;
        }
    }
    

    和//MyService.java

    package service;
    
    import java.util.List;
    import java.util.Scanner;
    
    import org.mybatis.spring.SqlSessionTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import test.dao.DepartmentMapper;
    import test.dao.EmployeeMapper;
    import test.model.Department;
    import test.model.Employee;
    import test.model.EmployeeExample;
    
    @Service
    @EnableTransactionManagement
    @Transactional(propagation=Propagation.REQUIRED)
    public class MyService {
        @Autowired
        private SqlSessionTemplate sqlSessionTemplate;
    
        public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
            this.sqlSessionTemplate = sqlSessionTemplate;
        }
        @Transactional(propagation=Propagation.REQUIRED)
        public void startOperations() {
            DepartmentMapper deptMapper = sqlSessionTemplate.getMapper(DepartmentMapper.class);
            EmployeeMapper empMapper = sqlSessionTemplate.getMapper(EmployeeMapper.class);
    
            System.out.println("Before Insert Dept" + com.acty.StartTransAction.isActive());
    
            this.select(deptMapper, empMapper);
    
            Department dept = new Department();
            //insert new dept
            Scanner sc = new Scanner(System.in);
            System.out.println("Enter dept id ");
            dept.setDeptid(sc.nextLong());
            System.out.println("Enter dept Name ");
            dept.setDeptname(sc.next());
    
            deptMapper.insert(dept);
    
            System.out.println("After Insert Dept" + com.acty.StartTransAction.isActive());
    
            this.select(deptMapper, empMapper);
    
            this.select(deptMapper, empMapper);
            //now update employee
    
            EmployeeExample example = new EmployeeExample();
            example.createCriteria().andEmpidEqualTo(1L);
    
            Employee emp = new Employee();
            emp.setEmpname("jjj");
    
            try {
            //empMapper.updateByExampleSelective(emp, example);
            empMapper.updateByExampleWithBLOBs(emp, example);
            }catch(Exception e) {
                e.printStackTrace();
            }
            System.out.println("After Update Emp");
    
            this.select(deptMapper, empMapper);
        }
        public void select(DepartmentMapper deptMapper, EmployeeMapper empMapper) {
            System.out.println("\nDeptartment\n");
            List<Department> deptList= deptMapper.selectByExampleWithBLOBs(null);
            for(Department de : deptList) {
                System.out.println(" Dept Id : " + de.getDeptid());
                System.out.println(" Dept Name : " + de.getDeptname());
            }
            System.out.println("\nEmployee\n");
            List<Employee> empList= empMapper.selectByExampleWithBLOBs(null);
            for(Employee emp : empList) {
                System.out.println(" Emp Id : " + emp.getEmpid());
                System.out.println(" Emp Name : " + emp.getEmpname());
            }
        }
    }
    

    //现在我正在服务层做事。 所有DAO都在其他包装中,我是从春天注入的。

    然后诀窍也没有用。 看输出:
    在插入部门之前是真的
    Deptartment

     部门ID:1  部门名称:si
     部门ID:2
     部门名称:esb
     部门ID:3

    员工

     Emp Id:1
     Emp名称:kkkkk
    输入部门号码 4
    输入部门名称
    ASDWE
    插入部门后
    Deptartment

     部门ID:1  部门名称:si
     部门ID:2
     部门名称:esb
     部门ID:3
     部门名称:esb
     部门ID:4
     部门名称:asdwe

    - 此处发生例外 org.springframework.dao.DataIntegrityViolationException:

    更新数据库时出错。原因:com.microsoft.sqlserver.jdbc.SQLServerException:Ca

    .....

    更新后Emp Deptartment

     部门ID:1  部门名称:si
     部门ID:2
     部门名称:esb
     部门ID:3
     部门名称:esb
     部门ID:4
     部门名称:asdwe

    ... 看这里,之前插入的部门,这里没有回滚...... (我们可以在这里使用sqlSessionTemplate的回滚方法,但是弹簧自动事务管理的用途是什么?我相信这样做是没有意义的!) 问题是什么,我真的没得到......

    Plz提供了一些有效的解决方案......

1 个答案:

答案 0 :(得分:3)

第一个事务,已插入行的事务已经过了很长时间,并且在更新时抛出异常时会提交。你可能想要实现的是在StartTransAction.execute中的try / catch中作为单个事务运行整个块。

一般情况下,不建议在dao级别定义事务 - 而MyUpdateUtil看起来像是带有select / insert / update方法的dao对象。您应该管理服务层中的事务。

首先,将这些行移至MyUpdateUtil中的新方法:

@Transactional
public void insertAndUpdate(Department dept) {
  this.insert(dept);
  this.select();
  this.update(true);
}

然后从execute try / catch块调用它。这将为您提供进一步代码抛光的工作起点。