【问题标题】:Grails - Why is withTransaction needed?Grails - 为什么需要 withTransaction?
【发布时间】:2015-01-12 01:10:44
【问题描述】:

首先是一些背景知识:我正在使用 Spring Security 的修改版本来执行 Active Directory 身份验证,并检查存储在数据库中的可能访问权限。这意味着在普通的 Groovy 类中有一个调用来从数据库中加载信息:

if (Holders.config.loadRolesFromDatabase)
{
  Set<DomainClassRole> roles = DomainClassUser.findByUsername(username)?.roles
  if (roles)
    authorities.addAll(roles.collect({ new SimpleGrantedAuthority('ROLE_' + it.name) }))
}

这在 Hibernate 4.3.6.1 和 Tomcat 7.0.54 上运行良好,但是,在升级两者(到 4.3.10.18 和 8.0.14.1)之后,它现在在调用时会产生“HibernateException: No Session found for current thread”异常动态查找方法。在做了一些研究之后,我决定将此代码包装在 withTransaction 块中:

if (Holders.config.loadRolesFromDatabase)
{
  DomainClassUser.withTransaction({
    Set<DomainClassRole> roles = DomainClassUser.findByUsername(username)?.roles
    if (roles)
      authorities.addAll(roles.collect({ new SimpleGrantedAuthority('ROLE_' + it.name) }))
  })
}

这修复了错误,但是,我不确定为什么需要这样做。我目前对 withTransaction 的理解是,它是用来创建可以在出现异常等情况下回滚的事务。但是,我这里不需要执行任何回滚(都是只读调用),为什么我还要需要一个事务来执行这个调用吗?

【问题讨论】:

  • 不知道为什么行为发生了变化并且您没有会话,但也许您可以使用withNewSession 而不是withTransaction 以获得(可能)更好的性能和更具描述性的代码?

标签: java spring hibernate grails transactions


【解决方案1】:

有一个静态的withSession 方法,看起来是您需要的,但事实并非如此;不幸的是,它只使当前的休眠会话可用,但如果没有活动/当前会话,它不会创建一个。但是withTransaction 确实如此,因为在 Grails 中使用 Hibernate 时,Spring PlatformTransactionManagerHibernateTransactionManager,它确保在事务期间有一个活动会话,并在提交之前刷新并关闭它(除非有显式或自动(异常触发)回滚)。

因此以这种方式使用withTransaction 有点麻烦,因为您依赖于副作用,但无论如何您都会访问数据库,因此事务开销(实际上只是初始调用将自动提交设置为 false 的连接、隔离级别等以及最后的无操作提交调用)是次要的,通常不是问题。我们真正需要的是介于 withSessionwithTransaction 之间的东西,它使会话可用并在当前代码期间创建一个会话,但没有不必要的事务。

【讨论】:

  • 那么这是否意味着 withNewSession 适合 - 在 grails 2.4.5 中?我在使用 withNewSession 时询问,但随后出现与上述相同的错误:“No Session found for current thread”,因此在此 SO 帖子中结束。所以我似乎最终需要做的是 withNewSession { withTransaction { ... } }。
【解决方案2】:

This is why you need Transactions 即使在读取数据时。所有数据库语句都必须在物理事务中注册,因此您不会不使用它们。当您没有明确地有事务边界时,您只需在自动提交模式下进行调整,因此每个语句都在不同的数据库事务中运行(这会增加开销)。更不用说对您的连接池解决方案造成的过度压力了。

【讨论】:

    【解决方案3】:

    你应该使用withNewSession()。根据文档,它自史前 Grails 1.2.0 以来就可用。

    简短回顾:

    • withSession() - 使当前的 Hibernate 会话可用(如果存在,否则你会得到“No Session found for current thread”)
    • withNewSession() - 在不启动事务的情况下创建新的 Hibernate 会话(我相信最适合您的解决方案)
    • withTransation() - 启动并提交事务(对于简单的只读操作而言,这并不是最佳选择)

    (在 Grails 3.1.13 上测试)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-12
      • 1970-01-01
      • 1970-01-01
      • 2015-03-24
      • 2013-08-02
      • 2019-06-09
      • 1970-01-01
      • 2020-05-03
      相关资源
      最近更新 更多