当前,我正在尝试使用Quarkus(1.3.1)构建一些简单的原型应用程序。我正在尝试使用反应式方法,并使用https://quarkus.io/guides/reactive-sql-clients将反应式数据库连接到PostgreSQL。在这里,我使用Kotlin和Mutiny来简化一些事情。
我有以下控制器:
package me.invitation.infrastructure.rest
import io.smallrye.mutiny.Uni
import me.invitation.application.service.InvitationService
import me.invitation.infrastructure.rest.api.InvitationDto
import me.invitation.infrastructure.rest.system.MicroserviceContentType
import org.eclipse.microprofile.context.ThreadContext
import org.slf4j.LoggerFactory
import javax.inject.Inject
import javax.ws.rs.*
import javax.ws.rs.core.Response
@Path("/invitation")
class Resource @Inject constructor(
private val invitationService: InvitationService,
private val threadContext: ThreadContext) {
@POST
@Consumes(MicroserviceContentType.PUBLIC_RSC_JSON)
fun postInvitation(invitation: InvitationDto): Uni<Response> {
return Uni.createFrom()
.completionStage(
threadContext.withContextCapture(
invitationService.sendInvite(invitation.email)))
.map { Response.status(Response.Status.CREATED).build() }
}
companion object {
private val log = LoggerFactory.getLogger(Resource::class.java)
}
}
InvitationService 实现如下:
package me.invitation.application.service
import io.smallrye.mutiny.Uni
import me.invitation.application.registry.InvitationRegistry
import me.invitation.application.service.mail.MailService
import org.apache.commons.codec.binary.Base32
import org.eclipse.microprofile.config.inject.ConfigProperty
import org.eclipse.microprofile.context.ManagedExecutor
import org.eclipse.microprofile.context.ThreadContext
import org.slf4j.LoggerFactory
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import java.time.Duration
import java.time.LocalDateTime
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.function.Supplier
import javax.enterprise.context.ApplicationScoped
import javax.inject.Inject
import javax.transaction.Transactional
@ApplicationScoped
class ExternalRegistryInvitationService @Inject constructor(
private val invitationRepository: InvitationRegistry,
private val mailService: MailService,
private val threadContext: ThreadContext,
private val managedExecutor: ManagedExecutor,
@ConfigProperty(name ="invitation.expiration-ttl-seconds") private val invitationTTLSeconds: Long
) : InvitationService {
@Transactional
override fun sendInvite(email: String): CompletableFuture<Unit> {
return Uni.createFrom()
.completionStage(threadContext.withContextCapture(invitationRepository.save(id, email)))
.flatMap {wasSaved ->
if(wasSaved) {
Uni.createFrom().completionStage(threadContext.withContextCapture(mailService.sendInvitationEmail(email)))
} else {
throw InvitationAlreadyPresentException()
}
}.subscribeAsCompletionStage()
}
}
在这里,我尝试将记录保存到数据库中,然后发送电子邮件
InvitationRegistry 的实现如下:
package me.invitation.infrastructure.db
import io.smallrye.mutiny.Uni
import io.vertx.mutiny.pgclient.PgPool
import io.vertx.mutiny.sqlclient.Tuple
import me.invitation.application.registry.InvitationRegistry
import java.time.LocalDateTime
import java.util.concurrent.CompletableFuture
import javax.enterprise.context.ApplicationScoped
import javax.inject.Inject
import javax.transaction.Transactional
@ApplicationScoped
class PostgresInvitationRegistry @Inject constructor(private val client: PgPool) : InvitationRegistry {
private final val insertQuery = "INSERT INTO invitation(id, email, aud_ts_created, aud_ts_last_modified) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING RETURNING id"
@Transactional(value = Transactional.TxType.MANDATORY)
override fun save(id: String, email: String): CompletableFuture<Boolean> {
return client
.preparedQuery(insertQuery, Tuple.of(id, email, LocalDateTime.now(), LocalDateTime.now()))
.flatMap { result -> Uni.createFrom().item(result.rowCount() > 0) }
.subscribeAsCompletionStage()
}
}
MailService的实现无关紧要,因为它所做的一切都会引发RuntimeException。
在这里,我希望该顶级事务能够回滚。因为 MailService.sendInvitationEmail 引发异常。但是事实并非如此。即使有例外情况,记录也会保留下来。我已经阅读了本指南https://quarkus.io/guides/transaction#reactive-extensions和https://quarkus.io/guides/context-propagation#usage-example-for-completionstage,并认为声明性事务与Quarkus中的反应式sql一起很好地工作。
我认为反应连接有一个 autocommit 设置,该设置设置为true。但是,无论是在文档中还是在quarkus及其扩展名的来源中都没有找到与自动提交相关的任何内容。令人惊讶的是,甚至对于quarkus主jdbc pool = agroal都没有自动提交设置!
Myabe有人知道如何使Quarkus中的反应式sql客户端使用声明式事务