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

Vol*_*kyi 15 java hibernate spring-test hibernate-session spring-transactions

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

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

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

getGenericDao().currentSession().save(expected);
Run Code Online (Sandbox Code Playgroud)

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

User actual = getUser(generatedId);
Run Code Online (Sandbox Code Playgroud)

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

currentSession().setFlushMode(MANUAL);
//do saving here
currentSession().flush();
Run Code Online (Sandbox Code Playgroud)

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

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

UPDATE建议准备方法如下:

@Transactional
public void doSave(User user){
    getGenericDao().currentSession().save(user);
}
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢!

Vol*_*kyi 11

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

首先,我的@Test方法没有在弹簧@Transactional支持下运行.请参阅此文章以了解它可能有多危险.接下来,不是@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);
... }
Run Code Online (Sandbox Code Playgroud)

将我的Answer对象放入数据库(紧随其后answerService.save(created);),方法getAnswerById转到DB并提取它以检查保存是否正确.
要消除@Test方法I中对数据库所做的更改,请重新创建数据库JdbcTestUtils.executeSqlScript

  • 您可以应用 @DirtiesContext 注释,而不是使用 JdbcTestUtils。 (2认同)

GKi*_*lin 6

  1. 看看这里有关于@Transactional测试的警告(Spring Pitfalls:Transactional tests thinking疼)。我曾经@org.springframework.test.context.jdbc.Sql在我的服务测试和@Transactional控制器中重新填充数据库。
  2. ConstraintViolationException只有在提交事务时才会抛出具有无效数据的控制器更新测试。所以我找到了 3 个选项:
    • 2.1 用@Commit或注释测试@Transactional(propagation = Propagation.NEVER)。请注意数据库更改。
    • 2.2 使用 TestTransaction

代码:

     TestTransaction.flagForCommit();
     TestTransaction.end();
Run Code Online (Sandbox Code Playgroud)
  • 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;
        });
Run Code Online (Sandbox Code Playgroud)


JSS*_*JSS 1

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

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

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