Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/345.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JUnit使用@Async方法回滚事务_Java_Junit_Spring Transactions_Spring Test_Spring Async - Fatal编程技术网

Java JUnit使用@Async方法回滚事务

Java JUnit使用@Async方法回滚事务,java,junit,spring-transactions,spring-test,spring-async,Java,Junit,Spring Transactions,Spring Test,Spring Async,我正在使用SpringJUnit4ClassRunner编写一个集成测试。 我有一个基本类: @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration({ /*my XML files here*/}) @Ignore public class BaseIntegrationWebappTestRunner { @Autowired protected WebApplicationCo

我正在使用
SpringJUnit4ClassRunner
编写一个集成测试。 我有一个基本类:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({ /*my XML files here*/}) 
@Ignore
public class BaseIntegrationWebappTestRunner {

@Autowired
protected WebApplicationContext wac; 

@Autowired
protected MockServletContext servletContext; 

@Autowired
protected MockHttpSession session;

@Autowired
protected MockHttpServletRequest request;

@Autowired
protected MockHttpServletResponse response;

@Autowired
protected ServletWebRequest webRequest;

@Autowired
private ResponseTypeFilter responseTypeFilter;

protected MockMvc mockMvc;

@BeforeClass
public static void setUpBeforeClass() {

}

@AfterClass
public static void tearDownAfterClass() {

}

@Before
public void setUp() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilter(responseTypeFilter).build();
}

@After
public void tearDown() {
    this.mockMvc = null;
}
}
然后我扩展它并使用mockMvc创建一个测试:

public class MyTestIT extends BaseMCTIntegrationWebappTestRunner {

@Test
@Transactional("jpaTransactionManager")
public void test() throws Exception {
    MvcResult result = mockMvc
            .perform(
                    post("/myUrl")
                            .contentType(MediaType.APPLICATION_XML)
                            .characterEncoding("UTF-8")
                            .content("content")
                            .headers(getHeaders())
            ).andExpect(status().isOk())
            .andExpect(content().contentType(MediaType.APPLICATION_XML))
            .andExpect(content().encoding("ISO-8859-1"))
            .andExpect(xpath("/*[local-name() ='myXPath']/")
                    .string("result"))
            .andReturn();
}
在流的末尾,一个实体被保存到数据库中。但是这里的要求是应该异步完成。所以认为这种方法叫做:< /P>
@Component
public class AsyncWriter {

    @Autowired
    private HistoryWriter historyWriter;

    @Async
    public void saveHistoryAsync(final Context context) {
        History history = historyWriter.saveHistory(context);
    }
}
@Component
public class HistoryWriter {

    @Autowired
    private HistoryRepository historyRepository;

    @Transactional("jpaTransactionManager")
    public History saveHistory(final Context context) {
        History history = null;
        if (context != null) {
            try {
                history = historyRepository.saveAndFlush(getHistoryFromContext(context));
            } catch (Throwable e) {
                LOGGER.error(String.format("Cannot save history for context: [%s] ", context), e);
            }
        }
        return history;
    }
}
然后调用
HistoryWriter

@Component
public class AsyncWriter {

    @Autowired
    private HistoryWriter historyWriter;

    @Async
    public void saveHistoryAsync(final Context context) {
        History history = historyWriter.saveHistory(context);
    }
}
@Component
public class HistoryWriter {

    @Autowired
    private HistoryRepository historyRepository;

    @Transactional("jpaTransactionManager")
    public History saveHistory(final Context context) {
        History history = null;
        if (context != null) {
            try {
                history = historyRepository.saveAndFlush(getHistoryFromContext(context));
            } catch (Throwable e) {
                LOGGER.error(String.format("Cannot save history for context: [%s] ", context), e);
            }
        }
        return history;
    }
}
所有这些的问题是,测试完成后,
History
对象留在数据库中。我需要进行测试事务以最终回滚所有更改

现在,我已经试过了:

  • 删除
    @Async
    注释。显然,这不是解决方案,但这样做是为了确认在没有它的情况下将执行回滚。确实如此
  • @Async
    注释移动到
    HistoryWriter.saveHistory()
    方法,使其与
    @Transactional
    放在一个位置。本文建议它应该以这种方式工作,但对我来说,测试后不会进行回滚
  • 交换这两个注释的位置。它也不能给出期望的结果
  • 有人知道如何强制回滚异步方法中的DB更改吗?

    旁注:

    事务配置:

    异步配置:

    
    

    有人知道如何强制回滚在异步方法中所做的DB更改吗

    不幸的是,这是不可能的

    Spring通过
    ThreadLocal
    变量管理事务状态。因此,在另一个线程中启动的事务(例如,为
    @Async
    方法调用创建的事务)不能参与为父线程管理的事务

    这意味着
    @Async
    方法使用的事务与Spring TestContext框架自动回滚的事务相同

    因此,解决问题的唯一可能方法是手动撤消对数据库的更改。您可以使用
    JdbcTestUtils
    @postertransaction
    方法中以编程方式执行SQL脚本,也可以通过Spring的
    @SQL
    注释(使用后执行阶段)配置SQL脚本以声明方式执行。有关后者的详细信息,请参阅

    问候,


    Sam(SpringTestContext框架的作者)

    非常感谢您的回复。由于我无法将其存档,现在我知道这是不可能的,因此我最终创建了单独的spring配置xml进行测试,其中删除了
    ,因此@Async在测试期间无法工作,因此事务将被回滚。我现在还有一个挑战,你能看一下吗?所以除了SpringJUnit4ClassRunner之外,我还需要将PowerMock添加到我的测试用例中。为此,我添加了
    @RunWith(PowerMockRunner.class)@powermockrunnerregate(SpringJUnit4ClassRunner.class)
    ,现在我还想利用参数化的runner。我检查过了,似乎我可以使用Spring规则使其与参数化的应用程序一起工作。但它也能与PowerMock一起工作吗?我试图简单地将PowerMock委托参数化并添加Spring规则,但没有成功。可以吗?我从来没有尝试过这种组合,所以我不知道它是否有效。