Как сбросить данные в базу данных внутри активной весенней транзакции?

Я хочу протестировать метод save () сеанса гибернации, используя среду тестирования Spring. @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);

Я попытался использовать метод очистки гибернации, например:

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

Он не сбрасывает моего пользователя в базу данных! Однако, если я не использую аннотацию @Transactional spring и сохраню своего пользователя в программной весенней транзакции, я достигну того, чего хочу. К сожалению, пользователь, сохраненный в db, не откатывается, поскольку нет spring @Transactional. Поэтому мой метод тестирования изменяет базу данных и поведение последующих методов тестирования.

Поэтому мне нужно сбросить моего пользователя в db внутри метода тестирования (не в конце), а в конце метода тестирования откатить все изменения в db.

ОБНОВЛЕНИЕ предлагает подготовить следующий метод:

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

И вызов doSave внутри testSave ничего не делает. По-прежнему у меня нет пользователя в базе данных после выполнения этого метода. Я устанавливаю точку останова и проверяю свою базу данных из командной строки.

ОБНОВЛЕНИЕ Большое спасибо за ответ. Проблема в том, что метод flush () не помещает моего пользователя в базу данных. Я пробовал Isolation.READ_UNCOMMITTED, и он не помещает моего пользователя в базу данных. Я могу добиться того, чего хочу, но только если я отключу весеннюю транзакцию в методе @Test и сохраню в программной транзакции. НО тогда метод @Test не откатывается, оставляя сохраненного пользователя для последующих методов @Test. Здесь метод @Test для сохранения пользователя не так опасен, как метод @Test для удаления пользователя, потому что он не откатывается. Таким образом, должна быть предусмотрена поддержка транзакций для метода @Test, с помощью которого я все равно не могу поместить своего пользователя (или удалить) в db. Фактически пользователь помещается (или удаляется) в базу данных только после завершения метода @Test и завершения транзакции для метода @Test. Итак, я хочу сохранить своего пользователя в базе данных в середине метода @Test и откатить его в конце метода @Test

Спасибо!


person Volodymyr Levytskyi    schedule 23.06.2013    source источник
comment
Можете ли вы также дать нам свой класс GenericDao? РЕДАКТИРОВАТЬ: на самом деле, я думаю, что вижу проблему. Текущая транзакция должна быть зафиксирована до того, как данные будут фактически сохранены в базе данных. Итак, вам нужно два метода @Transactional: один для сохранения, а другой для загрузки.   -  person Martin Wickham    schedule 23.06.2013
comment
Пожалуйста, посмотрите, правильно ли я сделал   -  person Volodymyr Levytskyi    schedule 23.06.2013
comment
Закрывать. Я имею в виду наличие двух транзакционных функций, вызываемых нетранзакционной функцией. Это должно сработать ...   -  person Martin Wickham    schedule 23.06.2013
comment
Если я сделаю свой метод testSave нетранзакционным, у меня не будет отката после завершения метода testSave. Таким образом сохраненный пользователь останется.   -  person Volodymyr Levytskyi    schedule 23.06.2013
comment
Сессия для текущего потока не найдена. Это исключение гибернации при вызове метода сохранения GenericDao. @Transactional на doSave не работает!   -  person Volodymyr Levytskyi    schedule 23.06.2013


Ответы (4)


В конце концов я остановился на следующем решении:

Во-первых, мои @Test методы не работают в рамках поддержки Spring @Transactional. См. эту статью, чтобы узнать, насколько это может быть опасно. Затем вместо использования @Repository beans внутри @Test методов я автоматически подключаю @Service beans, которые используют аннотацию @Transactional. Чудо в том, что @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);
... }

помещает мой объект ответа в базу данных (сразу после answerService.save(created);), а метод getAnswerById переходит в базу данных и извлекает его, чтобы проверить правильность сохранения.
Чтобы исключить изменения, внесенные в базу данных методом @Test, я воссоздаю базу данных с помощью JdbcTestUtils.executeSqlScript

person Volodymyr Levytskyi    schedule 28.06.2013
comment
Вместо использования JdbcTestUtils вы можете применить аннотацию @DirtiesContext. - person Rosty Kerei; 16.08.2016

  1. Посмотрите здесь с предупреждением о @Transactional тестах (Spring Ловушки: транзакционные тесты считаются опасными). Я использовал @org.springframework.test.context.jdbc.Sql для повторного заполнения БД в своих сервисных тестах и ​​@Transactional для контроллеров.
  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 Используйте TestTransaction

Код:

     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;
        });
person Grigory Kislin    schedule 25.09.2017

Вы также должны позаботиться об импортированном пакете: в моем случае я импортировал

import javax.transaction.Transactional;

вместо

import org.springframework.transaction.annotation.Transactional;

person Meinew    schedule 10.06.2014

Если flush не работает, это во многом зависит от уровня изоляции вашей базы данных.

Isolation - одно из ACID свойств базы данных, которое определяет, как / когда изменения, сделанные одной операцией, становятся видимыми для других параллельных операций.

Я считаю, что ваш уровень изоляции установлен на Read Committed или Repeatable Read.

person JSS    schedule 23.06.2013