How to flush data into db inside active spring transaction?

主宰稳场 提交于 2019-11-28 07:38:17
Volodymyr Levytskyi

Finally I stuck to the following solution:

First, my @Test methods are not running within spring @Transactional support. See this article to know how dangerous it may be. Next, instead of using @Repository beans inside @Test methods I autowire @Service beans which use @Transactional annotation. The miracle is that @Test method like this

@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testSave() {
    Answer created = createAnswer();
    Long generatedId = answerService.save(created);
//at this moment answer is already in db
    Answer actual=getAnswerById(generatedId);
... }

puts my Answer object into database (just after answerService.save(created);) and method getAnswerById goes to DB and extracts it to check if save was correct.
To eliminate changes made to database in @Test method I recreate database by JdbcTestUtils.executeSqlScript

  1. Have a look here with warning about @Transactional tests (Spring Pitfalls: Transactional tests considered harmful). I've used @org.springframework.test.context.jdbc.Sql to re-populate DB in my service tests and @Transactional for controllers.
  2. ConstraintViolationException for controller update test with invalid data have been thrown only when transaction is committed. So I've found 3 options:
    • 2.1 Annotate test with @Commit or with @Transactional(propagation = Propagation.NEVER). Be aware of DB change.
    • 2.2 Use TestTransaction

Code:

     TestTransaction.flagForCommit();
     TestTransaction.end();
  • 2.3 Use TransactionTemplate

Code:

    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    @Test(expected = Exception.class)
    public void testUpdate() throws Exception {
        TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        String json = ...
        transactionTemplate.execute(ts -> {
            try {
                mockMvc.perform(put(REST_URL + USER_ID)
                    .contentType(MediaType.APPLICATION_JSON)
                    .content(json))
                    .andExpect(status().isOk());
                ...
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        });

If flush is not working then it very much depend on your database isolation level.

Isolation is one of the ACID properties of database, that defines how/when the changes made by one operation become visible to other concurrent operations.

I believe your isolation level is set to Read Committed or Repeatable Read.

You should also take care of the importedpackage : in my case I imported

import javax.transaction.Transactional;

instead of

import org.springframework.transaction.annotation.Transactional;

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!