grails-数据库编程(高级GORM特性)

江佐
2023-12-01
一:事件和自动实现时间戳
   GORM支持事件注册,只需要将事件作为一个闭包即可,当某个事件触发,比如删除,插入,更新。为了添加一个事件需要在你的领域
   类中添加相关的闭包。
   (1):事件类型
      A:beforeInsert事件
         触发当一个对象保存到数据库之前
         class Person {
            Date dateCreated
            def beforeInsert = {         dateCreated = new Date()       }
          }
      B: beforeUpdate事件
         更新前事件
         class Person {
            Date dateCreated
            Date lastUpdated
            def beforeInsert = { dateCreated = new Date() }
            def beforeUpdate = { lastUpdated = new Date() }
           }
        C:beforeDelete事件
          触发但一个对象被删除
          class Person {
             String name
             Date dateCreated
             Date lastUpdated
             def beforeDelete = {      new ActivityTrace(eventName: "Person Deleted",data:name).save()    }
            }
       D:onLoad事件
         触发当一个对象从数据库取出
         class Person {
           String name
           Date dateCreated
           Date lastUpdated
           def onLoad = {   name = "I'm loaded"   }
         }
    (2):自动时间戳
       上面的例子演示了使用事件来更新一个 lastUpdateddateCreated属性来跟踪对象的更新。事实上,这些设置不是必须的。通过
        简单的定义一个 lastUpdateddateCreated属性 ,GORM会自动的为你更新。
        如果,这些行为不是你需要的,可以屏蔽这些功能。如下设置
        class Person {
           Date dateCreated
           Date lastUpdated
           static mapping = { autoTimestamp false }
         }
  二: 自定义ORM映射

       Grails 的域对象可以映射到许多遗留的模型通过 关系对象映射域语言。接下来的部分将带你领略它是可能的通过ORM DSL这是必要
       的,如果你高兴地坚持以约定来定义GORM对应的表,列名等。你只需要这个功能,如果你需要定制GORM 映射到遗留模型或进行
       缓存自定义映射是使用静态的 mapping块定义在你的域类中的:
       class Person {
          ..
          static mapping = {
          }
        }
       (1) :表名和列名

          A:表名
             类映射到数据库的表名可以通过使用 table关键字来定制
             class Person {
                ..
                static mapping = {    table 'people'     }
              }
             在上面的例子中,类会映射到 people表来代替默认的 person
         B:列名
             同样,也是可能的定制某个列到数据库。比如说,你想改变列名例子如下
             class Person {
                String firstName
                static mapping = {    table 'people' firstName column:'First_Name'    }
              }
             在这个例子中,你定义了一个 column块,此块包含的方法调用匹配每一个属性名称(本例子中是 firstName),接下来使用命
             名的 column来指定字段名称的映射
          C:列类型
             GORM还可以通过DSL的type属性来支持Hibernate类型,包括特定Hibernate的 org.hibernate.types.UserType的子类。比如,有
             一个PostCodeType,你可以象下面这样使用:
             class Address {
                String number
                String postCode
                static mapping = {    postCode type:PostCodeType    }
             }
            另外如果你想将它映射到Hibernate的基本类型而不是Grails的默认类型,可以参考下面代码:
            class Address {
               String number
               String postCode
               static mapping = {     postCode type:'text'      }
            }
          上面的例子将使postCode列映射到数据库的SQL TEXT或者CLOB类型
   三:映射关系
        A:一对一映射
           在关联中,你也有机会改变外键映射联系,在一对一的关系中,对列的操作跟其他常规的列操作并无二异,例子如下
           class Person {
              String firstName
              Address address
              static mapping = {   table 'people' firstName
                                             column:'First_Name'
                                             address column:'Person_Adress_Id'
                                          }
                }
               默认情况下, address将映射到一个名称为address_id 的外键。但是使用上面的映射,我们改变外键列为 Person_Adress_Id
         B:一对多映射
             在一个双向的一对多关系中,你可以象前节中的一对一关系中那样改变外键列,只需要在多的一端中改变列名即可。然而,在单
             向关联中,外键需要在关联自身中(即一的一端-译者注)指定。比如,给定一个单向一对多联系 PersonAddress,下面的代
             码会改变 address表中外键
             class Person {
                String firstName
                static hasMany = [addresses:Address]
                static mapping = {    table 'people'
                                                firstName column:'First_Name'
                                               addresses column:'Person_Address_Id' 
                                           }
                 }
                如果你不想在 address表中有这个列,可以通过中间关联表来完成,只需要使用joinTable参数即可:
                class Person {
                   String firstName
                   static hasMany = [addresses:Address]
                   static mapping = {
                                                table 'people'
                                                firstName column:'First_Name'
                                                addresses joinTable:[name:'Person_Addresses', key:'Person_Id', column:'Address_Id']
                                               }
                  }
             C:多对多映射
                 默认情况下,Grails中多对多的映射是通过中间表来完成的,以下面的多对多关联为例:
                 class Group {
                     …
                    static hasMany = [people:Person]
                  }
                 class Person {
                    …
                    static belongsTo = Group
                    static hasMany = [groups:Group]
                 }
                 在上面的例子中,Grails将会创建一个 group_person表包含外键 person_idgroup_id对应 persongroup表。假如你需
                 要改变列名,你可以为每个类指定一个列映射
                 class Group {
                    …
                     static mapping = {         people column:'Group_Person_Id'         }
                 }
                 class Person {
                    …
                    static mapping = {    groups column:'Group_Group_Id'    }
                 }
                你也可以指定中间表的名称
                class Group {
                    …
                    static mapping = {     people column:'Group_Person_Id',joinTable:'PERSON_GROUP_ASSOCIATIONS'         }
                 }
                class Person {
                   …
                   static mapping = {     groups column:'Group_Group_Id',joinTable:'PERSON_GROUP_ASSOCIATIONS'           }
                 }
   四:缓存策略
        设置缓存
            hibernate 本身提供了自定义二级缓存的特性,这就需要在 grails-app/conf/DataSource.groovy文件中配置:
            hibernate { cache.use_second_level_cache= true
                              cache.use_query_cache= true
                              cache.provider_class='org.hibernate.cache.EhCacheProvider'
                            }
            当然,你也可以按你所需来定制设置。比如,你想使用分布式缓存机制
         缓存实例
            假如要在映射代码块中启用缺省的缓存,可以通过调用 cache方法实现:
            class Person {
                ..
                static mapping = {      table 'people' cache true     }
             }
            上面的例子中将配置一个读-写(read-write)缓存包括lazy和non-lazy属性。假如你想定制这些特性,你可以如下所示:
             class Person {
                 ..
                 static mapping = {      table 'people' cache usage:'read-only', include:'non-lazy'      }
              }
          缓存关联对象
             就像使用Hibernate的二级缓存来缓存实例一样,你也可以来缓存集合(关联),比如:
             class Person {
                String firstName
                static hasMany = [addresses:Address]
                static mapping = { table 'people' version false addresses column:'Address', cache: true }
             }
             class Address {
                String number String postCode
             }
            上面的例子中,我们在addresses集合启用了一个读-写缓存,你也可以使用
             cache:'read-write' // or 'read-only' or 'transactional'
             更多配置请参考缓存用法
            缓存用法
   下面是不同缓存设置和他们的使用方法
  • read-only - 假如你的应用程序需要读但是从不需要更改持久化实例,只读缓存或许适用
  • read-write - 假如你的应用程序需要更新数据,读-写缓存或许是合适的
  • nonstrict-read-write - 假如你的应用程序仅偶尔需要更新数据(也就是说,如果这是极不可能两笔交易,将尝试更新同一项目同时)并且时进行) ,并严格交易隔离,是不是需要一个非严格-读写缓存可能是适宜的
  • transactional - transactional缓存策略提供支持对全事务缓存提供比如JBoss的TreeCache。这个缓存或许仅仅使用在一个JTA环境,同时你必须在grails-app/conf/DataSource.groovy文件中配置hibernate.transaction.manager_lookup_class

   五: 自定义数据库标识符

        你可以通过DSL来定制GORM生成数据库标识,缺省情况下GORM将根据原生数据库机制来生成ids,这是迄今为止最好的方法,但
        是仍存在许多模式,不同的方法来生成标识。
        为此,Hibernate特地定义了id生成器的概念,你可以自定义它要映射的id生成器和列,如下:
        class Person {
          ..
          static mapping = {
                                         table 'people' version false
                                         id generator:'hilo', params:[table:'hi_value',column:'next_value',max_lo:100]
                                     }
        }
        在上面的例子中,我们使用了Hibernate内置的'hilo'生成器,此生成器通过一个独立的表来生成ids。此外还有许多不同的生成器可以
        配置,具体参考Hibernate在这个主题上的相关文档。
        注意,如果你仅仅想定制列id,你可以这样
        class Person {
            ..
            static mapping = {    table 'people' version false
                                            id column:'person_id'
                                       }
         }
   :复合主键
       GORM支持复合标识(复合主键--译者注)概念(标识由两个或者更多属性组成)。这不是我们建议的方法,但是如果你想这么做,
       这也是可能的:
       class Person {
          String firstName String lastName
          static mapping = {       id composite:['firstName', 'lastName']        }
       }
      上面的代码将通过 Person类的 firstNamelastName属性来创建一个复合id。当你后面需要通过id取一个实例时,你必须用这个对
      象的原型
      def p = Person.get( new Person(firstName: "Fred", lastName: "Flintstone"))
      println p.firstName
   七: 数据库索引
        为得到最好的查询性能,通常你需要调整表的索引定义。如何调整它们是跟特定领域和要查询的用法模式相关的。使用GORM的
        DSL你可以指定那个列需要索引
        class Person {
           String firstName
           String address
           static mapping = {    table 'people' version false
                                           id column:'person_id'
                                           firstName column:'First_Name', index:'Name_Idx'
                                           address column:'Address', index:'Name_Idx, Address_Index'
                                       }
         }
   八:乐观锁和版本定义
      就像在 乐观锁和悲观锁部分讨论的,默认情况下,GORM使用乐观锁和在每一个类中自动注入一个 version属性,此属性将映射数据
      库中的一个 version
      如果你映射的是一个遗留数据库(已经存在的数据库--译者注),这将是一个问题,因此可以通过如下方法来关闭这个功能:
      class Person {
         ..
         static mapping = {      table 'people' version false    }
      }
   九:立即加载和延迟加载
      延迟加载集合

        就像在 立即加载和延迟加载部分讨论的,默认情况下,GORM 集合使用延迟加载的并且可以通过 fetchMode来配置 。但如果你更喜
        欢把你所有的映射都集中在 mappings代码块中,你也可以使用ORM的DSL来配置获取模式:
        class Person {
           String firstName
           static hasMany = [addresses:Address]
           static mapping = {   addresses lazy: false   }
         }
         class Address {  
             String street
             String postCode  
          }
       延迟加载单向关联
          在 GORM中,one-to-one和many-to-one关联缺省是非延迟加载的。这在有很多实体(数据库记录-译者注)的时候,会产生性能
          问题,尤其 是关联查询是以新的SELECT语句执行的时候,此时你应该将one-to-one和many-to-one关联的延迟加载象集合那样进
          行设置:
          class Person {
             String firstName
             static belongsTo = [address:Address]
             static mapping = { address lazy: true // lazily fetch the address }
          }
          class Address {
              String street
              String postCode
          }
          这里我们设置 Personaddress属性为延迟加载
十: 事务编程

   Grails是构建在Spring的基础上的,所以使用Spring的事务来抽象处理事务编程,但GORM类通过 withTransaction方法使得处理更简  
   单,方法的第一个参数是Spring的 TransactionStatus对象
   典型的使用场景如下:
   def transferFunds = {  
                                        Account.withTransaction {   status ->
                                              def source = Account.get(params.from)
                                              def dest = Account.get(params.to)
                                              def amount = params.amount.toInteger()
                                              if(source.active) {
                                                 source.balance -= amount
                                                 if(dest.active) {
                                                    dest.amount += amount
                                                  } else {
                                                    status.setRollbackOnly()
                                                  }
                                               }
                                         }

                                  }

 在上面的例子中,如果目的账户没有处于活动状态,系统将回滚事务,同时如果有任何异常抛出在事务的处理过程中也将会自动回滚。假如你不想回滚整个事务,你也可以使用"save points"来回滚一个事务到一个特定的点。你可以通过使用Spring的SavePointManager接口来达到这个目的。

withTransaction方法为你处理begin/commit/rollback代码块作用域内的逻辑。
 类似资料: