论Grails-

最近用Grails 0.3.1/0.4写了一个小应用耍耍,主要想感受一下grails的快速开发能力,顺便尝尝Grooy的语法糖。

刚开始的几天,写了一个简单的模块,只有几个domain class和几个controller,也没有写test case,Grails 还在0.3.1,使用下来觉得Grails非常爽,尤其是Groovy的语法糖,那些closure, each操作,简直是爽坏了。

过了几天,业务开始复杂起来,需要整合acegi了,于是碰到了第一个问题,pojo service和POGO service/domain 互操作的问题。

再过了几天,grails升级到0.4了,业务也更加复杂了,添加了几个自己的POGO service. 用户,角色,权限,自定义的acege voter...多对多关系终于出现了,单元测试不得不写了。。。于是感觉到用Grails开发的问题越来越严重了。。。

下面是我碰到的问题列表,由于我是初次使用GRails,如果有谁发现其中的错误请不吝指出,谢谢!

(1) POJO service / Grails service 互相操作不方便.假如我在某个java service中需要注入一个Groovy写的service,那么,这个Service Type究竟该是什么? 按照[http://www.grails.org/Services 官方的doc],需要我们定义一个java interface, 然后让POGO的Service来implements该接口,然后就可以通过request.getSession().getServletContext().getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT).getBean(...)来获得POGO Service.

看起来挺不错的,仔细一想,该操作根本必须让POJO Service自行resolve需要的POGO Service dependency, 这不是忽悠人么.还算啥IoC啊. 还有,在POJO里面通常不大可能有request对象哈? 实现ContextAware? 不管用! 嘿嘿一般人我可不告诉他啊,您必须得让POJO Service继承WebApplicationObjectSupport,才能获得servletContext...然后获得该bean.估计是POGO Service的Scope有问题,或者可能和POJO Bean不在同一个level. 于是一番折腾下来,我们的POJO变得面目全非,人不人鬼不鬼咯.

有一个现成的例子是Grails与Acegi的整合,我们必须提供一个GrailsUserDetailService实现,这是一个Pojo Service. Acegi需要调用该Service来获得ACEGI中UserDetails的信息,同时该Pojo Service需要通过操作grails domain object, 比如User.findByUsername()来lookup domain classes,于是里面就有很多有趣的代码了,比如invokeMethod('foo','args)....

(2) Grails run-app/test-app 启动非常慢, 在P1.9M的laptop上面从输入命令到可以运行,至少30s. 这是很严重的问题,我们严重依赖TestCase来保证代码的行为,为了调试代码中的问题,必须不停的跑测试,调试,再跑测试. JUnit跑一次通常< 5s,而这里每次run testcase都需要30s,这是无法忍受的. 这篇doc就是在grails test-app的等待间歇中写出来的...

(3) Domain Class Validation的不足.据一个简单的场景创建用户.输入数据验证除了简单的字串长度检测外, 还有一个额外需求, 界面需要用户2次输入密码并且一致后,才能创建用户. 这个场景需要同时提供Form端的验证和DomainClass端的验证,而password confirm验证必须在Form端完成.由于目前Grials仅支持在domain class上的验证,所以我们只能在controller里面用类似的代码来验证:
{{{
#!python
//FIXME we need do validation against the form data instead of the domain class
  if(!params.password.equals(params.password_confirm)){
    flash['error'] = "The password must match"
    return params
  }
}}}

(4) Grails0.4 不支持domain/controller里面的subfolder,这也是很必要的一个功能, 试想写Java代码但是不能用package,所有类都在一个目录里...受不了

(5) Plugin机制还不够完善,灵活性不够,反而大大降低了系统性能. Domain class无法在APP中使用, gsp文件无法在app中被正确load...

(6) GORM mapping有限制,复杂应用还是得靠手动些Java Class/Annotation/Mapping file来完成.如果domain class变成pojo,由于这些entity是由Hibernate创建的,dependency injection就成了问题这些东西就变成了Entity,至少不再是rich domain object咯.另外,问题(1)也会随之而来.

(7) 映射关系复杂到一定程度, 在save entity object graph的时候,就会碰到Hibernate中著名的jdbc batch execution exception问题.而我们无法在Service中处理tx,session. 新增加的withTransaction{} closure也只是给你一个spring transactionManager,你只能savePoint() or setRollbackOnly()而无法操控整个TX
以下代码是一POGO Service Test Case,其中创建一系列POGO,save,然后再调用userService load entity.我们需要用一个Session来创建并save这些POGO,然后另开一个Session来load这些POGO进行验证.但似乎testcase缺省的用了OpenSessionInView pattern,从头到尾只有一个Session.... 哪怕让我session.evict()也好啊....

(8) Domain class 的delete() 方法永远返回null,所以无法判断删除操作是否成功

----
2007-02-06 继续批判

(9) Reload机制太弱.只有在controller/view上的代码改动,才可以reload. 如果Domain class被改动了,会报Unable to re-create configuration for reloaded class 500 错误. 同样修改过的transactional sevice也不能reload. [[br]] 按照grails doc的说法,
{{{
Domain classes are core to any business application. They hold state about business processes and hopefully also implement behavior.
}}}
{{{
A service is a class that holds one or more methods that implement business logic. Logical parts of the business logic are contained in separate service classes. Services are demarcated by transactions to make units of work atomic on the persistence level. When certain types of exceptions are thrown transactions are rolled back.
}}}
毫无疑问,domain和service中将含有绝大部分的业务逻辑代码,而你现在稍微一改动代码就要重新运行grails run-app,运行一次就是几十秒. 对有过TDD经历人,这种等待绝对是折磨!

你可能感兴趣的:(bean,Hibernate,grails,groovy,Acegi)