有点绕圈子试图处理一个场景,我可能只是不以“反应性”方式看待它,只是需要休息一下才能看到更大的图景。
假设我想将用户保存到数据库中,但首先我需要查看是否存在具有该名称的人,所以:
使用
ReactiveCrudRepository
的存储库
interface UserRepository : ReactiveCrudRepository<User, Long> {
@Query("SELECT * FROM user WHERE name = :name LIMIT 1")
fun findByName(name: String): Mono<User>
fun saveUser(user: User): Mono<User>
}
企业代码
fun saveUser(user: User): Mono<User> {
return repository.findByName(user.name)
.flatMap { existingUser ->
return Mono.error(UserExistsException()) //A Mono<User> is expected
}
.switchIfEmpty {
return repository.saveUser(user)
}
}
存储库在空值上返回 Mono.empty() (即,如果没有找到具有该名称的用户)这一事实似乎限制了我处理用户已存在的情况的方式,因为我想抛出错误所以调用者以他的方式处理这个问题。
我尝试在存储库端返回
Mono<Optional<User>>
,所以我从未得到 empty
,但库不允许这样做。
我对响应式总体来说是新手,我想让事情变得简单,但又在良好实践的范围内,那么我应该如何处理这个问题呢?
flatMap
是我想要的吗?我需要在成功时返回一个 User,理想情况下在失败时返回一个异常,但我不能简单地将其放入 flatMap
中,因为它要求 Mono<User>
在 Kotlin 中,与 Java 不同,
return
不在 lambda 内部使用。这只允许在 inline
lambda 内实现提前返回。
此外,您的代码还有两个问题:
switchIfEmpty
接受 Mono
值,但不接受 lambda。Mono.error
需要通用参数 T
。所以这是固定代码:
fun saveUser(user: User): Mono<User> =
repository.findByName(user.name)
.flatMap { existingUser ->
Mono.error<User>(UserExistsException())
}.switchIfEmpty(repository.saveUser(user))
如果您使用 Kotlin 但使用 Spring,建议使用协程并尽可能避免使用 Project Reactor。为此,我们可以使用
UserRepository
而不是 CoroutineCrudRepository
重新定义 ReactiveCrudRepository
。
import org.springframework.data.repository.kotlin.CoroutineCrudRepository
interface UserRepository : CoroutineCrudRepository<User, Long> {
@Query("SELECT * FROM user WHERE name = :name LIMIT 1")
suspend fun findByName(name: String): User?
fun saveUser(user: User): User
}
生成的代码看起来也更好了。
suspend fun saveUser(user: User): User {
val foundUser = repository.findByName(user.name)
return if (foundUser == null) repository.saveUser(user)
else throw UserExistsException()
}