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
是一种黑客攻击,因为你依赖于副作用,但无论如何你都要去数据库,所以事务开销(实际上只是连接上的初始调用以设置自动提交)假,隔离级别等,以及最后的无操作提交调用)是次要的,通常不是问题。 我们真正需要的是withSession
和withTransaction
之间的东西,它使会话可用并在当前代码的持续时间内创建一个,但没有不必要的事务。
这就是为什么即使在读取数据时也需要交易 。 所有数据库语句都必须在物理事务中注册,因此它不像您不使用它们。 如果您没有明确拥有事务边界,则只需在自动提交模式下进行调整,因此每个语句都在不同的数据库事务中运行(这会产生更多开销)。 更不用说连接池解决方案的过度压力了。
你应该使用withNewSession()
。 根据文档,它可以从史前Grails 1.2.0开始提供。
简短回顾:
-
withSession()
– 使当前的Hibernate会话可用(如果存在,否则你会得到“没有找到当前线程的会话”) -
withNewSession()
– 在不启动事务的情况下创建新的Hibernate会话(我相信最适合您的解决方案) -
withTransation()
– 启动并提交事务(不完全是简单只读操作的最佳选择)
(在Grails 3.1.13上测试)