CRUD 存储库不遵守 UNIQUE 约束 [英] CRUD repository does not respect UNIQUE constraint

查看:21
本文介绍了CRUD 存储库不遵守 UNIQUE 约束的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下 JPA 实体

I have the following JPA entity

@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 约束的测试,插入具有相同电子邮件的另一个用户.

And this is my test to check the UNIQUE constraint, inserting another user with the same email.

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

    @Autowired
    private lateinit var userCrudRepository: UserCrudRepository

    private val testUserEntity = UserEntity.fromParameters(
        UUID.randomUUID().toString(),
        "test@test.com",
        "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) }
        }
    }
}

新实体总是被插入重复的电子邮件,而不会抛出任何异常.

The new entity always gets inserted with a duplicate email without any exception being thrown.

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

我可以在日志中看到使用给定的约束正确创建了表.

I can see in the log that the table is created correctly with the given constraint.

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)

提前致谢!

推荐答案

发生这种情况是因为没有发出 insert 语句.

This happens because no insert statement issued.

Hibernate 不会flush 会话除非有充分的理由这样做.

Hibernate does not flush session unless it has a good reason to do so.

  1. @DataJpaTest@Transactional.这意味着在方法返回后回滚 @Test 方法中执行的事务.
  2. UserEntity 映射还鼓励休眠延迟 insert(尝试在 id 上使用 @GeneratedValue(strategy = IDENTITY)code> 属性强制发出 inserts)
  1. @DataJpaTest is @Transactional. This means that the transaction a @Test method executed within is rolled back after the method returns.
  2. UserEntity mapping also encourages hibernate to delay the insert (try using @GeneratedValue(strategy = IDENTITY) on id property to force issuing inserts)

不要深入研究太多细节,运行测试时会发生以下情况:

Not diving into too much details the following happens when you run test:

  1. Spring 的测试基础架构 begin 的事务
  2. @Test 方法运行
  3. save(testUserEntity) - Hibernate 意识到没有理由命中数据库并延迟insert
  4. shouldThrow{ save(newUserEntity) } - 与之前相同
  5. @Test 方法返回
  6. 事务回滚.Hibernate 确实执行 insert s,因为没有理由这样做.
  1. Spring's test infrastructure begins transaction
  2. @Test method runs
  3. save(testUserEntity) - Hibernate realizes that there is no reason to hit the database and delays the insert
  4. shouldThrow<SQLException> { save(newUserEntity) } - same as previous
  5. @Test method returns
  6. Transaction rolls back. Hibernate does execute inserts because there is no reason to.

如何解决?

最简单的方法是使用 JpaRepository#flush:

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

注意CrudRepository

Note that there is no flush method in CrudRepository

我猜你已经扩展了 CrudRepository... 你可能想要扩展 JpaRepository .

I guess that you have extended CrudRepository... You may want to extend JpaRepository instead.

参见:CrudRepository 和 JpaRepository 有什么区别Spring Data JPA 中的接口?

异常注意事项

您正在等待 SQLException 被抛出.

You are expecting an SQLException to be thrown.

但请注意DataIntegrityViolationException 将被抛出.

But note that DataIntegrityViolationException will be thrown instead.

参见:一致异常层次结构

这篇关于CRUD 存储库不遵守 UNIQUE 约束的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