需要Spring Transaction传播,REQUIRES_NEW

时间:2013-04-03 19:13:21

标签: spring transactions propagation

在下面的代码方法doService1()中更新正确的sql但是doService2() sql有一些问题,但是当我调用doService()时,它必须将doService1()更新提交给DB,即使doService2()有一个sql exception,因为doService2()有一个REQUIRES_NEW Propagation类型,但是当我确认此doService1()更新未提交数据库时..

@Service public class DbClass {

      static Logger log = Logger.getLogger(
              DbClass.class.getName());

@Autowired
private DataSource dataSource;

@Transactional(propagation=Propagation.REQUIRED)
public void doService(){
    doService1();
    doService2();
}

@Transactional(propagation=Propagation.REQUIRED)
public void doService1(){
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String sql = "  update BATCHJOBSTATUS set PROCESSINGDATE = '20130322'  " ;
    int rowCount1 =  jdbcTemplate.update(sql);
    System.out.println(" rowCount1 >" + rowCount1);
}

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void doService2(){
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String sql = "  update aa set a_name = 'hhh' where a_id = 4 and " ;
    int rowCount1 =  jdbcTemplate.update(sql);
    System.out.println(" rowCount2 >" + rowCount1);
}   
}

正如您的家伙建议以下列方式进行测试,但仍面临同样的问题。 这里我doService2()在一个单独的班级,但即使仍然有与上面相同的问题

@Service
public class DbClass {

  static Logger log = Logger.getLogger(
          DbClass.class.getName());

@Autowired
private DataSource dataSource;

@Autowired
private DbClass2 dbClass2;

@Transactional
public void doService(){
    doService1();
    dbClass2.doService2();
}

@Transactional(propagation=Propagation.REQUIRED )
public void doService1(){
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String sql = "  update BATCHJOBSTATUS set PROCESSINGDATE = '20130322'  " ;
    int rowCount1 =  jdbcTemplate.update(sql);
    System.out.println(" rowCount1 >" + rowCount1);

    }


}


@Service
public class DbClass2 {


@Autowired
private DataSource dataSource;

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void doService2() {
    System.out.println("*******doService2*********`");

    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

    String sql = "  update aa set a_name = 'hhh' where a_id_ = 4  " ;

    int rowCount2 =  jdbcTemplate.update(sql);

    System.out.println(" rowCount2 >" + rowCount2);

}

}



<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:oxm="http://www.springframework.org/schema/oxm"
         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-3.0.xsd
             http://www.springframework.org/schema/tx 
     http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
     http://www.springframework.org/schema/aop 
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd">


    <context:annotation-config />

    <context:component-scan base-package="com.spring"/>

      <tx:annotation-driven transaction-manager="txManager1" proxy-target-class="true"/>



    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
        <property name="url" value="jdbc:oracle:thin:@192.168.8.121:1521:h3" />
        <property name="username" value="admin" />
        <property name="password" value="admin" />
    </bean>


  <bean id="txManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
  <property name="dataSource" ref="dataSource"/>
  </bean>

    <bean id="batchJob" class="com.spring.jdbc.BatchJob">
    </bean>


</beans>

3 个答案:

答案 0 :(得分:5)

我之前遇到了同样的问题,并在此处解决了:Strange behaviour with @Transactional(propagation=Propagation.REQUIRES_NEW)

使用默认设置,当您从同一个类调用doService2()时,不会创建任何新的事务代理,因此您的注释不是用户。

要避免此问题,您可以将doService2()放入另一个类或使用aspectJ进行事务处理,方法如下:<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/>

最佳解决方案取决于您的应用程序。 (这里的第二个似乎更合适)

答案 1 :(得分:4)

doService2()的调用可能没有运行任何事务通知,因为我假设您正在使用JDK动态代理(接口代理)而不是基于CGLIB的代理。如果您还不知道这是如何工作的,您可能需要阅读:http://static.springsource.org/spring/docs/3.0.x/reference/aop.html#aop-proxying

如果你没有使用CGLIB(目标类代理),当你调用doService2()时它永远不会通过Spring Transaction顾问程序,因为它直接调用方法而不是通过Spring创建的包装器启动时的服务。

您可以将doService2()移动到其他服务类,然后将其注入此服务,从而使您的示例正常工作。这样你就可以通过代理,交易建议将会运行。

否则,如果您准备对项目进行更大的更改,您可以让您的示例按原样运行: 1)确保CGLIB在你的类路径中,并且 2)打开proxy-target-class,或强制它使用CGLIB代理,以便摆脱服务界面。

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

如果您要这样做,请确保通过第一个链接阅读:)。

答案 2 :(得分:0)

根据spring documentation(检查第10.5.6.1节),spring框架事务将仅回滚 RunTimeException
不适用于SqlException等其他已检查的摘要。 因此,如果您真的想要对此异常进行回滚,则必须如下所示指定

@Transactional(propagation=Propagation.REQUIRES_NEW,rollbackFor=SQLException.class)
public void doService2(){
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    String sql = "  update aa set a_name = 'hhh' where a_id = 4 and " ;
    int rowCount1 =  jdbcTemplate.update(sql);
    System.out.println(" rowCount2 >" + rowCount1);
}

试试这个,让我知道它是否有效。
另外this可能会有所帮助。