精通Grails: Grails与遗留数据库
导入新的机场数据
美国地质勘探局(United States Geological Survey,USGS)发表了一个全面的美国机场的列表,包括 IATA 代码和纬度/经度(参见 参考资料)。果然,USGS 字段与现行使用的 Airport 类不相匹配。虽然可以改变 Grails 类,使它与 USGS 表中的名称相匹配,但是这要大量改写应用程序。相反,本文不需要这样做,而是探讨几种不同的技术,在后台将现有的 Airport 类无缝映射到新的、不同的表模式中。
首先,需要将 USGS “遗留” 数据导入到数据库。然后运行清单 4 中的 createUsgsAirports.groovy 脚本,创建新表(该脚本假设您正在使用 MySQL。由于每个数据库创建新表的语法有所不同,所以使用其他数据库时,需要对该脚本做出适当修改)。
清单 4. 创建 USGS 机场表
"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 复杂一些。这其中,每一个元素都处在一个名称空间中,而且元素嵌套得更深。
清单 5. GML 格式的 USGS 机场表
<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 机场数据恢复到数据库
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 文件中的数据插入到新创建的表中:
usgs-airports.xml
验证数据插入成功:从命令行登入 MySQL,确保数据已经就位,如清单 7 所示:
清单 7. 验证数据库中的 USGS 机场数据
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
禁用 dbCreate
遗留表就位之后,您需要做最后一件事:禁用 grails-app/conf/DataSource.groovy 中的 dbCreate 变量。回想一下 “GORM:有趣的名称,严肃的技术” 就会知道,如果相应的表不存在的话,该变量会指示 GORM 在后台创建它,并且会改变任何现有表,从而匹配 Grails 域类。因此,如果要处理遗留表的话,就一定要禁用该特性,这样 GORM 才不会破坏其他应用程序可能会用到的模式。
如果能够有选择地为特定的表启用或禁用 dbCreate 就好了。不幸的是,它是一个全局的 “全有或全无” 的设置。我遇到既有新表又有遗留表的情况时,会先允许 GORM 创建新表,然后禁用 dbCreate,导入现有的遗留表。在这样的情况下,您就会了解到有一个好的备份与恢复策略是多么重要了。