前篇:
Grails陷阱之一
Grails陷阱之二:
跨request使用Domain Class实例需要重新attach
其实这并不能说是Grails的陷阱,而是Grails所依赖的Hibernate设计使然,不过初学者(比如我)可能对此没有概念,因此拿来说一下。
代码非常简单,如下:
if (session.user.canDo(actionName)) {
...
}
这段代码从session中取出user(假设user登陆之后,你把user对象保存在HTTP session中),然后调用上面的canDo方法,检测user是否有权限执行某个action。canDo方法会读取user.roles(一对多)。
扔出异常如下:
Grails Runtime Exception
Error Details
Error 500: could not initialize proxy - no Session
Servlet: grails
URI: /test/grails/admin/index.dispatch
Exception Message: could not initialize proxy - no Session
Caused by: could not initialize proxy - no Session
Class: AuthFilters
At Line: [40]
注意,这个异常仅会在你的代码(比如canDo)访问一对一或一对多的关联对象时产生。
熟悉hibernate的同志可能一眼就看出是什么问题了。在访问关联对象时,Hibernate通过
动态代理来读取以便支持lazy加载,而延迟加载的相关操作同所有持久化操作一样,需要在一个session内(是Hibernate的持久化Session,不要和Web容器管理的HTTP Session混淆哦),而当一个持久化对象被保存到HTTP Session中,然后在下一个request中被拿出来时,当然之前的Hibernate的Session早结束了。
注意,Hibernate提供了OSIV(Open Session In View)的Filter,可以配置在web.xml中,会针对每个HTTP request,开启一个新的Hibernate session,然后在request结束时关闭。
如果没有OSIV,你也会遇到类似的异常,但是这和这里讨论的并不是一个问题——我为什么对这个陷阱印象深刻,就是因为我一度把这两种情况搞混了。
Grails已经内置了OSIV,虽然它可能存在一些bug(见后文),但是这里的问题并非如此。这里的问题是,在新一次request中的Hibernate session,已经不是创建user对象当初的那个Hibernate session了。
解决方案:
1. 不在session中保存domain class的实例(即持久化对象),而是保存它的id。
if (User.read(session.userId).canDo(actionName)) {
...
}
我见到的类似的方式还有
session.user = User.read(session.user.id)
if (session.user.canDo(actionName)) {
...
}
当然这样的代码太别扭,而且只适用User.read(即只读)的情形,如果你之前对user对象做过修改(比如loginCount++)并尚未保存,则重新读取会丢失原对象上的修改。
2. 不使用lazy load,所有关联都一次读入。
class User {
...
static hasMany = [roles:Role]
static mapping = {
roles lazy:false
}
}
注意,调用时所有可能读到的关联都必须改成不是lazy的的,比如假设你的canDo方法,还要检查每个roles上的permission,则permissions关联也要禁用lazy加载:
class User {
...
static hasMany = [roles:Role]
static mapping = {
roles lazy:false
}
boolean canDo(String action) {
roles.any {
it.permissions.contains('*') ||
it.permissions.contains(action)
}
}
}
class Role {
...
static hasMany = [permissions:String]
static mapping = {
permissions lazy:false
}
}
方案1的问题就是它只适用于只读情况,如果需要跨若干个request修改持久化对象就不行了。当然你可以把所有修改存放在web session中,然后一次性修改和提交,但是这无谓增加了代码复杂度。方案2也只适用于只读情况,还要求你小心处理所有的关联。而且在许多情况下不宜一次性读入所有相关数据(否则干嘛要lazy加载)。幸好,我们有方案3。
3. 重新attach
if (!session.user.attached) session.user.attach()
if (session.user.canDo(actionName)) {
...
}
Grails的domain class上的attach方法,可以把一个持久化对象重新attach到当前的Hibernate Session中。
类似的方式还有调用domainInstance.refresh(),但它会强制重新读取数据库,所以通常不应使用。
还有domainInstance = domainInstance.merge(),它相当于detached版本的save()。注意merge返回一个新对象,所以要重新给引用赋值。
就我自己的案例来说,由于user对象是每次都要用的,所以我最后使用了Grails filters来重新attach:
class AuthFilters {
def filters = {
login(controller:'*', action:'*') {
before = {
if (controllerName && controllerName != 'auth' && !session.user) {
redirect(controller:'auth', action:'login',
params:[url:request.forwardURI])
return false
} else if (session.user && !session.user.attached) {
log.debug "reattach ${session.user}"
session.user.attach()
}
}
}
}
}
其他:
不过你还是有可能会在layout gsp中碰到类似的问题(我暂时还没遇到),据说是grails和sitemesh的整合bug,将在grails 1.2中解决。
分享到:
相关推荐
Grails 是一个基于 Groovy 语言的开源Web应用程序框架,它构建在Java平台之上,旨在简化开发过程并提高生产力。Grails 的设计深受Ruby on Rails的影响,提供了MVC(模型-视图-控制器)架构模式,允许开发者快速构建...
标题中提到的"Grails开发之(Rest教程)"表明本文是一份关于Grails框架下进行RESTful服务开发的教程。Grails是一个使用Groovy语言编写的高生产力的框架,其使用约定优于配置的理念,允许快速开发Web应用程序。...
Grails框架是建立在Groovy编程语言之上的一个完整的Web应用开发框架,它结合了Java平台的优势和动态语言的灵活性。Grails采用约定优于配置的原则,简化了开发流程,使开发者能够快速地构建高性能的Web应用程序。该...
2. **MVC架构**:Grails采用MVC模式组织应用程序,将业务逻辑、用户界面和数据访问分离,有利于团队协作和代码复用。在Grails中,Controller处理请求,View呈现结果,Model则存储和管理业务数据。 3. **GORM...
在Grails中开发RESTful API是一项常见的任务,这个文档提供了基于Grails 3.1.5版本的REST教程。Grails是一种基于Groovy语言的开源Web应用框架,它利用了Spring Boot的功能,使得构建现代互联网应用程序变得更加高效...
grails-2
- GORM是Grails的核心特性之一,用于处理对象与数据库之间的映射。 - 支持基本的CRUD操作。 **领域(Domain)建模** - 领域类是GORM的基础,代表数据库中的实体。 - 支持多种关联方式,如多对一、一对多等。 - 支持...
2. **Eclipse安装** - 由于Grails项目涉及GSP(Groovy Server Pages)文件,推荐使用包含JSP编辑器的Eclipse JEE版本。如果你对JSP语法熟悉且内存有限,也可选择Classic版本。 - 安装Eclipse 3.4.0 JEE版本或其他...
2. View:视图层负责展示数据,Grails支持多种模板引擎,如GSP(Groovy Server Pages),可以混合Groovy代码和HTML来构建动态页面。 3. Controller:控制器层接收用户请求,调用模型进行业务处理,并决定视图如何...
《Grails脚手架二次优化详解》 在Web开发领域,Grails框架以其高效和便捷的特性深受开发者喜爱。其中,脚手架(Scaffolding)是Grails提供的一种快速开发工具,它能够自动生成基本的CRUD操作,极大地提高了开发效率...
《Grails从入门指南(第二版)》是一本专为初学者设计的全面教程,旨在帮助读者快速掌握Grails框架的基础知识和高级特性。Grails是一个基于Groovy语言的开源Web应用开发框架,它简化了Java平台上的开发过程,提供了...
2. **安装Grails插件**:有了GroovyEclipse的支持,我们还需要安装Grails插件。同样地,可以通过Eclipse的"Help" -> "Install New Software",添加Grails插件的更新站点(如:...
Grails 入门教程(二) 一、建立域关系 在 Grails 框架中,建立域关系是指定义对象之间的关系。一个域对象可以拥有多个其他域对象的引用,这些引用可以是“一对一”、“一对多”或“多对多”的关系。例如,在实例...
[Apress] Grails 2 权威指南 (英文版) [Apress] The Definitive Guide to Grails 2 (E-Book) ☆ 出版信息:☆ [作者信息] Jeff Scott Brown, Graeme Rocher [出版机构] Apress [出版日期] 2013年01月23日 ...
#### 二、Grails 的核心特性 - **面向领域语言 (DSL)**:Grails 提供了一种面向领域的语言,让开发者能够用更少的代码完成更多的工作。 - **自动配置**:Grails 自动处理很多配置细节,如数据源、日志管理等,减少...
Grails 的强大之处在于其丰富的插件库,如Spring Security用于安全控制,Hibernate Search提供全文搜索功能,以及各种用于支付、邮件发送、社交网络集成的插件,极大地扩展了框架的功能。 6. **IDE集成** ...
3. Convention over Configuration(CoC):Grails的核心理念之一就是“约定优于配置”,这意味着开发者在很多情况下不需要写大量的配置文件,框架会自动根据约定进行工作。 二、Grails中文文档的价值 1. 学习入口...
Grails是一个基于Groovy语言的全栈框架,它遵循约定优于配置的原则,并且紧密集成Spring和Hibernate等流行的Java库,简化了开发流程。Grails在IT行业中尤其受到重视,因为它能够帮助开发者快速搭建并部署基于MVC模式...