Grails 1.2参考文档速读(6):GORM基础和关系建模

如今,ORM几乎是所有企业开发项目的标配,在Grails中,它的ORM解决方案是GORM。
GORM的基础是Hibernate3,虽然GORM已经极大简化了Hibernate的使用,但是要想真正用好GORM,对于Hibernate的理解是必须的。
为了对GORM有个感性认识,我们先来看看如何在Grails中实现对数据的CRUD操作。首先,我们可以简单地认为,凡是要保存到数据库中对象都要实现为Grails的Domain Class。Domain Class的创建过程如下:
1. 使用grails create-domain-class命令,如:
grails create-domain-class person
2. 创建完成的Domain Class保存在grails-app/domain目录下。
3. 接下来就是编辑产生的Domain Class,使之符合我们的要求:
class Person { 
     String name
     Integer age
     Date lastVisit
    }
现在,Domain Class已经创建完毕,该是考虑如何把Domain Class保存到数据库的时候了。典型的数据库操作是C(增)、R(读)、U(改)、D(删)。如果你是第一次接触Grails,相信你会有一种在黑暗的Java开发中看到光明的感觉:
1. 创建(C)
def p = new Person(name:"Fred", age:40, lastVisit:new Date())
    p.save()
2. 读取(R)
//for修改
    def p = Person.get(1)
    //for只读,此时Hibernate将不会进行“脏检查”
    def p= Person.read(1)
3. 更新(U)
def p = Person.get(1)
    p.name = "Bob"
    p.save()
4. 删除(D)
def p = Person.get(1)
    p.delete()
借助Groovy语言的动态特性,在Grails中完全消除了写DAO的必要性。要是你还有什么疑惑的话,那肯定是会“我没有定义那些方法呀,怎么自己就突然冒出来了?”,别着急,跟着这个系列,你就会慢慢清楚的;)。现在,你只需要记住,凡是Grails中的Domain Class,都可以直接调用那几个方法。
看完CRUD操作,就应该实际了解一下在Grails中创建Domain Class的相关知识的事情了。因为在现实项目中,Domain Class通常不会一个个独立存在,与其他Domain Class不发生任何关系;此外,对于Hibernate熟悉的读者也会知道Hibernate是支持组件、继承、集合的,这些在Grails中又是如何实现的呢?
首先,让我们来回顾一下我们对于Hibernate的一些建模方面的基础知识:
  1. 关系是有方向的:有单向和双向之分,而且存在于定义侧
  2. 只要有关系存在,操作是可以级联的:典型的级联操作有增加、更新、删除等
  3. 典型的关系有:one-to-one、many-to-one、one-to-many、many-to-many。
使用过Hibernate的开发人员对于既要写Java类又要编辑映射文件应该不会太陌生,虽然后面Hibernate也推出了映射的Annotation,但是依然显得繁琐。在Grails中,这些统统都得到了简化,很多时候,你只需要在Domain Class中去书写Groovy代码就可以了。
对于类之间的关联定义,Grails中主要有这么几种:
  • 引用1个对象:
    Nose nose
        或
        //通过nose引用
        static hasOne = [nose:Nose]
  • 引用多个对象:hasMany
    //通过books引用
        static hasMany = [ books : Book ]
  • 级联:belongsTo
    //只定义级联
        static belongsTo = Face
        //定义引用对象且级联删除
        static belongsTo = [face:Face]
  • 如果存在有多个属性映射到同一对象:mappedBy
考虑到关系的方向性以及级联,对于one-to-one的映射大概有:
  1. 单向(非级联):
    class Face {
            Nose nose
            static constraints = {
          nose unique:true
         }
        }
        class Nose { 
        }
  2. 单向(级联):
    class Face {
            Nose nose
     static constraints = {
      nose unique:true
     }
        }
        class Nose { 
         static belongsTo = Face
        }
  3. 双向(非级联):
    class Face {
            Nose nose
            static constraints = {
          nose unique:true
         }
        }
        class Nose { 
         Face face
         static constraints = {
          face unique:true
         }
        }
  4. 双向(级联):
    class Face {
            Nose nose
            static constraints = {
      nose unique:true
     }
        }
        class Nose { 
         static belongsTo = [face:Face]
            static constraints = {
      face unique:true
     }
        }
one-to-one还有一种定义关联的属性:hasOne,使用它时,必须是双向的:
//5
    class Face {
        static hasOne= [nose: Nose]
    }
    
    class Nose {
        Face face
    }
one-to-one对产生的关系表的影响:
  • 1、2、4:face表中有一个外键node_id,指向node表
  • 3:face和node表中分别有一个外键,分别指向对方
  • 5:node表中有一个外键face_id,指向face表
在Grails中,不必明显的定义出many-to-one,因为引用一个对象就有这一层意思在里面了。而定义one-to-many,则是使用hasMany来完成的,但与hasOne不一样,它的使用不必是双向的。即你可以只使用hasMany,而另一端无需定义任何关联属性。one-to-many同样有以上那几种情况,使用完全类似,因此这里就不一一列出,在此只列出双向/级联的情况:
class Author {
        static hasMany = [ books : Book ]
        String name
    }
    class Book {
     static belongsTo = [author:Author]
     String title
    }
说belongsTo定义了级联并不完全正确,它实际定义的是级联删除。如果不定义,缺省级联特性为插入和更新。
one-to-many对产生关系表的影响,其中(A和B是一对多关系):
  • A -> B:A、B和A_B,A_B分别和A、B关联
  • B -> A:A、B,B中有A的主键
  • A <-> B:A、B,B中有A的主键
对于以上第一种情况,表示的是A和B之间可选的一对多关系,即B有可能没有A。对于这种情况,就需要引入关联表。关于这部分的讨论,请参见《Java Persistence with Hibernate》。
最后一种关系类型就是many-to-many了,它必然是双向的,而且一方必须指定belognsTo:
class Book {
       static belongsTo = Author
       static hasMany = [authors:Author]
       String title
    }
    class Author {
       static hasMany = [books:Book]
       String name
    }
many-to-many最终产生的是关联表,此外,这里需要纠正一下文档的错误: Grails从1.1起就已经支持many-to-many的scaffolding了
以上的关联定义都只是一个关系定义对应一个关联属性,但在项目中可能还会出现其他情况:
  • 多属性、单关系:
    class Airport {
         static hasMany = [flights:Flight]
         static mappedBy = [flights:"departureAirport"]
        }
        class Flight {
         Airport departureAirport
         Airport destinationAirport
        }
  • 多属性、多关系:
    class Airport {
         static hasMany = [outboundFlights:Flight, inboundFlights:Flight]
         static mappedBy = [outboundFlights:"departureAirport"
                        , inboundFlights:"destinationAirport"]
        }
        class Flight {
         Airport departureAirport
         Airport destinationAirport
        }

你可能感兴趣的:(职场,grails,休闲,gorm)