Grails - 为什么需要交易?

时间:2014-11-14 15:32:32

标签: java spring hibernate grails transactions

首先是一些背景知识:我正在使用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:当前线程找不到会话”异常动态查找器方法。在做了一些研究之后,我决定将这段代码包装在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的理解是它用于创建可以在异常情况下回滚的事务等。但是,我不需要在这里执行任何回滚(它只是所有只读调用),为什么我仍然需要一个交易来执行此调用吗?

3 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:1)

This is why you need Transactions即使在阅读数据时也是如此。所有数据库语句都必须在物理事务中注册,因此它不像您不使用它们。如果您没有明确拥有事务边界,则只需在自动提交模式下进行调整,因此每个语句都在不同的数据库事务中运行(这会产生更多开销)。更不用说连接池解决方案的压力过大。

答案 2 :(得分:1)

您应该使用withNewSession()。根据文档,它可以从史前Grails 1.2.0开始提供。

简短回顾:

  • withSession() - 使当前的Hibernate会话可用(如果存在,否则你得到&#34;找不到当前线程的会话&#34;)
  • withNewSession() - 创建新的Hibernate会话而不启动事务(我相信最适合您的解决方案)
  • withTransation() - 启动并提交事务(不完全是简单只读操作的最佳选择)

(在Grails 3.1.13上测试)