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

Hibernate:如何改进会话和事务的处理?,hibernate,kotlin,session,transactions,ktor,Hibernate,Kotlin,Session,Transactions,Ktor,我目前正在用Kotlin/ktor和Hibernate(没有Spring!)开发一个服务器应用程序,我对如何处理Hibernate会话和事务不太满意。因此,我正在寻找一些关于如何改进我的设置的建议,或者(如果合理的话)一些我正在做的事情实际上并没有那么糟糕的保证 所做的任何调用都将转发到处理程序函数,该函数在ktors路由功能中调用 route("foo"){ get { FooHandler.doStuff(call) } //...

我目前正在用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()
}
class SomeDAO (val session: Session = HibernateDBA.sessionFactory.openSession()) {
    fun makeChanges(someObj : SomeClass, someOtherObj : SomeOtherClass) {
        session.persist(someObj)
        SomeOtherDAO(session).makeChanges(someOtherObj)
    }
}
如您所见,我将会话传递给DAO的构造函数,因为
makeChanges()
需要会话(例如,对于
session.persist(someObj)
)。这里困扰我的是,我必须在每个处理函数中包含相同的4行代码

我还想指出,我有意在我的示例中包含多个DAO,因为这在我的应用程序中很常见,也是我努力寻找令人满意的解决方案的核心原因。如果这是愚蠢的,请随意指出,因为我仍在学习如何使用数据库和专门的休眠。如果是这样的话,我将非常感谢你给我的关于如何避免这种情况的建议

我最初的想法是拦截每个呼叫,在
Routing.RoutingCallStarted
创建会话,然后在
Routing.RoutingCallFinished
关闭会话。这将删除每个函数中的2行,但迫使我以某种方式管理
调用
会话
之间的关系(在映射或其他内容中),这对我来说似乎有些过分

另一种方法是在需要的地方链接我的DAO,在构造函数调用时打开会话,或者在需要时将现有会话传递给构造函数,这可能类似于:

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()
}
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工作,我应该在哪里(或者如何)管理我的会话和事务?

最后这样做:

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

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

编辑-一些评论:

  • 在我的例子中,
    doStuff()
    和传递的
    task()
    需要
    suspend
    关键字,因为他们正在处理
    io.ktor.application.ApplicationCalls
    ,它们是协同程序
  • 在扩展中,
    executeInSession()
    需要被
    suspend
    ed,因为 它内联了
    suspend
    -函数
  • 在我的情况下,可能会有调用返回加载的信息 因此,为什么lambda-
    任务和
    
    executeInSession()
    函数返回
    Any
    。你应该只通过吗 自包含任务,您可以从
    executeInSession()
    并将lambda的返回类型更改为
    Unit