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)
来强制发出insert
s)
没有深入了解太多细节,运行测试时会发生以下情况:
-
Spring 的测试基础架构
begin
s 事务
@Test
方法运行
save(testUserEntity)
- Hibernate 意识到没有理由访问数据库并延迟 insert
shouldThrow<SQLException> save(newUserEntity)
- 和之前一样
@Test
方法返回
事务回滚。 Hibernate 确实会执行 insert
s,因为没有理由这样做。
如何解决?
最简单的方法是使用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 约束的主要内容,如果未能解决你的问题,请参考以下文章