Grails 对象关系映射(Grails Object Relational Mapping,GORM)API 是 Grails Web 框架的核心部分之一。“精通 Grails:GORM - 有趣的名称,严肃的技术” 向您介绍了 GORM 的基础知识,包括简单的一对多关系。之后的 “使用 Ajax 实现多对多关系” 教您使用 GORM 建模越来越复杂的类关系。现在您将看到 GORM 的 “ORM” 如何能够灵活处理遗留数据库中不符合 Grails 标准命名约定的表名与列名。
无论什么时候处理数据库中的现有数据,都要有一份最新的备份。著名的墨菲法则(Murphy's Law )的墨菲(Murphy)就像是我的守护神。什么样的错误都有可能发生,所以还是未雨绸缪的好。
除了用常规备份软件备份目标数据库外,我还建议再保存一份数据的纯文本副本。这样就能够用相同的数据集轻松地创建测试和开发数据库了,还可以轻松地跨数据库服务器移动数据(例如,在 MySQL 和 DB2 之间来回移动数据)。
您将再一次使用本系列一直开发的 Trip Planner 应用程序。清单 1 是一个名为 backupAirports.groovy 的 Groovy 脚本,它备份了 airport
表的记录。它用了三条语句、不足 20 行的代码连接到了数据库,从表中选定了每一行,并将数据作为 XML 导出。
sql = groovy.sql.Sql.newInstance( "jdbc:mysql://localhost/trip?autoReconnect=true", "grails", "server", "com.mysql.jdbc.Driver") x = new groovy.xml.MarkupBuilder() x.airports{ sql.eachRow("select * from airport order by id"){ row -> airport(id:row.id){ version(row.version) name(row.name) city(row.city) state(row.state) country(row.country) iata(row.iata) lat(row.lat) lng(row.lng) } } } |
清单 1 中的第一条语句创建了一个新的 groovy.sql.Sql
对象。这是一个标准 JDBC 类集的瘦 Groovy facade,包括 Connection
、Statement
和 ResultSet
。您可能已经认出了 newInstance
工厂方法的四个参数了:JDBC 连接字符串、用户名、密码以及 JDBC 驱动程序(在 grails-app/conf/DataSource.groovy 中也可以找到相同值)。
下一条语句创建了 groovy.xml.MarkupBuilder
。该类允许您动态创建 XML 文档。
最后一条语句(以 x.airports
开头)创建了 XML 树。XML 文档的根元素为 airports
。它还为数据库的每一行创建了一个 airport
元素,该元素带有 id
属性。嵌套于 airport
元素的元素有 version
、name
和 city
元素(想了解更多关于 Groovy Sql
和 MarkupBuilder
用途的信息,参见 参考资料)。
清单 2 展示了由此得到的 XML:
<airports> <airport id='1'> <version>2</version> <name>Denver International Airport</name> <city>Denver</city> <state>CO</state> <country>US</country> <iata>den</iata> <lat>39.8583188</lat> <lng>-104.6674674</lng> </airport> <airport id='2'>...</airport> <airport id='3'>...</airport> </airports> |
在备份脚本中,一定要按照主键顺序拖出记录。当恢复这个数据时,一定要按相同的顺序插入值,以确保外键值同样匹配(关于这一点我将在下一小节进一步详述)。
注意,该脚本是完全独立于 Grails 框架的。要使用它,就一定要在您的系统上安装 Groovy(参见 参考资料,查找下载与安装说明)。另外,类路径中一定要有 JDBC 驱动程序 JAR。可以在运行脚本时进行指定。在 UNIX® 中,要输入:
groovy -classpath /path/to/mysql.jar:. backupAirports.groovy |
当然了,在 Windows® 上,相应的文件路径和 JAR 分隔符是不同的。在 Windows 中,则需要输入:
groovy -classpath c:/path/to/mysql.jar;. backupAirports.groovy |
由于我经常使用 MySQL,所以我将一份该 JAR 的副本保存在了我的主目录(在 UNIX 上为 /Users/sdavis,在 Windows 上为 c:/Documents and Settings/sdavis)中的 .groovy/lib 目录中。当从命令行运行 Groovy 脚本时,该目录中的 JAR 会自动包含在类路径中。
清单 1 中的脚本将输出写到了屏幕。要将数据保存在一个文件中,可以在运行脚本时重定向输出:
groovy backupAirports.groovy > airports.xml |
从数据库中获取出数据仅仅是成功了一半。还要再将数据恢复到数据库中。清单 3 中展示的 restoreAirports.groovy 脚本用 Groovy XmlParser
读入了 XML,构造了一个 SQL insert
语句,并用了一个 Groovy SQL
对象来执行该语句(要了解更多关于 XmlParser
的信息,参见 参考资料)。
清单 3. 从 XML 中恢复数据库记录的 Groovy 脚本
if(args.size()){ f = new File(args[0]) println f sql = groovy.sql.Sql.newInstance( "jdbc:mysql://localhost/aboutgroovy?autoReconnect=true", "grails", "server", "com.mysql.jdbc.Driver") items = new groovy.util.XmlParser().parse(f) items.item.each{item -> println "${item.@id} -- ${item.title.text()}" sql.execute( "insert into item (version, title, short_description, description, url, type, date_posted, posted_by) values(?,?,?,?,?,?,?,?)", [0, item.title.text(), item.shortDescription.text(), item.description.text(), item.url.text(), item.type.text(), item.datePosted.text(), item.postedBy.text()] ) } } else{ println "USAGE: itemsRestore [filename]" } |
要运行该脚本,需要输入:
groovy restoreAirports.groovy airports.xml |
切记,对于要工作的表之间的关系而言,关系的一 的方面的主键字段一定要与关系的多 的方面的外键字段相匹配。例如,储存于 airport
表的 id
列中的值一定要与 flight
表的 arrival_airline_id
列中的值相同。
![]() |
|
为了确保自动编号的 id
字段被恢复为相同的值,一定要在恢复表前将它们全部删除。这样就可以在下次启动 Grails 重新创建表时将自动编号重置为 0。
将机场数据安全地备份之后(大概其他表中的数据也已经安全备份了),那么现在您就可以开始试验一些新的 “遗留” 数据了。不懂么?看完下一小节您就会明白了。
美国地质勘探局(United States Geological Survey,USGS)发表了一个全面的美国机场的列表,包括 IATA 代码和纬度/经度(参见 参考资料)。果然,USGS 字段与现行使用的 Airport
类不相匹配。虽然可以改变 Grails 类,使它与 USGS 表中的名称相匹配,但是这要大量改写应用程序。相反,本文不需要这样做,而是探讨几种不同的技术,在后台将现有的 Airport
类无缝映射到新的、不同的表模式中。
首先,需要将 USGS “遗留” 数据导入到数据库。然后运行清单 4 中的 createUsgsAirports.groovy 脚本,创建新表(该脚本假设您正在使用 MySQL。由于每个数据库创建新表的语法有所不同,所以使用其他数据库时,需要对该脚本做出适当修改)。
sql = groovy.sql.Sql.newInstance( "jdbc:mysql://localhost/trip?autoReconnect=true", "grails", "server", "com.mysql.jdbc.Driver") ddl = """ CREATE TABLE usgs_airports ( airport_id bigint(20) not null, locid varchar(4), feature varchar(80), airport_name varchar(80), state varchar(2), county varchar(50), latitude varchar(30), longitude varchar(30), primary key(airport_id) ); """ sql.execute(ddl) |
看一看清单 5 中的 usgs-airports.xml。它是 GML 格式的一个例子。该 XML 要比清单 2 中由备份脚本创建的简单的 XML 复杂一些。这其中,每一个元素都处在一个名称空间中,而且元素嵌套得更深。
- <?xml version="1.0" encoding="utf-8" ?>
- <ogr:FeatureCollection
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://ogr.maptools.org/airports.xsd"
- xmlns:ogr="http://ogr.maptools.org/"
- xmlns:gml="http://www.opengis.net/gml">
- <gml:featureMember>
- <ogr:airprtx020 fid="F0">
- <ogr:geometryProperty>
- <gml:Point>
- <gml:coordinates>-156.042831420898438,19.73573112487793</gml:coordinates>
- </gml:Point>
- </ogr:geometryProperty>
- <ogr:AREA>0.000</ogr:AREA>
- <ogr:PERIMETER>0.000</ogr:PERIMETER>
- <ogr:AIRPRTX020>1</ogr:AIRPRTX020>
- <ogr:LOCID>KOA</ogr:LOCID>
- <ogr:FEATURE>Airport</ogr:FEATURE>
- <ogr:NAME>Kona International At Keahole</ogr:NAME>
- <ogr:TOT_ENP>1271744</ogr:TOT_ENP>
- <ogr:STATE>HI</ogr:STATE>
- <ogr:COUNTY>Hawaii County</ogr:COUNTY>
- <ogr:FIPS>15001</ogr:FIPS>
- <ogr:STATE_FIPS>15</ogr:STATE_FIPS>
- </ogr:airprtx020>
- </gml:featureMember>
- <gml:featureMember>...</gml:featureMember>
- <gml:featureMember>...</gml:featureMember>
- </ogr:FeatureCollection>
现在,创建如清单 6 所示的 restoreUsgsAirports.groovy 脚本。要获取具有名称空间的元素,需要声明几个 groovy.xml.Namespace
变量。与前面的 restoreAirport.groovy 脚本(清单 3)中使用的简单的点表示法不同,这里的具有名称空间的元素要用方括号括上。
清单 6. 将 USGS 机场数据恢复到数据库
- if(args.size()){
- f = new File(args[0])
- println f
- sql = groovy.sql.Sql.newInstance(
- "jdbc:mysql://localhost/trip?autoReconnect=true",
- "grails",
- "server",
- "com.mysql.jdbc.Driver")
- FeatureCollection = new groovy.util.XmlParser().parse(f)
- ogr = new groovy.xml.Namespace("http://ogr.maptools.org/")
- gml = new groovy.xml.Namespace("http://www.opengis.net/gml")
- FeatureCollection[gml.featureMember][ogr.airprtx020].each{airprtx020 ->
- println "${airprtx020[ogr.LOCID].text()} -- ${airprtx020[ogr.NAME].text()}"
- points = airprtx020[ogr.geometryProperty][gml.Point][gml.coordinates].text().split(",")
- sql.execute(
- "insert into usgs_airports (airport_id, locid, feature, airport_name, state,
- county, latitude, longitude) values(?,?,?,?,?,?,?,?)",
- [airprtx020[ogr.AIRPRTX020].text(),
- airprtx020[ogr.LOCID].text(),
- airprtx020[ogr.FEATURE].text(),
- airprtx020[ogr.NAME].text(),
- airprtx020[ogr.STATE].text(),
- airprtx020[ogr.COUNTY].text(),
- points[1],
- points[0]]
- )
- }
- }
- else{
- println "USAGE: restoreAirports [filename]"
- }
在命令指示符处输入如下信息,将 usgs_airports.xml 文件中的数据插入到新创建的表中:
groovy restoreUsgsAirports.groovy usgs-airports.xml |
验证数据插入成功:从命令行登入 MySQL,确保数据已经就位,如清单 7 所示:
清单 7. 验证数据库中的 USGS 机场数据
$ mysql --user=grails -p --database=trip mysql> desc usgs_airports; +--------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------------+-------------+------+-----+---------+-------+ | airport_id | bigint(20) | NO | PRI | | | | locid | varchar(4) | YES | | NULL | | | feature | varchar(80) | YES | | NULL | | | airport_name | varchar(80) | YES | | NULL | | | state | varchar(2) | YES | | NULL | | | county | varchar(50) | YES | | NULL | | | latitude | varchar(30) | YES | | NULL | | | longitude | varchar(30) | YES | | NULL | | +--------------+-------------+------+-----+---------+-------+ 8 rows in set (0.01 sec) mysql> select count(*) from usgs_airports; +----------+ | count(*) | +----------+ | 901 | +----------+ 1 row in set (0.44 sec) mysql> select * from usgs_airports limit 1/G *************************** 1. row *************************** airport_id: 1 locid: KOA feature: Airport airport_name: Kona International At Keahole state: HI county: Hawaii County latitude: 19.73573112487793 longitude: -156.042831420898438 |
遗留表就位之后,您需要做最后一件事:禁用 grails-app/conf/DataSource.groovy 中的 dbCreate
变量。回想一下 “GORM:有趣的名称,严肃的技术” 就会知道,如果相应的表不存在的话,该变量会指示 GORM 在后台创建它,并且会改变任何现有表,从而匹配 Grails 域类。因此,如果要处理遗留表的话,就一定要禁用该特性,这样 GORM 才不会破坏其他应用程序可能会用到的模式。
如果能够有选择地为特定的表启用或禁用 dbCreate
就好了。不幸的是,它是一个全局的 “全有或全无” 的设置。我遇到既有新表又有遗留表的情况时,会先允许 GORM 创建新表,然后禁用 dbCreate
,导入现有的遗留表。在这样的情况下,您就会了解到有一个好的备份与恢复策略是多么重要了。
我将示范的第一个将域类映射到遗留表的策略是使用静态 mapping
块。大多数情况下我都会使用这个块,因为它感觉最像 Grails。我习惯将静态 constraints
块添加到域类,这样添加静态 mapping
块感觉起来和添加框架的其余部分是一致的。
将 grails-app/domain/Airport.groovy 文件复制到 grails-app/domain/AirportMapping.groovy。这个名称只是为了示范用的。因为将会有三个类全部映射回相同的表中,因此需要有一种方式来将每一个类单独命名(这在真实的应用程序中不大可能会发生)。
注释掉城市与国家字段,因为在新的表中没有这些字段。然后从 constraints
块中移除这些字段。现在添加 mapping
块,将 Grails 的名称链接到数据库名,如清单 8 所示:
- class AirportMapping{
- static constraints = {
- name()
- iata(maxSize:3)
- state(maxSize:2)
- lat()
- lng()
- }
- static mapping = {
- table "usgs_airports"
- version false
- columns {
- id column: "airport_id"
- name column: "airport_name"
- iata column: "locid"
- state column: "state"
- lat column: "latitude"
- lng column: "longitude"
- }
- }
- String name
- String iata
- //String city
- String state
- //String country = "US"
- String lat
- String lng
- String toString(){
- "${iata} - ${name}"
- }
- }
mapping
块的第一条语句将 AirportMapping
类链接到 usgs_airports
表。下一条语句通知 Grails 表没有 version
列(GORM 通常会创建一个 version
列来优化乐观锁定)。最后,columns
块将 Grails 名称映射到数据库名称。
注意,使用了这个映射技术,表中的某些特定的字段是可以忽略的。在这种情况下,feature
与 county
列未表示在域类中。要想让未储存于表中的字段存在于域类中,可以添加静态 transients
行。该行看起来与一对多关系中使用的 belongsTo
变量类似。例如,如果 Airport
类中有两个字段不需要储存到表中,代码会是这样的:
static transients = ["tempField1", "tempField2"] |
此处示范的 mapping
块仅仅涉及到此技术可以实现的皮毛而已。想了解更多的信息,参见 参考资料。
输入 grails generate-all AirportMapping
,创建 控制器和 GSP 视图。由于此表实质上是一个查找表,所以请进入 grails-app/controllers/AirportMappingController.groovy,只留下 list
和 show
闭包。移除 delete
、edit
、update
、create
以及 save
(不要忘记从 allowedMethods
变量中移除 delete
、edit
和 save
。可以完全移除整个行,或者只留下方括号空集)。
要使该视图为只读,还需要做几个快捷的更改。首先,从 grails-app/views/airportMapping/list.gsp 顶端移除 New AirportMapping
链接。然后对 grails-app/views/airportMapping/show.gsp 做相同操作。最后,从 show.gsp 底部移除 edit
和 delete
按钮。
输入 grails run-app
,验证 mapping
块可以运行。请看一下图 1 中展示的页面:
图 1. 验证
mapping
块可以运行

