CRUD 存储库不遵守 UNIQUE 约束

Posted

技术标签:

【中文标题】CRUD 存储库不遵守 UNIQUE 约束【英文标题】:CRUD repository does not respect UNIQUE constraint 【发布时间】:2019-09-29 05:01:11 【问题描述】:

我有以下 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(),
        "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) 
        
    

插入新实体时总是带有重复的电子邮件,不会引发任何异常。

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)

提前致谢!

【问题讨论】:

【参考方案1】:

发生这种情况是因为没有发出 insert 声明。

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

    @DataJpaTest@Transactional。这意味着在其中执行的@Test 方法的事务会在方法返回后回滚。 UserEntity 映射还鼓励休眠延迟insert(尝试在id 属性上使用@GeneratedValue(strategy = IDENTITY) 来强制发出inserts)

没有深入了解太多细节,运行测试时会发生以下情况:

    Spring 的测试基础架构begins 事务 @Test 方法运行 save(testUserEntity) - Hibernate 意识到没有理由访问数据库并延迟 insert shouldThrow&lt;SQLException&gt; save(newUserEntity) - 和之前一样 @Test 方法返回 事务回滚。 Hibernate 确实会执行 inserts,因为没有理由这样做。

如何解决?

最简单的方法是使用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中没有flush方法

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

见:What is difference between CrudRepository and JpaRepository interfaces in Spring Data JPA?


异常说明

您期望抛出SQLException

但请注意,DataIntegrityViolationException 将被抛出。

见:Consistent Exception Hierarchy

【讨论】:

很好的答案,我 3 天前刚开始使用 Spring Boot :) 用 @Commit 注释测试并不能解决我的问题,因为我在测试方法结束之前检查异常。我按照您的建议扩展了JpaRepository,并在flush() 上捕获了异常,并在测试中使用了@Rollback,以避免它抛出UnexpectedRollbackException。也许你想用这些来更新你的答案。 哦,我只是错过了一些时刻。 @Commit 将抛出 @Test 的异常 outside (我删除了这部分答案)。您不需要使用@Rollback。将flush 包装成assertThrows 就足够了。只有当@Commit 出现在@Test 上时,我才能获得UnexpectedRollbackException。你能详细说明一下吗? 很高兴能帮到你:)

以上是关于CRUD 存储库不遵守 UNIQUE 约束的主要内容,如果未能解决你的问题,请参考以下文章

MySQL-进阶CRUD

MySQL<4>

MySQL-约束存储引擎事务索引视图

不能在 Oracle 11g 中删除表的约束?

sql

数据定义