Board logo

标题: GORM - 有趣的名称,严肃的技术(1) [打印本页]

作者: look_w    时间: 2018-8-8 10:08     标题: GORM - 有趣的名称,严肃的技术(1)

这篇文章的重点是使用 Grails 可以实现简化的另一领域:使用 Grail 对象关系映射(Grails Object Relational Mapping,GORM)API 进行持久化。我将首先介绍什么是对象关系映射器(object-relational mapper,ORM),以及如何创建一对多关系。然后将学习数据验证(确保应用程序不会出现无用信息输入/无用信息输出(garbage in/garbage out)问题)。然后将看到如何使用 Grails ORM 的领域特定语言(domain-specific language,DSL),使用 DSL 语句能够在幕后对普通的旧 Groovy 对象(plain old Groovy objects,POGO)的持久化方式进行微调。最后,将看到能够轻松地切换到另一个关系数据库。任何有 JDBC 驱动程序和 Hibernae 方言的数据库都受支持。
ORM 定义关系数据库出现于 20 世纪 70 年代末,但是软件开发人员至今依然在寻求有效的方法来存入和取出数据。当今软件的基础并不是多数流行数据库所使用的关系理论,而是基于面向对象的原则。
为此产生了一整套称为 ORM 的程序,用来缓解在数据库和面向对象的代码之间来回转移数据的痛苦。Hibernate、TopLink 和 Java 持久性 API(Java Persistence API,JPA)是处理这一问题的三个流行的 Java API(请参阅 ),不过它们都并不完美。这个问题如此持久(不是故意一语双关,而是实情),以至于有了自己专用的术语对象关系阻抗失谐(请参阅 )。
GORM 是在 Hibernate 上的一层薄薄的 Groovy 层。(我猜 “Gibernate” 不像 “GORM” 那样容易上口)。这意味着现有的所有 Hibernate 技巧仍然有用 — 例如,HBM 映射文件和标注得到全面支持 — 但这篇文章的重点是 GORM 带来的有趣功能。
创建一对多关系对于将 POGO 保存到数据库表所面临的挑战,很容易被低估。实际上,如果只是将一个 POGO 映射到一个表,那么工作相当简单 —POGO 的属性恰好映射到表列。但是当对象模型稍稍变复杂一点,例如有两个彼此相关的 POGO,那么事情将很快变得困难起来。
例如,请看上个月  中开始的旅行规划网站。显然,Trip POGO 在应用程序中有重要的作用。请在文本编辑器中打开 grails-app/domain/Trip.groovy(如清单 1 所示):
清单 1. Trip 类
1
2
3
4
5
6
7
8
class Trip {
  String name
  String city
  Date startDate
  Date endDate
  String purpose
  String notes
}




清单 1 中的每个属性都轻松漂亮地映射到 Trip 表中的对应字段。还记得在上一期的文章中说过,在 Grail 启动时,所有存储在 grails-app/domain 目录下的 POGO 都会自动创建对应的表。默认情况下,Grails 使用内嵌的 HSQLDB 数据库,但是到本文结束时,就能够使用自己喜欢的其他任意关系数据库。
旅程中经常要包含飞行,所以还应该创建一个        Airline 类(如清单 2 所示):
清单 2. Airline 类
1
2
3
4
5
6
class Airline {
  String name
  String url
  String frequentFlyer
  String notes
}




现在要将这两个类链接起来。为了计划一个通过 Xyz 航线到芝加哥的旅行,在 Groovy 代码中的表示方法与在 Java 代码中的表示方法相同 — 要在        Trip 类中添加一个 Airline 属性(如清单 3 所示)。这个技术称为对象组合(object composition)(请参阅 )。
清单 3.在 Trip 类中添加 Airline 属性
1
2
3
4
5
6
class Trip {
  String name
  String city
  ...
  Airline airline
}




对于软件模型来说,这种表示方法非常合适,但是关系数据库采取的表示方法略有不同。表中的每个记录都有一个惟一的 ID,称为主键。向 Trip 表添加一个 airline_id 字段,就能将一个记录与另一个记录链接在一起(在这个示例中,“Xyz航线” 记录与 “芝加哥旅行” 记录链接)。这称为一对多 关系:一个航线能够与多个旅行关联。(在 Grails 的联机文档中,可以找到一对一和多对多关系的示例,请参阅 。)
这样形成的数据库模式只有一个问题。您可能对数据库成功地进行了规范化(请参阅 ),但是现在表中的列与软件模型就失去了同步。如果将 Airline 字段替换成 AirlineId 字段,那么实现的细节(在数据库中持久化 POGO)就泄漏 到了对象模型。Joel Spolsky 将这种情况称为 抽象泄漏法则(Law of Leaky Abstractions)(请参阅 )。
GORM 有助于缓解抽象泄漏问题,它支持使用对 Groovy 有意义的方式表示对象模型,由 GORM 在幕后处理关系数据库的问题。但是正如即将看到的,如果需要,覆盖默认设置也很容易。GORM 并不是隐藏数据库细节的不透明的 抽象层,而是一个半透明的 层 — 它尝试在不进行用户干预的情况下执行正确的工作,但是如果用户需要对它的行为进行自定义,它也可以提供支持。这样它就提供了两方面的好处。
现在已经在 POGO 类 Trip 中添加了 Airline 属性。要完成一对多关系,还要在 Trip 这个 POGO 中添加一个 hasMany 设置,如清单 4 所示:
清单 4. 在 Airline 中建立一对多关系               
1
2
3
4
5
6
7
8
class Airline {
  static hasMany = [trip:Trip]

  String name
  String url
  String frequentFlyer
  String notes
}




静态的 hasMany 设置是个 Groovy 的 hashmap:键是 trip;值是        Trip 类。如果要在 Airline 类中设置额外的一对多关系,那么可以将逗号分隔的键/值对放在方括号内。
现在在 grails-app/controllers 中迅速创建一个 AirlineController 类(如清单 5 所示),这样就能看出新的一对多关系的效果:
清单 5. AirlineController class
1
2
3
class AirlineController {
  def scaffold = Airline
}




还记得在上一期的文章中说过 def scaffold 的功能是告诉 Grails 在运行的时候动态创建基本的 list()、save() 和 edit() 方法。它还告诉 Grails 动态创建 GroovyServer Page(GSP)视图。请确保        TripController 和        AirlineController 都包含        def scaffold。如果曾经因为输入 grails generate-all 在 grails-app/views 中生成过任何 GSP 工件,例如 trip 目录或者是 airline 目录,都应该删除它们。对于这个示例,需要确保既允许 Grails 动态搭建控制器,又允许它动态搭建视图。
现在域类和控制器类都已经就位,请启动 Grails。请输入        grails prod run-app 在生产模式下运行应用程序。如果一切正常,应该看到欢迎消息:
1
Server running. Browse to http://localhost:8080/trip-planner




在浏览器中,应该看到        AirlineController 和        TripController 链接。单击        AirlineController 链接,填写 Xyz 航线的详细信息,如图 1 所示:
图 1. 一对多关系:一方如果不喜欢字段按照字母顺序排序,也不用担心。在下一节就能改变这种方式。
现在新建一个旅程,如图 2 所示。请注意        Airline 的组合框。添加到        Airline 表的每个记录都在这里显示。不用担心 “泄漏” 主键 — 在下一节将会看到如何添加更具描述性的标签。
图 2. 一对多关系:多方




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0