技术开发 频道

精通Grails: Grails与遗留数据库

  导入新的机场数据

  美国地质勘探局(United States Geological Survey,USGS)发表了一个全面的美国机场的列表,包括 IATA 代码和纬度/经度(参见 参考资料)。果然,USGS 字段与现行使用的 Airport 类不相匹配。虽然可以改变 Grails 类,使它与 USGS 表中的名称相匹配,但是这要大量改写应用程序。相反,本文不需要这样做,而是探讨几种不同的技术,在后台将现有的 Airport 类无缝映射到新的、不同的表模式中。

  首先,需要将 USGS “遗留” 数据导入到数据库。然后运行清单 4 中的 createUsgsAirports.groovy 脚本,创建新表(该脚本假设您正在使用 MySQL。由于每个数据库创建新表的语法有所不同,所以使用其他数据库时,需要对该脚本做出适当修改)。

  清单 4. 创建 USGS 机场表

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 复杂一些。这其中,每一个元素都处在一个名称空间中,而且元素嵌套得更深。

  清单 5. GML 格式的 USGS 机场表

<?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

  禁用 dbCreate

  遗留表就位之后,您需要做最后一件事:禁用 grails-app/conf/DataSource.groovy 中的 dbCreate 变量。回想一下 “GORM:有趣的名称,严肃的技术” 就会知道,如果相应的表不存在的话,该变量会指示 GORM 在后台创建它,并且会改变任何现有表,从而匹配 Grails 域类。因此,如果要处理遗留表的话,就一定要禁用该特性,这样 GORM 才不会破坏其他应用程序可能会用到的模式。

  如果能够有选择地为特定的表启用或禁用 dbCreate 就好了。不幸的是,它是一个全局的 “全有或全无” 的设置。我遇到既有新表又有遗留表的情况时,会先允许 GORM 创建新表,然后禁用 dbCreate,导入现有的遗留表。在这样的情况下,您就会了解到有一个好的备份与恢复策略是多么重要了。

0
相关文章