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 行,但迫使我以某种方式管理 call
和 session
之间的关系(在地图或其他东西中),这对我来说似乎有点过分了。
另一种方法是在需要的地方 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)
,而不是第二个块中的六行代码。听起来不错,但事实并非如此:
- 现在我把someOtherObj传给了SomeDAO,这显然是不对的
- 我还没有解决事务处理。我没有忘记它,在这个例子中我什至无法想出一个伪解决方案。我必须知道交易总是在
SomeDAO.makeChanges()
开始并在 SomeOtherDAO.makeChanges()
结束
- 此外,在阅读该想法时,我遇到了多个声明,这些声明让我放心,linking DAO 通常只是不好的做法
这就是我现在被困的地方。我知道如何不这样做,但我仍然觉得离我想去的地方更近了。所以我最后的问题是:我应该在哪里(也许如何)管理我的会话和事务以便能够跨多个 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()
需要 suspend
ed 因为
它内联 suspend
-函数
- 在我的例子中,可以有调用应该 return 加载信息
来自数据库,因此为什么 lambda-
task
和
executeInSession()
函数 return Any
。你应该只通过
自包含任务,您可以从中省略 return 类型定义
executeInSession()
并将 lambda 的 return 类型更改为 Unit
我目前正在使用 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 行,但迫使我以某种方式管理 call
和 session
之间的关系(在地图或其他东西中),这对我来说似乎有点过分了。
另一种方法是在需要的地方 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)
,而不是第二个块中的六行代码。听起来不错,但事实并非如此:
- 现在我把someOtherObj传给了SomeDAO,这显然是不对的
- 我还没有解决事务处理。我没有忘记它,在这个例子中我什至无法想出一个伪解决方案。我必须知道交易总是在
SomeDAO.makeChanges()
开始并在SomeOtherDAO.makeChanges()
结束
- 此外,在阅读该想法时,我遇到了多个声明,这些声明让我放心,linking DAO 通常只是不好的做法
这就是我现在被困的地方。我知道如何不这样做,但我仍然觉得离我想去的地方更近了。所以我最后的问题是:我应该在哪里(也许如何)管理我的会话和事务以便能够跨多个 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()
需要suspend
ed 因为 它内联suspend
-函数 - 在我的例子中,可以有调用应该 return 加载信息
来自数据库,因此为什么 lambda-
task
和executeInSession()
函数 returnAny
。你应该只通过 自包含任务,您可以从中省略 return 类型定义executeInSession()
并将 lambda 的 return 类型更改为Unit