【问题标题】:Hibernate: how to improve handling of Sessions and Transactions?Hibernate:如何改进会话和事务的处理?
【发布时间】:2020-11-25 19:40:00
【问题描述】:

我目前正在使用 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,因为这在我的应用程序中很常见,这也是我努力寻找令人满意的解决方案的核心原因.如果这很愚蠢,请随时指出,因为我仍在学习专门使用数据库和 Hibernate。在这种情况下,我将非常感谢有关如何避免这种情况的建议。

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

另一种方法是在需要的地方链接我的 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),而不是第二个块中的六行代码。听起来不错,但它没有:

  • 现在我将 someOtherObj 传递给 SomeDAO,这显然是不对的
  • 我还没有解决事务处理。我没有忘记它,在这个例子中我什至想不出一个伪解决方案。我必须知道事务总是以SomeDAO.makeChanges() 开始并以SomeOtherDAO.makeChanges() 结束
  • 此外,在阅读该想法时,我发现多个声明让我放心,链接 DAO 通常是不好的做法

这就是我现在卡住的地方。我有一些想法如何不这样做,但我仍然觉得离我想去的地方更近。所以我的结论性问题是:我应该在哪里(也许如何)管理我的会话和事务,以便能够跨多个 DAO 工作?

【问题讨论】:

    标签: hibernate kotlin session transactions ktor


    【解决方案1】:

    最终这样做了:

    首先:定义inline函数,接受任务(lamda),打开会话,执行给定任务,提交事务并关闭会话(注意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)
        }
    }
    

    编辑 - 一些 cmets:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-17
      • 2019-10-01
      • 2011-01-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多