如何在活跃的spring事务中将数据刷新到db?

时间:2013-06-23 17:40:30

标签: java hibernate spring-transactions spring-test hibernate-session

我想使用spring测试框架测试hibernate session的save()方法。 @Test方法是:

@Test
@Transactional
public void testSave() {
    User expected = createUser();
    getGenericDao().currentSession().save(expected);
    User actual = getUser(generatedId);
    assertUsersEqual(expected,actual);
}

我想将用户刷新到数据库中。我想让我的用户在这个方法之后进入数据库

getGenericDao().currentSession().save(expected);

然后我想使用spring数据框架进入数据库,并通过下一行获取此保存的用户:

User actual = getUser(generatedId);

我尝试使用hibernate flush方法,如:

currentSession().setFlushMode(MANUAL);
//do saving here
currentSession().flush();

它不会将我的用户刷新到数据库中! 但是,如果我不使用@Transactional spring注释并将我的用户保存在编程弹簧事务中,我就能实现我想要的。不幸的是,用户保存到db 由于没有弹簧@Transactional,因此不会回滚。因此,我的测试方法会改变后续测试方法的db和行为。

所以我需要将我的用户刷新到db里面的测试方法(不是在最后),并在测试方法结束时回滚所有对db的更改。

更新建议准备方法如下:

@Transactional
public void doSave(User user){
    getGenericDao().currentSession().save(user);
}

在testSave中调用doSave什么都不做。执行此方法后,我仍然没有db中的用户。我设置断点并从命令行检查我的数据库。

更新非常感谢您的回复。问题是方法flush()不会将我的用户放入数据库。我尝试了Isolation.READ_UNCOMMITTED并且它没有将我的用户放入数据库。我可以实现我想要的,但前提是我在@Test方法上关闭spring事务并在程序化事务中保存。然后@Test方法不回滚,为后续的@Test方法留下保存的用户。这里保存用户的@Test方法不像删除用户的@Test方法那样危险,因为它没有回滚。因此必须对@Test方法提供Spring事务支持,我无法将我的用户(或删除)放入db。实际上,只有在@Test方法结束并且@Test方法的事务被调用后,才会将用户放入(或删除)到db中。 所以我想在@Test方法的中间将我的用户保存到数据库中,并在@Test方法结束时将其回滚

谢谢!

4 个答案:

答案 0 :(得分:10)

最后我坚持以下解决方案:

首先,我的@Test方法未在spring @Transactional支持范围内运行。请参阅this article以了解它可能有多危险。接下来,不是在@Repository方法中使用@Test bean,而是使用@Service注释自动装配@Transactional bean。 奇迹就是这样的@Test方法

@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);
... }

将我的Answer对象放入数据库(就在answerService.save(created);之后),方法getAnswerById转到DB并提取它以检查保存是否正确。
为了消除@Test方法中对数据库所做的更改,我按JdbcTestUtils.executeSqlScript

重新创建了数据库

答案 1 :(得分:4)

  1. 查看here并发出有关@Transactional个测试(Spring Pitfalls: Transactional tests considered harmful)的警告。我已使用@org.springframework.test.context.jdbc.Sql在我的服务测试中重新填充数据库,并使用@Transactional重新填充控制器。
  2. ConstraintViolationException仅在提交事务时抛出了无效数据的控制器更新测试。所以我找到了3个选项:
    • 2.1使用@Commit@Transactional(propagation = Propagation.NEVER)注释测试。注意DB更改。
    • 2.2使用TestTransaction
  3. 代码:

         TestTransaction.flagForCommit();
         TestTransaction.end();
    
    • 2.3使用TransactionTemplate

    代码:

        @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;
            });
    

答案 2 :(得分:1)

如果flush不起作用,那么它在很大程度上取决于您的数据库隔离级别。

Isolation是数据库的ACID属性之一,用于定义一个操作所做的更改如何/何时对其他并发操作可见。

我相信您的隔离级别设置为Read CommittedRepeatable Read

答案 3 :(得分:0)

您还应该注意进口包装: 在我的情况下,我导入

  

导入javax.transaction.Transactional;

而不是

  

import org.springframework.transaction.annotation.Transactional;