了解了 mapping
块后,让我们再深入一步。不难想象如果拥有了遗留表,就有可能也拥有了遗留 Java 类。如果您想将现有 Java 代码与现有表中的数据融合,可以使用接下来的两个映射技术。
在 Java 1.5 引入注释之前,Hibernate 用户需要创建名为 HBM 文件的 XML 映射文件。回忆一下,GORM 是一个优于 Hibernate 的瘦 Groovy facade,因此,那些古老的 Hibernate 技巧仍然奏效也不足为奇。
首先,将遗留 Java 源文件复制到 src/java。如果使用包的话,要为每一个包名创建一个目录。例如,清单 9 中所示的 AirportHbm.java 文件位于 org.davisworld.trip
包中。这意味着该文件的完整路径应该是 src/java/org/davisworld/trip/AirportHbm.java。
- package org.davisworld.trip;
- public class AirportHbm {
- private long id;
- private String name;
- private String iata;
- private String state;
- private String lat;
- private String lng;
- public long getId() {
- return id;
- }
- public void setId(long id) {
- this.id = id;
- }
- // all of the other getters/setters go here
- }
Java 文件一旦就位,就可以挨着它创建一个清单 10 中所示的名为 AirportHbmConstraints.groovy 的 “影子” 文件了。该文件中可以放置本应该位于域类中的静态 constraints
块。切记该文件一定要与 Java 类位于相同的包中。
清单 10. AirportHbmConstraints.groovy
package org.davisworld.trip static constraints = { name() iata(maxSize:3) state(maxSize:2) lat() lng() } |
src 目录下的文件会在运行应用程序或者创建要部署的 WAR 文件时编译。如果已经编译了 Java 代码的话,也可以仅将它压缩为 JAR 文件并将其置于 lib 目录中。
接下来,让我们来建立控制器。按照约定优于配置的规定,控制器应该命名为 AirportHbmController.groovy。由于 Java 类位于一个包中,因此可以将控制器置于同一包中,或是在文件顶部导入 Java 类。我更偏爱导入的方法,如清单 11 所示:
清单 11. AirportHbmController.groovy
import org.davisworld.trip.AirportHbm class AirportHbmController { def scaffold = AirportHbm } |
接下来,将现有的 HBM 文件复制到 grails-app/conf/hibernate。应该会有一个如清单 12 所示的单一的 hibernate.cfg.xml 文件,您要在这里指定每一个类用的映射文件。在本例中,应该会有一个 AirportHbm.hbm.xml 文件的条目。
清单 12. hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <mapping resource="AirportHbm.hbm.xml"/> </session-factory> </hibernate-configuration> |
每一个类都必须有它自己的 HBM 文件。该文件为先前使用的静态 mapping
块的 XML 等价体。清单 13 展示了 AirportHbm.hbm.xml:
清单 13. AirportHbm.hbm.xml
- <?xml version="1.0"?>
- <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
- <hibernate-mapping>
- <class name="org.davisworld.trip.AirportHbm" table="usgs_airports">
- <id name="id" column="airport_id">
- <generator class="native"/>
- </id>
- <property name="name" type="java.lang.String">
- <column name="airport_name" not-null="true" />
- </property>
- <property name="iata" type="java.lang.String">
- <column name="locid" not-null="true" />
- </property>
- <property name="state" />
- <property name="lat" column="latitude" />
- <property name="lng" column="longitude" />
- </class>
- </hibernate-mapping>
包的完整名是参考 Java 类而指定的。剩余的条目将 Java 名映射到表名。name
和 iata
字段条目演示了长表单。由于 state
字段在 Java 代码中和表中是一样的,因此可以将其条目缩短。最后两个字段 — lat
与 lng
— 演示了缩短了的语法(想了解更多关于 Hibernate 映射文件的信息,参见 参考资料)。
如果 Grails 仍在运行的话,重新启动它。现在应该能够在 http://localhost:8080/trip/airportHbm 看到 Hibernate 映射数据。
对 Java 类使用 Enterprise JavaBeans(EJB)3 注释
正如我在上面所提到的,Java 1.5 引入了注释。注释允许您通过添加 @
前缀的方式直接向 Java 类添加元数据。Groovy 1.0 在其发行初期(2006 年 12 月)并不支持 Java 1.5 的诸如注释这样的语言特性。一年以后发行的 Groovy 1.5 则发生了翻天覆地的变化。这就意味着您也可以将 EJB3 注释的 Java 文件引入到一个现有的 Grails 应用程序中了。
再次启动 EJB3 注释的 Java 文件。将清单 14 展示的 AirportAnnotation.java 置于 src/java/org.davisworld.trip 中,紧挨着 AirportHbm.java 文件:
- package org.davisworld.trip;
- import javax.persistence.*;
- @Entity
- @Table(name="usgs_airports")
- public class AirportAnnotation {
- private long id;
- private String name;
- private String iata;
- private String state;
- private String lat;
- private String lng;
- @Id
- @Column(name="airport_id", nullable=false)
- public long getId() {
- return id;
- }
- @Column(name="airport_name", nullable=false)
- public String getName() {
- return name;
- }
- @Column(name="locid", nullable=false)
- public String getIata() {
- return iata;
- }
- @Column(name="state", nullable=false)
- public String getState() {
- return state;
- }
- @Column(name="latitude", nullable=false)
- public String getLat() {
- return lat;
- }
- @Column(name="longitude", nullable=false)
- public String getLng() {
- return lng;
- }
- // The setter methods don't have an annotation on them.
- // They are not shown here, but they should be in the file
- // if you want to be able to change the values.
- }
注意,一定要导入文件顶部的 javax.persistence
包。@Entity
与 @Table
注释了类声明,将它映射到了适当的数据库表中。其他的注释位于每一个字段的 getter 方法之上。所有的字段都应该有 @Column
注释,它将字段名映射到列名。主键也应该有一个 @ID
注释。
清单 15 中的 AirportAnnotationConstraints.groovy 文件与前面清单 10 中的例子没什么不同:
清单 15. AirportAnnotationConstraints.groovy
package org.davisworld.trip static constraints = { name() iata(maxSize:3) state(maxSize:2) lat() lng() } |
AirportAnnotationController.groovy(清单 16 中所展示的)是按照通常的方式搭建的:
清单 16. AirportAnnotationController.groovy
import org.davisworld.trip.AirportAnnotation class AirportAnnotationController { def scaffold = AirportAnnotation } |
hibernate.cfg.xml 文件再次开始其作用。这回,语法有点不同。您直接将它指向类,而不是指向一个 HBM 文件,如清单 17 所示:
清单 17. hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <mapping resource="AirportHbm.hbm.xml"/> <mapping class="org.davisworld.trip.AirportAnnotation"/> </session-factory> </hibernate-configuration> |
要让注释开始生效,还需要做最后一件事。Grails 并不是本来就被设置成可以查找 EJB3 注释的。而是需要导入 grails-app/conf/DataSource.groovy 中的一个特定的类,如清单 18 所示:
清单 18. DataSource.groovy
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration dataSource { configClass = GrailsAnnotationConfiguration.class pooled = false driverClassName = "com.mysql.jdbc.Driver" username = "grails" password = "server" } |
一旦导入了 org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
并允许 Spring 将其作为 configClass
而注入 dataSource
块之后,Grails 就会支持 EJB3 注释了,同时它还可以支持 HBM 文件和本地映射块。
如果忘了这最后一步的话(我几乎每一次在 Grails 中使用 EJB3 注释时都会忘记这一步 ),会得到如下的错误信息:
清单 19. 未注入 DataSource.groovy 中的
configClass
时抛出的异常
org.hibernate.MappingException: An AnnotationConfiguration instance is required to use <mapping class="org.davisworld.trip.AirportAnnotation"/> |
这样看来,将对象 映射 到 Grails 的关系 数据库中应该易如反掌(毕竟,这正是它被命名为 GORM 的原因)。一旦您有信心能够轻松备份和恢复数据,您就会有很多种方式使 Grails 符合遗留数据库中的非标准的命名约定。静态的 mapping
块是完成这个任务的最简单的方式,因为它与 Grails 最相似。但如果您的遗留 Java 类已经映射到了遗留数据库中的话,那就不用多此一举了。无论您使用 HBM 文件还是较新的 EJB3 注释,Grails 都可以直接利用您已经完成的成果,这样您就可以投身其他的任务了。
在下一篇文章中,您将有机会了解 Grails 事件模型。从构建脚本到单个的 Grails 工件(域类、控制器等),所有这些都会在应用程序的生命周期的关键点抛出事件。因此在下一篇文章中,您将学习如何设置侦听器来捕获这些事件,并使用定制操作回应。到那时,请尽情享受精通 Grails 带来的乐趣吧。
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
样例代码1 | j-grails07158.zip | 991KB | HTTP |
![]() | ||||
![]() | ![]() |