Grails – 为什么需要交易?

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

if (Holders.config.loadRolesFromDatabase) { Set 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:当前线程找不到会话”exception方法。 做了一些研究之后,我决定将这段代码包装在withTransaction块中:

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

这修复了错误,但是,我不确定为什么这是必需的。 我目前对withTransaction的理解是它用于创建可以在exception情况下回滚的事务等。但是,我不需要在这里执行任何回滚(它只是所有只读调用),为什么我仍然需要一个交易来执行此调用?

有一个静态的withSession方法看起来像你需要的东西,但它不是; 不幸的是,它只使当前的Hibernate会话可用,但如果没有活动/当前会话,则不会创建一个。 但是withTransaction确实如此,因为在Grails中使用Hibernate时,Spring PlatformTransactionManager是一个HibernateTransactionManager ,它确保在事务持续期间有一个活动会话,并在提交之前刷新并关闭它(除非有明确的或自动的(例外 -触发)回滚)。

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

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

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

简短回顾:

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

(在Grails 3.1.13上测试)