spring junit transaction rollback multi datasource

有些测试用例会修改数据库。如果配置了自动回滚 ,测试用例就可以多次运行。加入此库还有服务在运行,那么也不会影响服务的运行。

之前一直采用注解TransactionConfiguration ,AbstractTransactionalJUnit4SpringContextTests以及注解Transactional实现。

但是发现TransactionConfiguration 已经废弃了,所以采用了新的方式配置。

 

通过TestExecutionListeners定义事物的listener,通过listener实现功能。

首先定义基类:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(classes = { TestConfig.class, DatasourceConfig.class })

@TestPropertySource({ "classpath:application.properties" })

@TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class,

        TransactionalTestExecutionListener.class })

public class BasicSpringTest {

}

由于项目需求,定义了多个DataSource,transanction manager。每组定义写在不同的config中,并通过ContextConfiguration加入spring。

测试用的链接属性信息都存放在properties文件中,通过TestPropertySource将属性加载。

DependencyInjectionTestExecutionListener 对于test class中的autowired进行注入。

TransactionalTestExecutionListener 开启事物。

 

然后定义测试类:

public class MonitorServiceTest extends BasicSpringTest {



    @Autowired

    private MonitorService monitorService;



    /**

     *

     * @author yunzhong

     * @time 2017年6月21日上午10:44:27

     */

    @Test

    @Transactional

    @Rollback(true)

    public void testInsert() {

     }

}

Rollback定义是否回滚。如果参数为true,则不会滚。还有注解@Commit ,等同于Rollback(true).

 

定义config
 

@Configuration

@MapperScan(basePackages = {

        "cn.das.hive.dao" }, sqlSessionFactoryRef = HiveDatasourceConfig.SQL_SESSION_FACTORY_NAME)

public class HiveDatasourceConfig {

    public static final String SQL_SESSION_FACTORY_NAME = "sessionFactoryHive";

    public static final String TX_MANAGER = "txManagerHive";

    public static final String DATASOURCE_BEAN_NAME = "datasourceHive";

    @Bean(name = DATASOURCE_BEAN_NAME)

    @Primary

    @ConfigurationProperties(prefix = "datasource.hive")

    public DataSource dataSource() {

        DruidDataSource dataSource = new DruidDataSource();

        return dataSource;

    }

    @Bean(name = SQL_SESSION_FACTORY_NAME)

    @Primary

    public SqlSessionFactory sqlSessionFactory() throws Exception {

        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();

        sessionFactoryBean.setDataSource(dataSource());

        return sessionFactoryBean.getObject();

    }

    @Bean(name = TX_MANAGER)

    @Primary

    public DataSourceTransactionManager transactionManager() {

        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();

        transactionManager.setDataSource(dataSource());

        return transactionManager;

    }

}



@Configuration

@ComponentScan(basePackages = "cn", excludeFilters = {})

@MapperScan(basePackages = { "cn.core.*.repository", "cn.das.dao" }, sqlSessionFactoryRef = TestConfig.SQL_SESSION_FACTORY_NAME)

@EnableConfigurationProperties

@ImportResource("classpath:hadoop-conf.xml")

public class TestConfig {

       public static final String SQL_SESSION_FACTORY_NAME = "sessionFactoryNew";

       public static final String TX_MANAGER = "transactionManager";

       private Interceptor pageHelper;

       @Value("${zookeeper.address}")

       private String address;


       @Bean

       public static PropertySourcesPlaceholderConfigurer propertiesResolver() {

           return new PropertySourcesPlaceholderConfigurer();

       }

       @Bean(name = "datasourceChorus")

       @Primary

       @ConfigurationProperties(prefix = "datasource")

       @DependsOn("propertiesResolver")

       public DataSource dataSource() {

             DruidDataSource dataSource = new DruidDataSource();

             return dataSource;

       }

       @Bean(name = SQL_SESSION_FACTORY_NAME)

       @Primary

       @DependsOn(value = {"propertiesResolver"})

