如何在Jdbi的inTransaction回调中执行挂起函数且不使用runBlocking
核心原因
Jdbi核心库的inTransaction是同步阻塞API,接收的回调为普通非挂起函数,回调执行时不存在协程续体上下文,因此无法直接在回调内部调用suspend修饰的挂起函数。直接在回调中使用runBlocking会阻塞协程调度线程,违背协程非阻塞设计原则。
可行方案
方案1:使用官方协程扩展(推荐)
Jdbi 3.35.0+ 版本提供了官方Kotlin协程适配模块,原生支持协程上下文下的事务操作,无需自行封装线程切换逻辑。
- 引入和你当前Jdbi版本一致的Kotlin协程适配依赖
- 直接调用扩展提供的挂起版
inTransaction方法即可,代码可直接编译运行:
suspend fun <T> withinTransaction(fn: suspend (Handle) -> T): T = jdbi.inTransaction { handle -> fn(handle) }
该扩展内部会自动将事务操作调度到合适的线程池执行,完整保留协程上下文,不会阻塞协程调度线程,完全符合协程编码规范。
方案2:自行封装适配(适用于低版本Jdbi)
如果项目使用的Jdbi版本低于3.35.0,无法使用官方扩展,可以结合withContext自行封装,将阻塞事务操作调度到专门的IO线程池执行,避免阻塞协程事件循环线程:
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import org.jdbi.v3.core.Handle import org.jdbi.v3.core.Jdbi import org.jdbi.v3.core.transaction.TransactionIsolationLevel suspend fun <T> Jdbi.inSuspendTransaction( isolationLevel: TransactionIsolationLevel? = null, action: suspend (Handle) -> T ): T = withContext(Dispatchers.IO) { if (isolationLevel != null) { inTransaction<T, Exception>(isolationLevel) { handle -> runBlocking { action(handle) } } } else { inTransaction<T, Exception> { handle -> runBlocking { action(handle) } } } } // 业务侧直接调用 suspend fun <T> withinTransaction(fn: suspend (Handle) -> T): T = jdbi.inSuspendTransaction(action = fn)
注意:该封装中使用的
runBlocking是安全的——整个同步事务块已经被调度到专门执行阻塞IO操作的Dispatchers.IO线程池,阻塞的是IO线程而非协程调度线程,不会破坏协程的非阻塞特性。如果要求代码中完全不出现runBlocking,请优先升级Jdbi版本使用官方协程扩展。
内容的提问来源于stack exchange,提问作者Lothium
相关产品推荐
相关产品推荐

