Транзакционный тест Spring Boot позволяет нарушить уникальное ограничение на обновление

У меня есть таблица БД (Postgres) с уникальным ограничением для одного столбца. У меня есть тест, отмеченный аннотацией @Transactional, который обновляет это уникальное значение столбца до неуникального значения. Я ожидаю, что операция обновления завершится ошибкой, но она выполняется успешно. Более того, когда я получаю обновленный объект из базы данных (внутри той же транзакции), значение столбца там обновляется.

Упрощенная версия сущности JPA:

@Entity
@Table(name = "entities")
public class Entity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    // The unique column
    @Column(name = "name", unique = true)
    @NotNull
    private String name;

    ...
}

Упрощенная версия для теста:

@Test
@Transactional
public void test() {
    Entity firstEntity = new Entity();
    firstEntity.setName("First Entity Name");

    // This just calls corresponding JPA repository .save method
    entityService.create(firstEntity);

    Entity secondEntity = new Entity();
    secondEntity.setName("Second Entity Name");
    entityService.create(secondEntity);

    // Update name to a not unique value
    secondEntity.setName(firstEntity.getName);

    // This calls corresponding JPA repository .save method. 
    // It also catches DataIntegrityViolationException and throws 
    // a more user friendly exception instead
    entityService.update(secondEntity);
}

Этот код работает так, как я ожидаю, если аннотация @Transactional удалена или транзакция зафиксирована. Я также пытался вызвать EntityManager.flush(), как указано здесь, но этот код выдает ConstraintViolationException после сброса результирующих данных, поэтому я не могу проверить, что мой метод entityService.update работает правильно и выдает правильное исключение.

Также обратите внимание, что если я попытаюсь создать новую запись с неуникальными данными в транзакционном тесте (не с обновлением), тогда тест будет работать так, как ожидалось — DataIntegrityViolationException генерируется, когда создается неуникальный объект.

Может ли кто-нибудь пояснить, можно ли заставить сценарий обновления работать должным образом, сохраняя транзакцию теста, чтобы мне не нужно было заботиться об очистке данных?


person knesterovich    schedule 19.04.2017    source источник
comment
Ваш метод обслуживания является фактически ошибочным методом здесь. Потому что, если вы можете перехватить исключение в своей службе, это означает, что у вас неправильная граница транзакции, ваша служба должна быть @Transactional, что означает, что исключение возникнет только ПОСЛЕ завершения выполнения метода службы. Если вам действительно нужно то, что у вас есть, позвоните saveAndFlush (но я бы посчитал это антипаттерном).   -  person M. Deinum    schedule 19.04.2017
comment
@ М.Дейнум, спасибо за ваш ответ. Во-первых, я мог бы упростить пример. На самом деле там есть куча вызовов, а try-catch оборачивает транзакционный сервисный метод. Во-вторых, я не думаю, что это влияет на то, что уникальный столбец обновляется до неуникального значения, хотя и не должен. Имеет ли это?   -  person knesterovich    schedule 19.04.2017
comment
Все дело в том, что сброс делается только как часть tx коммита (или вручную). Теперь ваша транзакция охватывает весь вызов (и, как уже говорилось, imho, ваш тест ошибочен, так как вы должны, по крайней мере, сбросить после create, поскольку без этого еще ничего не сохраняется). Из-за большей границы tx ваш тест не пройден.   -  person M. Deinum    schedule 20.04.2017
comment
@ M.Deinum, для меня это имеет смысл, спасибо. Мой единственный вопрос: почему тогда работает сценарий create? т.е. создать объект 1, а затем создать объект 2 с тем же именем в тех же транзакциях. Это работает так, как я ожидаю, т.е. создание entity2 не удается. Не могли бы вы уточнить это, если можно?   -  person knesterovich    schedule 20.04.2017
comment
поскольку при создании необходимо выполнить запрос к базе данных, чтобы получить идентификатор объекта, это нельзя отложить, в то время как обновление может.   -  person M. Deinum    schedule 20.04.2017