程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 精通Grails: Grails與遺留數據庫

精通Grails: Grails與遺留數據庫

編輯:關於JAVA

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 導出。

清單 1. backupAirports.groovy

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 元素。

清單 2 展示了由此得到的 XML:

清單 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 對象來執行該 語句。


清單 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 列中的值相同。

轉換 USGS 數據

USGS 將機場的數據提供為一個 shapefile。這是一個交換地理數據的固定文件格式。一個 shapefile 至少由三個文件組成。.shp 文件包含地理數據 — 此處指每個機場的緯度/經度。.shx 文件是一個空間 索引。.dbf 文件是 — 您猜對了 — 一個很好的古老的 dBase 文件,它包含了所有的非空間數據(此處 指機場名、IATA 代碼等)。

我使用了 Geospatial Data Abstraction Library(GDAL)— 一套處理地理數據的開源的命令行工具 集 — 來將 shapefile 轉換為文中使用的地理標記語言(Geography Markup Language,GML)文件。我 使用的具體的命令為:

ogr2ogr -f "GML" airports.xml airprtx020.shp

您還可以使用 GDAL 將數據轉換為逗號分割值(comma-separated value,CSV)、GeoJSON、Keyhole 標記語言(Keyhole Markup Language,KML)以及其他很多種格式。

為了確保自動編號的 id 字段被恢復為相同的值,一定要在恢復表前將它們全部刪除。這樣就可以在 下次啟動 Grails 重新創建表時將自動編號重置為 0。

將機場數據安全地備份之後(大概其他表中的數據也已經安全備份了),那麼現在您就可以開始試驗 一些新的 “遺留” 數據了。不懂麼?看完下一小節您就會明白了。

導入新的機場數據

美國地質勘探局(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,導 入現有的遺留表。在這樣的情況下,您就會了解到有一個好的備份與恢復策略是多麼重要了。

靜態映射塊

我將示范的第一個將域類映射到遺留表的策略是使用靜態 mapping 塊。大多數情況下我都會使用這個 塊,因為它感覺最像 Grails。我習慣將靜態 constraints 塊添加到域類,這樣添加靜態 mapping 塊感 覺起來和添加框架的其余部分是一致的。

將 grails-app/domain/Airport.groovy 文件復制到 grails-app/domain/AirportMapping.groovy。 這個名稱只是為了示范用的。因為將會有三個類全部映射回相同的表中,因此需要有一種方式來將每一個 類單獨命名(這在真實的應用程序中不大可能會發生)。

注釋掉城市與國家字段,因為在新的表中沒有這些字段。然後從 constraints 塊中移除這些字段。現 在添加 mapping 塊,將 Grails 的名稱鏈接到數據庫名,如清單 8 所示:

清單 8. AirportMapping.groovy

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 塊可以運行

結合使用遺留 Java 類與 Hibernate 映射文件

了解了 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。

清單 9. 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 — 演示了縮短了的語法。

如果 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 文件:

清單 14. AirportAnnotation.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. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved