【问题标题】:Rollback transaction in functional tests started in another spring context功能测试中的回滚事务在另一个 Spring 上下文中开始
【发布时间】:2015-01-16 03:06:49
【问题描述】:

正在实施功能测试。为了避免数据损坏,我需要在执行后回滚每个测试。这是一项非常简单的任务 - 只需在测试中标记 rollback=true。但是,如果我用另一个 spring 上下文启动另一个模块并且第一个模块以某种方式与之交互(例如发送 jms 消息,第二个将它保存到同一个数据库),那么回滚不适用于第二个上下文。如何也回滚第二个模块?

    @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/my-service-context.xml")
@Transactional(value = "myTransactionManager")
public class ParserServiceTest {

    protected Logger l = LoggerFactory.getLogger(getClass());

    @Autowired
    @Qualifier(value = "earMessageDaoBean")
    EARMessageDao dao;

    @Autowired
    @Qualifier(value = "myParserService")
    ParserService service;

    @Test
    @Rollback(value = true)
    public void testExecute() throws Exception {
        service.execute("fff", "ttt");
        EARMessage byId = dao.findById(1L);
        assertNotNull(byId);
        assertEquals("fff", byId.getFrom());
        assertEquals("ttt", byId.getTo());
        l.info("{}", byId);
    }
}

如果我查看数据库,我将看不到任何数据,这很好

但如果我要添加另一个模块

    @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/my-service-context.xml")
@Transactional(value = "myTransactionManager")
public class ParserServiceTest {

    protected Logger l = LoggerFactory.getLogger(getClass());

    ClassPathXmlApplicationContext context2;

    @Before
    public void setUp() throws Exception {
        context2 = new ClassPathXmlApplicationContext(
                "my-service-context2.xml"
        );
    }

    @Autowired
    @Qualifier(value = "earMessageDaoBean")
    EARMessageDao dao;

    @Autowired
    @Qualifier(value = "myParserService")
    ParserService service;

    @Test
    @Rollback(value = true)
    public void testExecute() throws Exception {
        service.execute("fff", "ttt");
        EARMessage byId = dao.findById(1L);
        assertNotNull(byId);
        assertEquals("fff", byId.getFrom());
        assertEquals("ttt", byId.getTo());
        l.info("{}", byId);
// theoretically there could be interaction with service2 via JMS
        MyParserService2 service2 = (MyParserService2) context2.getBean("myParserService2");
        service2.execute("FFF", "TTT");
    }
}

servie2 添加的数据不会被回滚。

我可以在测试上下文中获取 EntityManager TransactionManager,但我无法回滚它们,因为事务已经提交。 我可以在第二个模块中添加一些标记并以某种方式在测试中标记它,但现在知道该怎么做

更新 以下是服务和上下文配置,以便更好地理解:

    @Service(value = "myParserService")
@Transactional(value = "myTransactionManager")
public class ParserService {

    protected Logger l = LoggerFactory.getLogger(getClass());

    @Autowired
    @Qualifier(value = "earMessageDaoBean")
    EARMessageDao dao;

    public void execute(String from, String to) {
        l.info("-------started service 1---------");
        EARMessage message = new EARMessage();
        message.setFrom(from);
        message.setTo(to);
        message.setProcessingDate(new DateTime());
        dao.persist(message);
        l.info("-------ended  service 1---------");
    }
}


    @Service(value = "myParserService2")
@Transactional(value = "myTransactionManager")
public class MyParserService2 {

    protected Logger l = LoggerFactory.getLogger(getClass());

    @Autowired
    @Qualifier(value = "earMessageDaoBean")
    EARMessageDao dao;

    public void execute(String from, String to) {
        l.info("-------started service 2---------");
        EARMessage message = new EARMessage();
        message.setFrom("666" + from);
        message.setTo("666" + to);
        message.setProcessingDate(new DateTime());
        dao.persist(message);
        l.info("-------ended  service 2---------");
    }
}

