Hibernate:如何改进会话和事务的处理?

Hibernate: how to improve handling of Sessions and Transactions?

我目前正在使用 Kotlin/ktor 和 Hibernate(没有 Spring!)开发一个服务器应用程序,我对我处理 Hibernate 会话和事务的方式不太满意。所以我正在寻找一些关于如何改进我的设置的指示,或者(如果有理由的话)一些保证我正在做的事情实际上并没有那么糟糕。

任何调用都将被转发到处理函数,该函数在 ktors 路由功能中调用。

route("foo"){
    get {
        FooHandler.doStuff(call)
    }
    //...
}

省略处理请求和响应,这些函数(例如 doStuff())可能看起来像这样:

suspend fun doStuff(call: ApplicationCall) {
    val session = HibernateDBA.sessionFactory.openSession()
    session.beginTransaction()

    SomeDAO(session).makeChanges(someObj)
    SomeOtherDAO(session).makeChanges(someOtherObj)

    session.transaction.commit()
    session.close()
}

如您所见,我将会话传递给 DAO 的构造函数,因为 makeChanges() 需要会话(例如 session.persist(someObj))。令我困扰的是,我必须在每个处理程序函数中包含相同的 4 行代码。

我还想指出,我有意在我的示例中包括多个 DAO,因为这在我的应用程序中很常见,这几乎是我努力寻找一个 DAO 的核心原因令人满意的解决方案。如果这是愚蠢的,请随时指出来,因为我仍在学习专门使用数据库和 Hibernate。在那种情况下,如果您能提供有关如何避免这种情况的建议,我将不胜感激。

我最初的想法是在 Routing.RoutingCallStarted 拦截每个调用以创建我的会话并在 Routing.RoutingCallFinished 关闭它。这将删除每个函数中的 2 行,但迫使我以某种方式管理 callsession 之间的关系(在地图或其他东西中),这对我来说似乎有点过分了。

另一种方法是在需要的地方 link 我的 DAO,然后在构造函数调用时打开会话,或者在需要时将现有会话传递给构造函数,这看起来像这样:

class SomeDAO (val session: Session = HibernateDBA.sessionFactory.openSession()) {
    fun makeChanges(someObj : SomeClass, someOtherObj : SomeOtherClass) {
        session.persist(someObj)
        SomeOtherDAO(session).makeChanges(someOtherObj)
    }
}

DomeOtherDAO 将具有相同的布局,包括构造函数。

那么我的处理程序只需调用 SomeDAO().makeChanges(someObj, someOtherObj),而不是第二个块中的六行代码。听起来不错,但事实并非如此:

这就是我现在被困的地方。我知道如何不这样做,但我仍然觉得离我想去的地方更近了。所以我最后的问题是:我应该在哪里(也许如何)管理我的会话和事务以便能够跨多个 DAO 工作?

最后这样做了:

首先:定义接受任务 (lamda)、打开会话、执行给定任务、提交事务并关闭会话的 inline 函数(注意我的特定需要 suspend 关键字用例,一般方法不需要!)

suspend inline fun executeInSession(task: suspend (session: Session) -> Any) : Any {
    val session = sessionFactory.openSession()
    session.beginTransaction()

    val taskOutcome = task(session)

    session.transaction.commit()
    session.close()
    return taskOutcome
}

其次:在处理函数中定义功能(例如doStuff())并将其传递给executeInSession():

suspend fun doStuff(call: ApplicationCall) {
    HibernateDBA.executeInSession {
        SomeDAO(session).makeChanges(someObj)
        SomeOtherDAO(session).makeChanges(someOtherObj)
    }
}

编辑 - 一些评论:

  • 在我的例子中,doStuff() 和传递的 task() 需要 suspend 关键字,因为他们正在处理 io.ktor.application.ApplicationCalls,它们是协程
  • 在扩展中,executeInSession() 需要 suspended 因为 它内联 suspend-函数
  • 在我的例子中,可以有调用应该 return 加载信息 来自数据库,因此为什么 lambda-taskexecuteInSession() 函数 return Any。你应该只通过 自包含任务,您可以从中省略 return 类型定义 executeInSession() 并将 lambda 的 return 类型更改为 Unit