代码之家  ›  专栏  ›  技术社区  ›  Brandon Yarbrough

在Spring/Hibernate堆栈中打开会话的位置?

  •  5
  • Brandon Yarbrough  · 技术社区  · 15 年前

    我正在尝试为一个Spring/Hibernate应用程序设计一个好的设计。当创建这样一个应用程序时,似乎有一些重大决策。

    第一个主要的决定似乎是在哪里放置会话/事务边界。看起来我有三个主要选择:作为一个过滤器,在控制器被调用之前,直接在服务调用级别的控制器之下,在存储库调用中填充到业务级别之下。

    在我看来,正确的选择是中间路线,但我不确定。我不希望我的事务打开太长时间,但同时,我不想经常担心分离的对象和业务逻辑中的延迟加载。不过,也有一些缺点。例如,它使业务逻辑很难在不暂停事务几秒钟的情况下进行远程调用。我想知道有没有更好的方法?

    2 回复  |  直到 15 年前
        1
  •  1
  •   Thierry    15 年前

    解决方案1:在视图筛选器中打开会话

    • 优势:
      • 您不会遇到懒惰的初始化异常,因此您可以在视图中使用模型对象而不必考虑两次
      • 如果进行多个服务调用(如果在第二个服务调用期间发生错误,则不会回滚第一个调用),则无需担心控制器在不同事务中执行其工作。
    • 缺点:
      • 较长的交易
      • 如果在提交时发生错误(例如,数据约束冲突),那么您将已经在大多数视图的响应输出流上写入了错误页,因此您将无法显示错误页(除非使用“在视图中打开会话”上方的另一个筛选器,该筛选器将在提交完成之前存储该外流,然后只发送生成的PAGE到客户机或重定向到错误页)

    解决方案2:业务层的事务边界

    • 优势:
      • 控制器级别的灵活性更高(但如果您的团队成员不真正了解事务,并且在控制器级别放置太多逻辑,则使用多个Tx(应该只有一个Tx),这可能是一个诅咒)
      • 资源使用更少(因为您更快地将DB连接返回池)
    • 缺点:
      • 在事务层之外,您需要小心地使用模型的对象。避免lazyinitialization异常的最简单方法可能是使用DTO,并让每个服务实现两个接口(一个接口扩展另一个接口):一个只包含返回DTO的方法,另一个只返回模型对象的方法。您将在控制器中的声明中使用第一个接口,并且仅在事务仍处于打开状态的业务层中使用第二个接口,因此返回模型对象不会导致lazy init异常。

    解决方案3:DAO层的事务边界

    除非DAO方法包含业务逻辑,否则将事务限制为DAO调用是没有意义的。这是接近“自动提交”模式的方法,我认为是有用的。


    在任何情况下,如果您希望缩短事务,我建议您密切关注每个业务用例的“SQL足迹”(通过将org.hibernate.sql日志类别设置为debug),并将生成的SQL与您自己编写的SQL进行比较。

    大多数时候,我都看到了缓慢的用例,这是因为Hibernate Lazy加载功能没有正确配置(它要么过于急切,在每个查询中添加12个连接级别,要么过于懒惰,按集合的元素发出查询)

        2
  •  1
  •   Snehal    15 年前

    这取决于您如何模块化代码。我假设您没有在控制器中编写所有与DB事务相关的代码。如果您已经将代码分隔到DAO或服务层,后者负责处理事务,那么只有所需的粒度才有意义。

    长时间占用事务并不是一个好的设计,因此让过滤器打开会话可能是一个坏主意。除非,您有一些要为其打开会话的延迟加载对象。

    如果上述不适用,那么您只能在数据访问层周围设置事务/会话边界。