我的服务上下文.xml

   <context:annotation-config/>
<context:component-scan base-package="com.dimas.tutorial.hibernate.simple"/>

<import resource="classpath:/my-service-config.xml"/>
<import resource="classpath:/my-data-source.xml"/>
<import resource="classpath:/my-entity-manager.xml"/>

<jdbc:initialize-database data-source="${dataSource.name}">
    <jdbc:script location="classpath:/sql/my-schema.sql"/>
</jdbc:initialize-database>

我的服务上下文2.xml

   <context:annotation-config/>
    <context:component-scan base-package="com.dimas.tutorial.hibernate.simple"/>

    <import resource="classpath:/my-service-config.xml"/>
    <import resource="classpath:/my-data-source.xml"/>
    <import resource="classpath:/my-entity-manager.xml"/>

    <jdbc:initialize-database data-source="${dataSource.name}">
        <jdbc:script location="classpath:/sql/my-schema.sql"/>
    </jdbc:initialize-database>

UPDATE2:添加了实体管理器配置

<bean id="valettaEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="MyPersistence"/>
        <property name="packagesToScan" value="com.dimas.tutorial.hibernate.simple.domain"/>
        <property name="dataSource" ref="${dataSource.name}"/>
        <property name="jpaVendorAdapter" ref="hibernateVendor"/>
        <property name="jpaPropertyMap" ref="jpaPropertyMap"/>
    </bean>

    <util:map id="jpaPropertyMap">
        <entry key="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
        <entry key="hibernate.default_schema" value="public"/>
        <entry key="hibernate.show_sql" value="false"/>
        <entry key="hibernate.format_sql" value="false"/>
        <entry key="hibernate.cache.use_second_level_cache" value="false"/>
        <entry key="hibernate.max_fetch_depth" value="3"/>
        <entry key="hibernate.jdbc.fetch_size" value="50"/>
        <entry key="hibernate.jdbc.batch_size" value="10"/>
    </util:map>

    <bean id="hibernateVendor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="showSql" value="false"/>
    </bean>

    <bean id="myTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="valettaEntityManagerFactory"/>
    </bean>

    <bean id="valettaTransactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="myTransactionManager"/>
    </bean>

    <context:annotation-config/>
    <tx:annotation-driven transaction-manager="myTransactionManager"/>

【问题讨论】:

    标签: java spring hibernate transactions rollback


    【解决方案1】:

    使用您的服务所需的传播。
    例如。
    第一笔交易:

    @Transactional(value = "myTransactionManager")
      public class ParserServiceTest {
    

    通过要求交易加入:

     @Transactional(value = "myTransactionManager", propagation=Propagation.REQUIRED)
        public class ParserService {
    

    通过要求交易加入:

    @Transactional(value = "myTransactionManager", propagation=Propagation.REQUIRED)
    public class MyParserService2 {
    

    PROPAGATION_REQUIRED: Spring REQUIRED 行为意味着如果当前 bean 方法执行上下文中有一个已经打开的事务,则将使用相同的事务。如果不存在,则创建一个新的。 简而言之,这意味着如果 inner(2nd Transaction) 方法导致事务回滚,则 outer(1st Transaction) 方法将无法提交并且也会回滚事务.

    【讨论】:

    • 嗯,不,不工作,仍然有来自第二个服务的更新
    • hmm 出现奇怪的异常 org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect 不支持保存点 - 检查您的 JPA 提供者的能力
    • 对不起。如果它没有在方言中实现,它不可用,也可能意味着它在 hibernate 中不受支持。我的答案已编辑。
    • 不,一样。恐怕这不是使用弹簧可以解决的问题。可能我需要使用 jdbc 层的保存点
    猜你喜欢
    • 2015-06-19
    • 2018-03-25
    • 1970-01-01
    • 1970-01-01
    • 2012-12-26
    • 2016-09-07
    • 2011-10-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多