精通 Grails: 使用 Ajax 实现多对多关系
来源:互联网 发布:flash cs6 for mac版 编辑:程序博客网 时间:2024/06/05 22:54
在 Web 应用程序中,多对多(m:m)关系很难处理。在 精通 Grails 系列的这一期文章中,Scott Davis 将向您展示如何在 Grails 中成功实现 m:m 关系。了解如何通过 Grails 对象关系映射(Grails Object Relational Mapping,GORM)API 和后端数据库处理多对多关系。学习如何使用 Ajax(Asynchronous JavaScript + XML)流线化用户界面。
软件开发就是使 用代码来模拟现实世界。例如,书籍都有作者和出版商。在 Grails 应用程序中,要为每个元素创建一个域类。GORM 为每个类创建对应的数据库表,搭建功能(scaffolding)提供基本的 Create/Retrieve/Update/Delete (CRUD) Web 界面。
接下来定义这些类之间的关系。一个出版商通常会出版多部图书,因此出版商和他的图书之间的关系就是一个简单的一对多(1:m)关系:一个 Publisher
出版多个 Book
。通过在 Publisher
类中加入 static hasMany = [books:Book]
,创建 1:m 关系。在 Book
类中放入 static belongsTo = Publisher
,可以向关系添加另一个方面 —— 级联更新和删除。如果删除一个 Publisher
,所有对应的 Book
也会被删除。
很容易在底层数据库中模拟 1:m 关系型。每个表有一个用作主键的 id
字段。当 GORM 向 book 表添加一个 publisher_id
字段时,就在两个表之间建立了一个 1:m 关系。在前端,Grails 也能够很好地处理 1:m 关系。创建一个新 Book
时,自动生成的(搭建而成)HTML 表单提供一个下拉组合框,将您的选择限制在现有的 Publisher
列表中。自本系列的 第一篇文章 以来,我们已经展示了许多 1:m 关系的示例。
现在来看看一个稍微复杂一些的关系 — 多对多(m:m)关系。与 Book
和 Publisher
之间的关系相比,模拟 Book
与 Author
之间的关系要复杂得多。一部图书可以有多个作者,一个作者也可以编写多部图书。这是一个典型的 m:m 关系。在真实世界中,m:m 关系很常见。一个人可以有多个支票帐号,一个支票帐号也可以由多个人来管理。一个顾问可以为多个项目工作,一个项目也可以有多个顾问。本文将向您展示如何 使用 Grails 实现 m:m 关系,我们在本系列中开发的 trip-planner 应用程序上进行构建。在讨论 trip-planner 应用程序之前,我还想谈谈图书的例子,以帮助您理解一个要点。
第三个类
在数据库中,用三个表表示 m:m 关系:前两个表您已经知道了(Book
和 Author
),第三个是一个连接表(BookAuthor
)。GORM 向 BookAuthor
连接表添加 book_id
和 author_id
,而不是向 Book
或 Author
表添加一个外键。连接表允许将具有一个作者的图书和具有多个作者的图书持久化。它还能够表示编写多部图书的作者。Author
和 Book
外键的每种惟一组合都会在连接表中有惟一的记录。这种方法具有极大的灵活性:一本书可以有任意数量的作者,而一个作者可以编写任意数目的图书。
Dierk Koenig 曾经告诉我,“如果您认为只有两个对象共享一个简单的多对多关系,那么您对多对多关系的了解还不够透彻。还存在第三个对象,这个对象有其自己的属性和生命周期”。确实如此,Book
和 Author
之间的关系已经超出了简单连接表的范围。例如,Dierk 是 Groovy in Action(Manning Publications,2007 年 1 月)的第一作者。第一作者的身份应该表示为 Author
与 Book
之间的关系中的一个字段。其他各种因素也应如此:作者会按特定顺序在封面上列出;每个作者编写图书的特定章节;而且每个作者的报酬也因贡献不同而异。您可以看到,Author
与 Book
之间的关系比最初计划的更加微妙。在真实世界中,每个作者都会签订一份合同,使用明确的条款详细描述他与这部图书之间的关系。或许应该创建一个一级类 Contract
来更好地表示 Book
与 Author
之间的关系。
简单来讲,这意味着一个 m:m 关系实际上就是两个 1:m 关系。如果两个类有可能共享一个 m:m 关系,那么您应该更深入地研究一下,确定出具有两个 1:m 关系的第三个类并显式地定义它。
回页首
模拟航空公司和机场
现在回到 trip-planner 应用程序,首先回顾一下域模型,并查看有没有潜在的 m:m 关系。在 第一篇文章 中,我创建了一个 Trip
类,如清单 1 所示:
清单 1.
Trip
类
class Trip {
String name
String city
Date startDate
Date endDate
}
在 第二篇文章 中,我添加了一个 Airline
类(如清单 2 所示),以演示一个简单的 1:m 关系:
清单 2.
Airline
类
class Airline {
static hasMany = [trip:Trip]
String name
String frequentFlier
}
这些类在当时具有其特定的用途 — 用于说明某一点的简单占位符 — 但是它们并不是一个严格的域模型。现在对最初的类进行改进,并添加一些更加健壮的内容。
我使用以前的方法创建了 Trip
类,因为当时看起来还不错。我当时说 “我正计划到芝加哥旅行” 或 “我计划在下个月 15 号到 20 号去纽约”。city
、startDate
和 endDate
这样的字段似乎是 Trip
的自然属性。但是,现在看来,Trip
可能还会涉及到更多因素。
我 住在科罗拉多州丹佛市 — 美国联合航空公司的中心城市。这意味着我通常可以直接飞到最终的目的地,但有时候需要中转多次。有时候一次旅行涉及到多个城市:“我正飞往波士顿,星期一 到星期五我要在那里教课。当我在东海岸时,我需要在华盛顿附近参加星期六的一场会议。我将在星期天下午飞回来”。即使我幸运地找到了到一个特定城市的直达 航班,而且我不会飞到其他城市,我的旅行涉及到的航班仍然不止一次 — 飞往目的地的航班和返回的航班。一个 Trip
可以包含多个 Flight
。清单 3 定义 Trip
与 Flight
之间的关系:
清单 3.
Trip
与 Flight
之间的 1:m 关系
class Trip{
static hasMany = [flights:Flight]
String name
}
class Flight{
static belongsTo = Trip
String flightNumber
Date departureDate
Date arrivalDate
}
请记住,使用 belongsTo
字段设置关系意味着,如果删除 Trip
,也会删除所有相关的 Flight
。如果我为空中交通管制员构建一个系统,我可能希望制定不同的架构决策。或者如果我尝试为同乘一个航班的多位乘客构建一个系统(一个 Flight
可以有多个 Passengers
,一个 Passenger
也可以有多个 Flights
),那么将一个航班绑定到一个特定的乘客可能是一个问题。但是我不会尝试为数百万乘客模拟全世界每天运行的数千次航班。在我的简单例子中,一个 Flight
的所有任务就是进一步描述一个 Trip
。如果对于我来说,某个 Trip
不再重要,那么每个对应的 Flight
也是如此。
现在,我应该使用 Airline
类做什么呢?一个 Trip
可能涉及到多个不同的 Airline
,而一个 Airline
又可以用于多个不同的 Trip
。这两个类之间是一种明确的 m:m 关系,但是 Flight
似乎是添加 Airline
的恰当位置,如清单 4 所示。一个 Airline
可以有多个 Flight
,而一个 Flight
只能有一个 Airline
。
清单 4. 将
Airline
与 Flight
关联
class Airline{
static hasMany = [flights:Flight]
String name
String iata
String frequentFlier
}
class Flight{
static belongsTo = [trip:Trip, airline:Airline]
String flightNumber
Date departureDate
Date arrivalDate
}
您应该注意到两点。首先,Flight
中的 belongsTo
字段由一个值转变为多个值的散列映射(hashmap)。一个 Trip
可以有多个 Flight
,一个 Airline
也可以有多个 Flight
。
其次,我向 Airline
添加了一个新的 iata
字段。这个字段用于填写 International Air Transport Association (IATA) 编码。IATA 为每个航空公司分配了一个惟一的编码 — UAL 表示 United Airlines、COA 表示 Continental、DAL 表示 Delta,等等(参见 参考资料,获取 IATA 编码的完整列表)。
最后,您应该注意到我制定了另一个架构决策,这次加入了 Airline
和常飞顾客(frequent-flier)编号之间的关系。由于我假设只有一个用户使用这个系统,因此可以将 FrequentFlier
作为 Airline
类的一个属性。每个航空公司最多只有一个 frequent-flier 编号,所以这是最简单的可能的解决方案。如果这次旅行计划的需求发生了更改,而且我需要支持多个用户,那么就出现了另一个 m:m 关系。一个乘客可以有多个 frequent-flier 编号,一个航空公司也可以有多个 frequent-flier 编号。创建一个连接表来管理这个关系会非常适合。现在我将使用简单的解决方案,但是如果需求发生了更改,我会将 FrequentFlier
字段标记为未来的一个重构点。
本文转自:IBM developerWorks 中国
请点击此处查看全文
- 精通 Grails: 使用 Ajax 实现多对多关系
- 使用 Ajax 实现多对多关系
- Hibernate入门到精通-关系映射多对一
- grails的一对一、一对多关系
- 精通 Grails: RESTful Grails
- grails 在一对多关系中如何排序多对象
- grails 设置一对多(hasMany belongsTo)级联关系
- hibernate实现多对多的关系
- hibernate[注解]实现[多对多]关系
- sqlalchemy多对多关系实现
- Grails国际化--多语种动态切换实现
- grails多条件搜索功能实现1
- 例子描述:grails jquery 实现ajax
- GRAILS AJAX
- 多对多关联关系的使用
- 精通用Grails实现面向资源服务的架构
- 精通Grails系列网址
- mybatis使用resultMap实现多对多查询 (需求:商品信息和订单明细有多对多的关系)
- linux _FTP 命令
- 诗人状告作协法律浅析
- 用EasyMF 开发GTalk Mobile 客户端
- 在 Linux 平台中调试 C/C++ 内存泄漏方法
- 通过SQL语句获取ORACLE的版本号
- 精通 Grails: 使用 Ajax 实现多对多关系
- Apache 2.0.55 无法加载 libphp5.so 模块
- Java多线程编程详解
- 技巧(6)- Datawindow中每页打印固定行
- 不定参数的应用
- 体系结构实践:第 6 部分,为何业务流程管理(BPM)对企业非常重要
- 开天辟地第一篇
- jsp表单用图标实现form的重置和提交
- 读代码思考