[Grails] 《The Definitive Guide to Grails》 学习笔记一 (对应1-3章)
biomedinfo
2010-12-01
这个系列是本人作为一只菜鸟,仔细阅读该书后做的笔记。因为已经发布在博客上,所以就不再一一在此重复贴了。
-------------------------------------------
1. 在grails平台下,测试驱动开发的特别必要性:由于Groovy的动态本质,在编译时间并不能提供充分的检查,一些错误只有在运行时间才能被发现,包括方法的解析,因此在我们create-controller的时候,grails会一同生成test case。在grails平台下,需要很高的测试覆盖率,因此TDD是非常必要的。
2. Stub和Mock的概念:Stub是用于模仿真实环境中调用的方法的一些测试类,会返回硬编码的值;Mock基本和Stub一样,但它能使用 expect(预计)来判断方法被调用的次数是否符合设计时的估计。grails在测试运行时,自动把真实环境中servlet对象 HttpServletResponse转化成Spring的MockHttpServletResponse,这种对象具有一些帮助属性如 contentAsString,这样就能在test case里进行类似于assertEquals “Hello World!", controller.response.contentAsString的判断了。 在集成测试中不需要Mock,因为在集成测试中所有的动态方法如 save(),validate()都会被自动加载,和run-app一样;但是在单元测试中这些方法没有被加载,这时就需要通过在单元测试类的测试方法中首先利用类似于mockDomain(Song)的调用来模拟动态环境,使后续的song.validate()方法可用。
3. Domain类:它最重要的特性就是会被grails自动持久化和映射到物理层的数据表,这种映射关系称为ORM(Object-Relational Mapping),在grails平台提供的ORM叫GORM(Grails ORM),是基于Hibernate的。GORM一般会把Domain类的类名映射为表名,把类中的每个属性名映射为表中的一个列(字段)名并且按照每个属性的类型生成字段的类型,但也有特例,如关系到嵌入、继承等的复杂情况在后面会提到。
4. Domain类之间的关系:有特定的一些属性可以用于在Domain类里定义和其他Domain类的关系,例如一对一的关系可以直接在类中直接定义其他类的instance作为属性之一。 例如在Car这个Domain类中包含了汽车的所有零部件,而一辆汽车只有一个发动机,则形成一对一的关系,因此在 Car类属性中增加Engine engine即可,如果两者有从属关系,则用belongsTo属性来定义,该属性的值是一个map,其中的key是关系名,value是关联到的 Domain类名,例如汽车发动机属于汽车一部分,则可在Engine类中定义: static belongsTo = [car:Car] 而不是简单地增加Car car。这样就为Engine对象增加了一个car属性,指向该Engine对象所属的Car对象;如果不需要增加这个Car属性来从Engine实例去引用其所属的Car实例,可以不用map而直接用类表示: static belongsTo = Car。 两种形式的belongsTo属性可以用于关联删除,当某个Car实例被删除时,grails会自动删除所有belongsTo这个Car实例的对象,从而很好地保持数据一致性。
hasMany表示一对多的关系,必须用一个map来表示,例如一个唱片里含有多首单曲,则在唱片的类属性中有一个 static hasMany = [songs: Song, players: Player] 这样的对应关系,缺省的Song对象使用java.util.Set类保存,但可以显式声明为其他,如 SortedSet songs;
5. Controller的简化:不再需要实现ORM框架诸如DAO(Data Access Object)、JDBC连接、业务接口等,GORM为每个Domain类自动提供了 list(params),get(id),findAllByName(name)等方法,可以在Controller里直接调用,无需开发人员再去实现,这就是Groovy的metaProgramming优越性的一大体现。
6. flash对象:flash对象是一个临时性的用于存放消息的对象,采用flash.message="Hello World!"这样的方式即可输出一个消息框。
7. Controller与view的联系:在很多Controller代码中并没有使用render()明确view的名称,仅仅是return一些值或者 redirect到其他的action,但是grails的Convention over Configuration特性可以自动通过Controller所关联的Domain和action名称,自动关联到相应的.gsp
8. render和redirect的区别:redirect发出一个HTTP redirect,创建了一个全新的request到另一个action,而render只是对当前request的response选择一个view进行渲染。
9. Domain类中的error属性:这是org.springframework.validation.Errors接口的一个instance,支持对validate规则的验证中产生的error进行多种查询,例如 getAllErrors(),getErrorCount(),getFieldError(fieldName),getObjectName(),hasErrors() 等; validate()方法也可以被显式调用,如果有error出现,可以进行调整后再次提交validate(),在此之前,可以通过 clearErrors()方法先清除前一次validate产生的errors。
10. Transient属性:对于Domain类里一些不需要持久化的属性,可以把它们列入transients属性中,例如:static transients = ['confirmPassword','age'],在右边的String列表中出现的属性,都不会被持久化。 另外,有些通过get,set方法会自动在持久层产生的列,不一定在Domain类中有定义,也可以通过transients属性让GORM知道它不需要在数据表中增加相应的列进行持久化。
11. ORM DSL:对于3的例外情况,例如在已有的一个数据表上开发应用,表的列和Domain类定义不完全一致,grails提供了ORM 的一套DSL(Domain Specific Language,领域相关语言,针对某个特定领域的编程语言,这是相对于通用型语言如Java, Groovy来说的),利用Domain类里包含的mapping 属性来进行映射,例如
12. Domain类的继承:不宜过度继承,最多2-3层,层次越少越好。因为在持久化中有两种对待继承的方式: 一是把一棵继承树上的所有类都放在一张数据表中,这是grails的缺省方式,但是所有的subclass都不能有非空字段的限制,因为每个实例保存的时候都会有某些其他subclass的数据项是空的; 二是每个subclass都单独保存为一张数据表,这样就需要在查询的时候进行父类和subclass的join,会付出很多性能成本。
13. Domain类的嵌入:如果某个Domain类是belongsTo另一个类的,可以在另一个类中通过embedded属性,把这个类定义为另一个类的内嵌类,这样在持久化的时候,两个类的属性会被存放在一张数据表中,有可能提高查询的性能。例如: |