Репозиторий CRUD не учитывает ограничение UNIQUE

У меня есть следующий объект JPA

@Entity
class UserEntity {

    companion object {
        fun fromParameters(uuid: String, email: String, password: String, firstName: String, lastName: String) =
            UserEntity().apply {
                this.uuid = uuid
                this.email = email
                this.password = password
                this.firstName = firstName
                this.lastName = lastName
            }
    }

    @Id
    lateinit var uuid: String

    @Column(nullable = false, unique = true)
    lateinit var email: String

    @Column(nullable = false)
    lateinit var password: String

    @Column(nullable = false)
    lateinit var firstName: String

    @Column(nullable = false)
    lateinit var lastName: String
}

И это мой тест, чтобы проверить ограничение UNIQUE, вставив другого пользователя с тем же адресом электронной почты.

@RunWith(SpringRunner::class)
@DataJpaTest
@AutoConfigureTestEntityManager
class TestUserCrudRepository {

    @Autowired
    private lateinit var userCrudRepository: UserCrudRepository

    private val testUserEntity = UserEntity.fromParameters(
        UUID.randomUUID().toString(),
        "[email protected]",
        "password".toHash(),
        "Caetano",
        "Veloso"
    )

    @Test
    fun `when email already exists it should throw error`() {
        with (userCrudRepository) {
            save(testUserEntity)
            val newUserEntity = with (testUserEntity) { UserEntity.fromParameters(UUID.randomUUID().toString(), email, password, firstName, lastName) }
            shouldThrow<SQLException> { save(newUserEntity) }
        }
    }
}

Новый объект всегда вставляется с дубликатом электронной почты без каких-либо исключений.

Expected exception java.sql.SQLException but no exception was thrown

Я вижу в журнале, что таблица создана правильно с заданным ограничением.

Hibernate: drop table user_entity if exists
Hibernate: create table user_entity (uuid varchar(255) not null, email varchar(255) not null, first_name varchar(255) not null, last_name varchar(255) not null, password varchar(255) not null, primary key (uuid))
Hibernate: alter table user_entity add constraint UK_4xad1enskw4j1t2866f7sodrx unique (email)

Заранее спасибо!

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
3 278
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это происходит из-за того, что не выдается заявление insert.

Спящий режим не flush сеанс если у него нет веских причин для этого.

  1. @DataJpaTest — это @Transactional. Это означает, что транзакция, в которой выполняется метод @Test, откатывается после возврата метода.
  2. UserEntity отображение также побуждает спящий режим задерживать insert (попробуйте использовать @GeneratedValue(strategy = IDENTITY) в свойстве id, чтобы принудительно выдать inserts)

Не вдаваясь в подробности, при запуске теста происходит следующее:

  1. Транзакция тестовой инфраструктуры Spring
  2. begin метод работает
  3. @Test - Hibernate понимает, что нет причин обращаться к базе данных и задержкиsave(testUserEntity)
  4. insert - то же, что и предыдущий
  5. shouldThrow<SQLException> { save(newUserEntity) } метод возвращает
  6. Транзакция откатывается. Hibernate выполняет @Tests, потому что для этого нет причин.

Как это исправить?

Самый простой способ сделать это — использовать insert:

with (userCrudRepository) {
    save(testUserEntity)
    val newUserEntity = with (testUserEntity) { UserEntity.fromParameters(UUID.randomUUID().toString(), email, password, firstName, lastName) }
    save(newUserEntity)
    assertThrows<DataIntegrityViolationException> {
        flush()
    }
}

Обратите внимание, что в JpaRepository#flush нет метода flush.

Я предполагаю, что вы расширили CrudRepository... Вместо этого вы можете расширить CrudRepository.

См.: В чем разница между интерфейсами CrudRepository и JpaRepository в Spring Data JPA?


Примечание об исключении

Вы ожидаете, что будет выброшено JpaRepository.

Но обратите внимание, что вместо этого будет выбрано SQLException.

См.: Согласованная иерархия исключений

Отличный ответ, я только начал работать с Spring Boot 3 дня назад :) Аннотирование теста с помощью @Commit не решает мою проблему, потому что я проверяю исключение до завершения метода тестирования. Я расширил JpaRepository, как вы предлагаете, поймал исключение на flush() и использовал @Rollback в тесте, чтобы избежать выдачи UnexpectedRollbackException. Возможно, вы захотите обновить свой ответ этим.

m0skit0 11.05.2019 21:03

О, я просто пропустил некоторые моменты. @Commit вызовет исключение за пределами из @Test (эту часть ответа я удалил). Вам не нужно использовать @Rollback. Обернуть flush в assertThrows должно быть достаточно. Я смог получить UnexpectedRollbackException, только если @Commit присутствует на @Test. Не могли бы вы рассказать об этом подробнее?

Denis Zavedeev 11.05.2019 21:27

Я рад, что смог вам помочь :)

Denis Zavedeev 12.05.2019 00:08

Другие вопросы по теме