       public SqlSessionFactory sqlSessionFactory() throws Exception {

             SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();

             sessionFactoryBean.setDataSource(dataSource());

             sessionFactoryBean.setPlugins(new Interceptor[] { pageHelper });

             return sessionFactoryBean.getObject();

       }

       @Bean(name = TX_MANAGER)

       @Primary

       @DependsOn("propertiesResolver")

       public DataSourceTransactionManager transactionManager() {

             DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();

             transactionManager.setDataSource(dataSource());

             return transactionManager;

       }

}

 

有两个DataSource和transanctionmanager,那么listener怎知道使用哪一个呢?这个问题是在使用过程中产生的。

开始的时候transanction并不生效,每次数据库都会变动。查看源码发现,是定位不到使用哪个manager。

这个问题可以通过两种方式解决:

一是采用transanction manager 默认bean的id,另一种方式是transanctional注解设置tm的id。

第一种方式:

首先看TransactionalTestExecutionListener在测试开始之前的准备工作:

public void beforeTestMethod(final TestContext testContext) throws Exception {

             final Method testMethod = testContext.getTestMethod();

             final Class<?> testClass = testContext.getTestClass();

             Assert.notNull(testMethod, "The test method of the supplied TestContext must not be null");



             TransactionContext txContext = TransactionContextHolder.removeCurrentTransactionContext();

             if (txContext != null) {

                    throw new IllegalStateException("Cannot start a new transaction without ending the existing transaction.");

             }



             PlatformTransactionManager tm = null;

             TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod, testClass);



             if (transactionAttribute != null) {

                    transactionAttribute = TestContextTransactionUtils.createDelegatingTransactionAttribute(testContext,

                           transactionAttribute);



                    if (logger.isDebugEnabled()) {

                           logger.debug("Explicit transaction definition [" + transactionAttribute + "] found for test context "

                                        + testContext);

                    }



                    if (transactionAttribute.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {

                           return;

                    }



                    tm = getTransactionManager(testContext, transactionAttribute.getQualifier()); //从beanfactory中获取tm。

             }



             if (tm != null) { //如果找到tm就开启事物管理,否则就啥也不做了。

                    txContext = new TransactionContext(testContext, tm, transactionAttribute, isRollback(testContext));

                    runBeforeTransactionMethods(testContext);

                    txContext.startTransaction();

                     TransactionContextHolder.setCurrentTransactionContext(txContext);

             }

       }

主要是因为getTransactionManager这个方法返回null,所以事物并没有开启。看下getTransactionManager的源码。

protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) {

             // look up by type and qualifier from @Transactional
             if (StringUtils.hasText(qualifier)) {

                           // Use autowire-capable factory in order to support extended qualifier

                           // matching (only exposed on the internal BeanFactory, not on the

                           // ApplicationContext).

                           BeanFactory bf = testContext.getApplicationContext().getAutowireCapableBeanFactory();

                           return BeanFactoryAnnotationUtils.qualifiedBeanOfType(bf, PlatformTransactionManager.class, qualifier);

             }
             // else
             return getTransactionManager(testContext);
       }

TestContextTransactionUtils

public static final String DEFAULT_TRANSACTION_MANAGER_NAME = "transactionManager";

public static PlatformTransactionManager retrieveTransactionManager(TestContext testContext, String name) {
                    ......
                    return bf.getBean(DEFAULT_TRANSACTION_MANAGER_NAME, PlatformTransactionManager.class);

       }

在getTransactionManager的内部调用了TestContextTransactionUtils.retrieveTransactionManager 获得manager。如果没有定义beanname,则采用默认的名字:transactionManager

所以,只要将manager的名字改成transactionManager就可以了。

 

第二种方式:

在TransactionalTestExecutionListener的源码中,可以看到一行:

TransactionAttribute transactionAttribute = this.attributeSource.getTransactionAttribute(testMethod, testClass);

之后transanction manager的bean id也是从attribute取得。bean id 是通过@Transanctional注解传递的。所以只要给注解添加manager属性,就可以指定manager了。

转载于:https://my.oschina.net/u/347227/blog/995292

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值