顯示具有 發表文章_刊登於RunPC 標籤的文章。 顯示所有文章
顯示具有 發表文章_刊登於RunPC 標籤的文章。 顯示所有文章

2007年8月5日 星期日

(2006.08月號--151期) 如何以Maven協助Hibernate開發

在今日的企業級應用開發環境中,物件導向開發方式已成為主流,但在實際的應用中,物件只存在於程式與記憶體之中,並不能直接進行儲存。如果想要永久保存物件狀態,則必須要進行物件的持久化,也就是把物件狀態儲存進專門的資料庫系統中,而目前最廣泛使用的關聯式資料庫(RDBMS),並不支援儲存物件導向數據資料。


因此傳統的關聯式資料庫程式設計,必須直接在程式中以Hard code的方式撰寫SQL語法進行開發,而以Hard code撰寫SQL語法的方式,相對的也代表了開發的應用程式無法跨資料庫平台。雖然JDBC統一了Java程式與資料庫之間的操作介面,讓開發人員可以不用了解與資料庫相關特定的API操作,然而自行撰寫SQL語法或再次將SQL語法進行封裝仍是不可避免的事,而在物件導向程式設計之中,物件與關聯式資料並無法以簡單的方式進行轉換,導致了資料永續性的開發上受到了先天的限制。


ORM(Object/Relational Mapping)物件-關聯映射技術,在該問題的處理上已經有完善的解決方案,而目前在開放原始碼的技術中,最受人矚目的ORM實作,應該就是Hibernate了。在本期中筆者將介紹如何使用Maven協助Hibernate進行快速開發,而對於Hibernate的基本觀念,筆者在此並不詳述,請參考之前刊載於RunPC中的相關文章。


註:關於Hibernate文章,請參考RunPC128129130132期,資料永續層解決方案Hibernate的相關介紹,作者為Mark Ho


設定hibernatedoclet環境

在開發過程中Hibernate進行物件與關聯式資料的處理過程中,需要有一份映射文件,用以描述物件與關聯式資料的轉換關係,例如:資料型態的對應、資料欄位長度的限制、單向關聯、雙向關聯、一對多、一對一與多對多關聯等等的設定。一般而言映射文件命名通常為*.hbm.xml


然而在實際開發上當物件作了修改或重構後,必須對映射文件進行維護。如此的開發方式相當的不直覺與不方便。並且學習映射文件的撰寫不是一件簡單的事。


因此我們換一個角度思考,如果在物件的設計中,順手將這些轉換關係的資訊寫在物件中,由物件編譯過程中直接幫我們產生相關的映射文件,則我們只要維護一份原始碼即可,當進行修改與重構的過程中,映射檔案的變更也將立即的反應出來。在此我們將使用XDoclet進行Code generate來達到這個目的。


註:有關於XDoclet相關觀念的文章,請參考RunPC130131期,XDoclet入門篇與進階篇,作者為歐宣修。


首先請參考筆者於148期中的Maven文章,使用Maven Genapp Plugin產生專案,並且參考設定 1project.xml進行修改,在該檔中可以看到我們在Maven相依的plugin套件中加入了maven-xdoclet-plugin。接著參考設定 2project.properties加入設定參數。說明請參考表 1中關於hibernatedoclet的參數設定。最後再對maven.xml進行設定,參考設定 3中可知當我們執行java:compile這個goal前會先行運作xdoclet:hibernatedoclet協助我們產生Mapping file


設定 1 project.xml

<?xml version="1.0" encoding="UTF-8"?>

<project>

<pomVersion>3</pomVersion>

<artifactId>hibernate-example</artifactId>

<groupId>hibernate-example</groupId>

<name>Example Hibernate Application</name>

<currentVersion>1.0</currentVersion>

<dependencies>

<dependency>

<groupId>xdoclet</groupId>

<artifactId>maven-xdoclet-plugin</artifactId>

<version>1.2.3</version>

<type>plugin</type>

</dependency>

中間省略…

</dependencies>

中間省略…

</project>


設定 2 project.properties

#####################################

# hibernatedoclet properties

#####################################

maven.xdoclet.hibernatedoclet.fileset.0=true

maven.xdoclet.hibernatedoclet.fileset.0.sourcedir=${maven.src.dir}/java

maven.xdoclet.hibernatedoclet.fileset.0.include=**/*.java

maven.xdoclet.hibernatedoclet.fileset.0.exclude=

maven.xdoclet.hibernatedoclet.hibernate.0=true

maven.xdoclet.hibernatedoclet.hibernate.0.Version=3.0

maven.xdoclet.hibernatedoclet.destDir=${maven.build.dest}


設定 3 maven.xml

<?xml version="1.0" encoding="UTF-8"?>

<project

xmlns:j="jelly:core"

xmlns:ant="jelly:ant">

<preGoal name="java:compile">

<attainGoal name="xdoclet:hibernatedoclet"/>

</preGoal>

</project>


1 maven-xdoclet-plugin設定

參數

說明

maven.xdoclet.hibernatedoclet.fileset.[index]

設定hibernatedoclet引用的檔案集設定,index值為0~910組設定,也就是說最多可以針對10組路徑下的原始檔進行hibernatedocletCode generate,預設index=0為啟用。若指定的maven.xdoclet.hibernatedoclet.fileset.[index]未設定,則以下相對應的檔案集設定即無效。

maven.xdoclet.hibernatedoclet.fileset.[index].sourcedir

指定檔案集原始碼放置路徑,預設為${maven.src.dir}/java

maven.xdoclet.hibernatedoclet.fileset.[index].include

指定檔案集引入檔案規則,預設為**/*.java

maven.xdoclet.hibernatedoclet.fileset.[index].exclude

指定檔案集排除檔案規則,預設不排除任何檔。

maven.xdoclet.hibernatedoclet.hibernate.[index]

設定對maven.xdoclet.hibernatedoclet.fileset.[index]進行hibernatedoclet是否生效,預設為true,若為false則對指定的檔案集不執行hibernatedoclet

maven.xdoclet.hibernatedoclet.hibernate.[index].Version

設定對maven.xdoclet.hibernatedoclet.fileset.[index]進行hibernatedoclet所產生的*.hbm.xml檔案可使用於hibernate那個版本,預設為1.1版,目前可設定的值為目前有1.12.02.13.0

maven.xdoclet.hibernatedoclet.destDir

所有檔案集產生出的*.hbm.xml放置的目的路徑。


建立Domain Object並產生Mapping File

當上一節的設定完成後,我們正式建立Domain Object,並試圖產生Hibernate所需要的Mapping file。這裏我們

是以POJO定義Domain Object,而POJO就是所謂的Plain Ordinary Java Object,字面上來講就是無格式普通Java 物件,簡單的可以理解爲一個不包含邏輯程式碼的值物件。


我們在此建立二個POJO,分別是User(使用者)Role(所屬角色)作為開始。這時Maven專案架構應該如資料結構 1所示,圖 1展示了類別屬性與其setget相關method。接著參考程式 1、程式 2分別在類別中加入<hibernatedoclet>Tag設定,在此我們簡單的了解一下相關屬性用途,若讀者希望了解更詳細的屬性設定,可參考XDoclet官方網站http://xdoclet.sourceforge.net/xdoclet/tags/hibernate-tags.html


  1. @hibernate.class:該Tag用於宣告定義持久化類別,需放置於Class宣告的上方。

    1. table:指定持久化類別對應資料庫Table名稱。

  2. @hibernate.id:定義主鍵欄位相關屬性。

    1. column:指定欄位對應資料庫欄位名稱,若未指定則使用相對應的property名稱。

    2. generator-class:主鍵產生器屬性,目前有uuid.hexuuid.stringincrementassignednativeidentitysequencehiloseqhiloforeign可供設定。

  3. @hibernate.property:定義持久化類別對應資料庫欄位相關屬性。

    1. column:指定欄位對應資料庫欄位名稱,若未指定則使用相對應的property名稱。

    2. length:指定對應資料庫欄位長度。

    3. not-null:是否允許欄位存放null值。

    4. unique:是否允許資料庫欄位值重覆

    5. type:對應資料庫欄位型態

另外,讀者可能發現,在範例中的二個POJO都實現了java.io.Serializable介面,這是Hibernate規範中所提到的,但目前大多數資料並未說明為何必須實現Serializable。事實上Hibernate並不要求持久化類必須實現Serializable介面,但是對於採用分散式架構的Java應用,當Java物件在不同的分散節點之間傳輸時(例如:RMI),這個物件所屬的類別必須實現Serializable介面,此外,在Java Web應用中,如果希望對HttpSession中存放的Java物件進行持久化,那麼這個Java物件所屬的類也必須實現Serializable介面,因此若只拿Hibernate作單純資料庫應用可不遵守該規定外,HibernateJ2EE的應用上都是必須實現Serializable的。


經過maven的設定與撰寫完POJO後,在Console下執行maven java:compile,當指令運作結束後,在[hibernate_example/target/classes/demo/model]路徑下將會出現User.hbm.xmlRole.hbm.xmlHibernate Mapping file

資料結構 1

hibernate-example

|--src

| --main

| --java

| --demo

| --model

| |--Role.java

| --User.java

|--maven.xml

|--project.properties

--project.xml


1 Domain Object類別圖


程式 1 User.java

package demo.model;

import java.io.Serializable;


/**

* @hibernate.class table="app_user"

*/

public class User implements Serializable {

private Long id;

private String username;

private String password;


/**

* @hibernate.id column="id" generator-class="native"

*/

public Long getId() {

return id;

}


/**

* @hibernate.property length="50" not-null="true" unique="true"

*/

public String getUsername() {

return username;

}


/**

* @hibernate.property column="password" not-null="true"

*/

public String getPassword() {

return password;

}

…set method 省略…

}


程式 2 Role.java

package demo.model;

import java.io.Serializable;


/**

* @hibernate.class table="role"

*/

public class Role implements Serializable {

private Long id;

private String name;

private String description;


/**

* @hibernate.id column="id" generator-class="native"

*/

public Long getId() {

return id;

}


/**

* @hibernate.property column="name" length="20"

*/

public String getName() {

return name;

}


/**

* @hibernate.property column="description" length="64"

*/

public String getDescription() {

return description;

}

…set method 省略…

}


使用Hibernate Plugin建立Table Schema

到目前為止我們設定了hibernatedoclet、建立了POJO的類別並自動產生了相關的Mapping file。假設這時資料庫Schema早已建立好的話,就可以開始使用Hibernate進行DAO的開發與測試了。倘若Schema尚未建立呢?需要自行建立嗎?而自行建立則又會引發另一個問題,POJO的設計是基於Schema所建立的。當日後Schema修改時也必須對應相關的POJOMapping file進行維護。一次、二次還可以接受,但在專案開發的過程中需求永遠在變化,很難保證Schema不會一直變更,一旦次數一多,將引發難以重構的災難。


Ant中可以透過net.sf.hibernate.tool.hbm2ddl.SchemaExportTask協助我們將*.hbm.xml再次轉化成相對應資料庫的Schema,而在Maven上可以使用Hibernate Plugin協助進行SchemaExport。基於某些因素Hibernate Plugin最新版本為1.3並且已有一段時間沒有新版本釋出,該版本並未支援Hibernate 3.X,若讀者使用Hibernate 2開發的話,可以直接進行下載。在本次範例中我們將下載經過1.3版本修改的Hibernate Plugin1.4 (請讀者參考相關資源4進行下載)


[%MAVEN_HOME%/plugins]下的1.3版移除,放置下載完成的maven-hibernate-plugin-1.4.jar於該資料夾內即完成了hibernate plugin的安裝。接著參考設定 4project.xml中加入相依套件,此處的設定中我們加入了maven-hibernate-plugin的設定與Hibernate進行SchemaExport時所需的套件,另外為了示範Hibernate跨資料庫平台的特色,在此引入而PostgreSqlHsqldb二個開放原始碼資料庫的JDBC協助進行測試。


在進行SchemaExport功能時可以設定是否正式進行資料庫的更新,或者只是單純的產生資料庫Schema的文字檔供開發人員進行修改或使用。為此必須正確設定Hibernate與資料庫連結的相關參數,參考設定 5與設定 6比較範例中二種資料庫設定的異同處,而關於hibernate.dialect參數的設定,請參考Hibernate官方參考手冊。


最後參考設定 7加入Hibernate plugin所需的參數,讀者對照表 2即可知相關參數的意義為何。當設定完成後於Console下執行maven hibernate:schema-export即可進行SchemaExport,在此我們分別為了PostgreSqlHsqldb作了SchemaExport,請參考圖 2與圖 3的結果。若讀者不希望直接進行資料庫的更新,則可將project.properties檔中參數maven.hibernate.text設為yes再次進行hibernate:schema-export,即可於[target/schema/schema.sql]檔中查看匯出的資料庫SQL語法。


hibernate:schema-export這個goal可以協助我們進行建立資料庫Schema,而當進行POJO的修改並重新產生Mapping file時,可運行maven hibernate:schema-update,該指令將可在不移除資料表的清況下進行資料庫Schema的更新,對於DataBase Schema的重構有很大的幫助。


設定 4 project.xml增添相依套件

<dependency>

<groupId>maven</groupId>

<artifactId>maven-hibernate-plugin</artifactId>

<version>1.4</version>

<type>plugin</type>

</dependency>

<dependency>

<groupId>postgresql</groupId>

<artifactId>postgresql</artifactId>

<version>8.1-407.jdbc3</version>

</dependency>

<dependency>

<groupId>hsqldb</groupId>

<artifactId>hsqldb</artifactId>

<version>1.7.3.3</version>

</dependency>

<dependency>

<groupId>geronimo-spec</groupId>

<artifactId>geronimo-spec-jta</artifactId>

<version>1.0.1B-rc4</version>

</dependency>

<dependency>

<groupId>commons-lang</groupId>

<artifactId>commons-lang</artifactId>

<version>2.1</version>

</dependency>

<dependency>

<groupId>hibernate</groupId>

<artifactId>hibernate</artifactId>

<version>3.0.5</version>

</dependency>


設定 5 database-postgresql.properties

hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

hibernate.connection.url=jdbc:postgresql://localhost/demo

hibernate.connection.driver_class=org.postgresql.Driver

hibernate.connection.show_sql=true

hibernate.connection.username=test

hibernate.connection.password=test


設定 6 database-hsqldb.properties

hibernate.dialect=org.hibernate.dialect.HSQLDialect

hibernate.connection.url=jdbc:hsqldb:hsql://localhost/demo

hibernate.connection.driver_class=org.hsqldb.jdbcDriver

hibernate.connection.show_sql=true

hibernate.connection.username=sa

hibernate.connection.password=


設定 7 project.properties增添相關屬性設定

#####################################

# hibernate properties

#####################################

maven.hibernate.properties=${basedir}/database-postgresql.properties

#maven.hibernate.properties=${basedir}/database-hsqldb.properties

maven.hibernate.quiet=no

maven.hibernate.text=no

maven.hibernate.drop=no

maven.hibernate.delimiter=;

maven.hibernate.output.dir=${maven.build.dir}/schema

maven.hibernate.output.file=${maven.hibernate.output.dir}/schema.sql


2 maven-hibernate-plugin設定

參數

說明

maven.hibernate.properties

hibernate參數設定檔放置路徑,無論直接進行資料庫的更新或單純產出Schema的文字檔,皆必須要進行該項設定主要以hibernate.dialect參數決定使用何種資料庫方言。

maven.hibernate.text

若為yes則執行Schema Export時將直接匯出SQL成文字檔,並不會進行資料庫的更新,若設為no則直接進行資料庫Schema的更新,預設為no

maven.hibernate.quiet

是否顯示詳細的Schema Export資訊,例:當maven.hibernate.text=nomaven.hibernate.quiet=yes時,將直接進行資料庫Schema的更新,Console下不會顯示任何訊息,預設為yes

maven.hibernate.drop

是否只執行drop資料表的動作,若maven.hibernate.text=yes則匯出SQL檔將只包含drop tableSQL語法。

maven.hibernate.delimiter

分隔SQL Commands的定義符號,預設值為空字串,而大多數狀況使用”;”作為分隔符號。

maven.hibernate.output.dir

maven.hibernate.text=yes,匯出檔放置資料夾,預設為${maven.build.dir}/schema

maven.hibernate.output.file

maven.hibernate.text=yes,匯出檔放置路徑,預設為${maven.hibernate.output.dir}/${maven.final.name}-schema.sql


2 PostgreSql SchemaExport結果

3 Hsqldb SchemaExport結果


結論

Hibernate是一個開放原始碼的物件關聯映射技術,針對了JDBC進行了輕量化的將關聯資料轉化為物件封裝,使得開發人員可以隨心所欲的使用物件導向思維來駕馭資料庫。而在本期的介紹中我們了解了使用Maven協助Hibernate建立Domain Object的一種應用,而除了本次範例的方式外,Hibernate也可以使用Middlegen的方式透過現有的資料庫Schema產生出Mapping file,又或著從Mapping file建立出SchemaJava Domain Object。因此可依專案的狀況進行彈性的調整。


Hibernate的應用可以切入在任何使用JDBC的場合,既可以應用於單純的Java Application,也可以在Servlet/JSPWeb應用程式中使用,更可以應用在EJBJ2EE架構中取代CMP,完成資料持久化的重任。倘若更深入Hibernate的架構與觀念中,則將更了解物件關聯映射技術的最佳化實踐。

Read More......

2007年7月29日 星期日

(2006.05月號--148期)開放原始碼專案管理工具_建立專案與IDE環境設定

在以往初步建立專案的過程中,多數傾向於二種方式,其一是依賴IDE,建立專案同時啟動專案精靈,顯示多個專案範本,藉由開發人員選擇範本的不同進行專案的初始化並建立符合需求的開發環境設定,並在IDE上進行建置、佈署、測試。這種方式的優點是方便、易學、無需了解複雜的環境配置即可進行。而缺點是受限於IDE的開發方式,只適用公司內部或單一公司實作的小型專案,也必須限制使用特定的IDE進行開發。對於大型或是多數委外進行開發的專案則不適用,因為我們無法強制委外的開發廠商使用統一的IDE進行開發。


其二是將專案建置邏輯撰寫於Ant 腳本中,由Ant協助進行程式碼的編譯、專案的佈署、單元測試、整合測試等的建置步驟。其優點是開發過程中完全不依賴IDE開發環境,因此在目前在大型的J2EE專案中幾乎都看的到Ant的蹤跡。而缺點是Ant的腳本並沒有一定的建置邏輯與規範,因此在目前業界中Ant腳本在各專案中可重覆使用的機率並不高,並且必須因應專案特性的不同而必須重寫或修改Ant的建置腳本,但在進行中大型專案裡維護Ant並不是件容易的事,而另一個缺點是Ant無法協助我們進行IDE環境的設定,因為充其量Ant只包含建置邏輯罷了。


在本期的內容中,筆者將為各位介紹Maven是如何協助我們從無到有建立出所需的專案開發環境,並且配合多個不同的IDE Plug-in協助我們建立EclipseJBuilderIntelliJ IDEAIDE開發環境的配置。

Maven Genapp Plugin

一般習慣使用IDE的開發人員,通常都是使用專案精靈工具,套用範本來建置專案。假若Maven專案的建置必須像Ant腳本一樣無中生有的自行撰寫的話,大概就不會有太多人嚐試用Maven進行開發了。而Maven Genapp Plugin如同專案精靈一樣,透過對話的方式,輔助我們建立新專案。


在開始實作之前,請先確認Maven環境中的Genapp Plugin的版本為何,但如何得知目前版本呢,可至[%MAVEN_HOME%/plugins]資料夾下查看是否有maven-genapp-plugin-x.x.jar或者在[${maven.home.local }/cache]下是否包含著maven-genapp-plugin-x.x的資料夾。我們可以從這二個地方即可得知目前版本訊息。至筆者截稿為止,版本為2.3,若讀者手上的版本稍舊的話,可透過執行maven plugin:download -DgroupId=maven -DartifactId=maven-genapp-plugin -Dversion=2.3進行下載更新。


為什麼要先確認Genapp的版本呢,其原因在於Maven在發展過程中有其建議的資料夾結構,參考資料結構 1,而在2.3之前版本中的專案範本,並沒有完全依照如此的資料夾結構進行設置。因此在建立出的專案中會與資料結構 1的格式稍有不同,但若不下載新版的maven-genapp-plugin也不影響往後專案的建置,因為Maven只建議開發人員,統一的資料夾結構對開發是有幫助的,若專案開發環境有自己的資料夾結構或是原先由Ant建置的專案欲移轉到Maven上進行開發,都可以依照專案原本的資料夾結構進行建置。


接下來參考圖 1執行maven genapp並依提示輸入,使用專案範本、專案根路徑名稱、專案ID、專案名稱、原始碼的Package名稱,當輸入完成後立即建置出所需的專案架構供開發人員進行下一步的開發。這樣的功能就如同IDE工具的專案精靈一樣方便,但有個問題,GenApp Plug-in究竟提供多少範本協助開發人員建置專案呢?這個部分可參考相關資源1GenApp官方網站,另外也可以直接查看[${maven.home.local }/cache/maven-genapp-plugin-2.2/plugin-resources]資料夾清單,相關內容說明可參考表 1


到目前為止我們已經可以很輕鬆的建立起一個新專案,而且完全不相依於任何IDE。假若Maven能順便協助開發人員將IDE開發環境設定好那可就更完美了!那是當然的,接著筆者將介紹Maven IDE Plug-in的使用。


1 範本清單

範本名稱

範本說明

default

簡易的應用程式專案。

ejb

簡易的EJB專案。

struts

Web應用程式專案並且以StrustMVC的架構。

struts-jstl

Web應用程式專案並且使用StrustJSTL架構。

struts-validation

Web應用程式專案並且使用StrustValidation架構。

tapestry

Web應用程式專案並且以TapestryMVC的架構。

web

簡易的Web應用程式專案。

web-jstl

簡易的Web應用程式專案並且使用JSTL

web-velocity

Web應用程式專案並且以VelocityMVC的架構。

complex

建立一個複雜的J2EE專案,產生multiproject架構。


資料結構 1 Maven建議資料夾結構

/

+- src/ --專案相關代碼路徑

| +- main/ --主要程式碼路徑

| | +- java/ --java原始碼路徑

| | | +- ...

| | +- resources/ --所需資源與設定路徑

| | +- ...

| +- test/ --測試程式碼路徑

| | +- java/ --java測試原始碼路徑

| | | +- ...

| | +- resources/ --測試用資源與設定路徑

| | +- ...

| +- site/ --建立專案站台設定路徑

| +- xdoc/

| +- ...

+- target/ --專案建置成品放置

| +- ... 與其他建置中暫存路徑

+- project.xml --POM設定檔

+- README.txt

+- LICENSE.txt


1 Maven Genapp

Maven Eclipse Plugin

透過圖 1的操作我們已經建立好套用web範本的MyWebApp專案,接著進入建立好的MyWebApp資料夾中並執行maven eclipse,這時Maven將協助開發人員建立Eclipse的開發環境設定。

當產生出環境設定之後,啟動Eclipse並於功能表中執行[File][Import…],進入匯入專案作業。在對話視窗中選擇[Existing Projects into Workspace]進行下一步,接著在對話框中[Select root directory:]選項執行[Browse],並指向產生專案的路徑[D:DevMyWebApp],最後按下[Finish]即可。


第一次在Eclipse中匯入Maven建立的專案時,Eclipse[Problems]標籤將會判斷出多項錯誤,分別是[The project cannot be built until build path errors are resolved]與多個[Unbound classpath variable: 'MAVEN_REPO/xxxx/jars/xxxx-xx.xx.jar' in project MyWebApp MyWebApp]。這些錯誤的原因在於Maven使用了MAVEN_REPO參數設定相關JAR檔的關聯,而Eclipse開發環境中尚未設定該參數所致。


解決方式有二種,其一執行[maven eclipse:add-maven-repo -Dmaven.eclipse.workspace=%WORKSPACE_PATH%]可透過Maven Eclipse Plugin進行MAVEN_REPO的參數設定至[%WORKSPACE_PATH%]指定的workspace路徑下。執行完成後,必須重新啟動Eclipse才會生效。


其二可參考圖 2直接在Eclipse功能表中點選[Window][Preferences],在對話視窗中選擇[Java][Build Path][Classpath Variables]進行Classpath參數的設定,新增MAVEN_REPO參數,並將路徑指向[${maven.home.local }/repository]下即可。Maven預設repository的路徑指向[C:Documents and Settings%登入帳號%.mavenrepository]


當完成設定後Eclipse將依目前設定重新建置專案,結果如圖 3所示,這時之前的錯誤訊息將不會再出現。因此日後在專案開發的過程中當我們修改過project.xmlPOM設定或者新增、移除JAR的的相依關聯性時,只需再次執行maven eclipse就可依project.xml的設定重新產生相符的Eclipse開發環境配置。如此的Plugin不管在那個專案中都非常方便。


2 設定Eclipse MAVEN_REPO參數


3 開啟專案的Eclipse IDE

Maven JBuilder Plugin

目前業界進行J2EE專案開發最常用的IDE,除了Eclipse之外我想另一個廣受歡迎的就是JBuilder吧。在此也不避諱的為各位介紹一下如何使用Maven JBuilder Plugin協助我們建立環境設定。


在此我們直接執行maven jbuilder,若無意外的話將會看到BUILD SUCCESSFUL的訊息並且產生本範例中的[D:DevMyWebAppMyWebApp.jpx]的環境設定檔,但若發生BUILD FAILED的狀況的話,筆者大概可以猜測出讀者應該使用的是20052006JBuilder版本。


其原因在於截至目前為止,Maven JBuilder Plugin的版本為1.5,在目前的版本中預設並未支援JBuiler2005之後的版本設定。但若是因為如此而造成JBuilder的愛用者對Maven興趣缺缺的話,那可就是筆者的罪過了。


雖然預設的Plugin尚未支援新版的JBuilder,但只需進行小部分修正即可排除這個問題。首先至[${maven.home.local }/cache/maven-jbuilder-plugin-1.5]路徑下開啟plugin.jelly檔案,在檔案開頭附近很容易可以找到有一行的設定如[value=".jbuilderX,.jbuilder9,.jbuilder8,...]將其修改為[value=".jbuilder2005,.jbuilderX,.jbuilder9,.jbuilder8,...]參考設定 1,存檔後再次執行maven jbuilder即可。


這時我們將會發現Maven JBuilder Plugin正確無誤執行完成,也就是說事實上JBuilder每一個不同版本在安裝時會將相關設定檔存放至UserHome下,例如JBuilder2005存放路徑為[C:Documents and Settings%登入帳號%.jbuilder2005],因此我們只需將設定的版本名稱加入plugin.jelly中就可使Maven JBuilder Plugin支援該版本。


接著只要在JBuilder中執行[Open Project…]開啟由Maven產生的MyWebApp.jpx,無需調整任何設定即可進行專案開發,參考圖 4


設定 1 maven-jbuilder-plugin-1.5/plugin.jelly

<goal name="jbuilder"

description="Generate JBuilder project files"

prereqs="jbuilder:generate-library, jbuilder:generate-project"/>

<def:taglib uri="jbuilder">

中間省略…

<def:tag name="getDefaultProject">

<j:set var="jbuilderDirs" value=".jbuilder2005,.jbuilderX,.jbuilder9,.jbuilder8,.jbuilder7,.jbuilder6,.jbuilder5,.jbuilder4,.jbuilder" />

<j:set var="${userHome}" value="null" scope="parent"/>

以下省略…


4 開啟專案的JBuilder IDE

Maven IDEA Plugin

介紹完二個主流IDE的設定之後,我們來看一下一個鮮為人知,但功能卻不俗的IDE開發工具IntelliJ IDEAIDEA無需像Eclipse必須自行加上一堆Plugin之後才能達到較完善的開發環境要求,預設的IDEA環境非常的友善,上手非常容易,並且相似於Eclipse,網路上有眾多的Plugin支援IDEA。另外在所有的IDE開發工具中IDEA擁有最強的Refactor功能,方便開發人員進行重構。


Maven IDEA Plugin可以協助建立IDEA開發環境,但必須注意到目前IDEA Plugin版本為1.6,若讀者系統中的版本較舊,可透過執行[maven plugin:download -DgroupId=maven -DartifactId=maven-idea-plugin -Dversion=1.6]進行下載。


執行maven idea後,啟動IDEA進行[Open Project…]開啟由Maven產生的MyWebApp.ipr無需調整任何設定即可進行專案開發,參考圖 5


5 開啟專案的IntelliJ IDEA IDE

結語

在本期中介紹了如何使用Maven起始一個專案的建置,也了解了如何透過IDE PluginMaven的專案導到各個主流的IDE開發環境中進行協同開發,從這些實作中我們了解到,Maven並不會在開發中完全取代IDE或者限制使用特定IDE,而是以一種通用的專案建置方式協助每一個專案的進行。開發人員可以使用任何方便的IDE工具進行開發,而專案的建置、管理、測試、佈署等,一切的一切交給Maven就搞定了。

Read More......

2007年7月1日 星期日

(2007.6月號-161期)_持續性整合開發導論(下)

持續性整合的實踐:選用合適的CI伺服器

持續性整合開發可不是有了專案建置工具與貫徹測試優先原則就沒事了。要記住現在不是單打獨鬥的時代,講求團隊合作,因此不論是公司內參與的開發人員或者外部協同合作的廠商,都會對開發的成果產生重大的影響,因此如何即時掌握專案整體的開發狀況就是另一項挑戰。


Continuous Integration Server又稱CI伺服器,就是用來管理協同開發的持續性整合工具,目前常見的有:

  • CruiseControlJava社群中老字號的CI工具,擁有完整的CI所需功能。

  • Continuum:為Maven的子專案,算是後起之秀,設定方便為其優點。


CruiseControl它於2001年發佈已經有五年多的歷史,在許多方面,CruiseControl 伺服器 已經成爲持續整合的同義詞,完善的對大多數版本控制伺服器的支援並方便進行擴充,安裝容易也是其優點,如果硬要說個缺點的話就是CruiseControl對於專案持續性整合是基於XML的設定,非常靈活而且彈性,但對不熟悉的人來說,有點困難,包含的Web的管理介面,對於專案的掌控來說非常方便。


Continuum2005年發佈是最新的CI 伺服器之一,支援AntMaven1Maven2Shell進行專案的建置,也支援了大多數的版本控制伺服器,同CruiseControl一樣也擁有Web管理介面,並且可直接在Web介面上進行CI伺服器的設定,相對於CruiseControl來說比較方便,但以靈活度與擴展性來說,就比較差了,且因為推出時間較短,某些功能還不齊全。


到此我們可知完整的持續性整合開發環境至少必須架設版本控制伺服器與持續性整合伺服器,參考圖 1所示,開發人員與協力廠商開發平台持續對版本控制伺服器提交修改完成的程式碼,而通常在提交前會先取得目前最新的原始碼進行測試,當確認不影響版本控制伺服器上的原始碼後才進行提交。持續性整合伺服器可設定特定時間,當發現原始碼已經發生變化時,執行取得原始碼動作,並進行編譯與測試,此時將可完整的知道目前開發專案的穩定狀態,可設定持續性整合伺服器當專案建置成功或失敗時通知相關人員即時進行問題排除。


通常在CI伺服器發生建置失敗的原因,除了一般的程式碼錯誤外,開發人員未提交所有程式碼檔案或未解決程式碼衝突就提交也是原因之一,前者是開發人員針對多個原始碼檔案進行了修改,但因經過了一段時間,已經不確定到底有那些檔案需要提交而造成的問題。後者是當程式碼在版本控制伺服器上同一個時間有多個開發人員進行了修改,後者提交時會提示發生了檔案衝突,而開發人員未解決重覆提交產生的問題。這些問題的發生事實上都是開發人員對版本控制觀念的認知不足所致。無論取得或提交原始碼時需對整個專案進行,才不會遺漏掉必須提交的檔案。版本衝突時相對於版本控制系統都會有一套解決衝突的流程,只要小心都能避免。



1 持續性整合開發環境


持續性整合的分析:產生可供評估的報表

開發過程中文件的產生是很重要的事,開發前期的系統分析、系統設計文件,開發中的原始碼進度與狀況追蹤報表,開發後期的元件使用手冊、系統操作手冊,無論是任何一份文件都是目前專案開發所需要的重要參考。但傳統軟體專案的開發多數文件的產生都是由人手工去撰寫的,若要在開發人員在開發過程中還必須挪出時間進行文件的撰寫,對於開發人員來說是非常痛苦的事。


更重要的是,開發過程中程式碼會變,所產出的文件當程式碼一改變可能沒多久就過時了。因此在業界有多數狀況都是專案開發前期非常認真的撰寫相關文件,開發中期發現文件內容跟不上程式碼的變化索性就暫時放一邊,打算等之後再統一寫,到了開發後期要不就是因為專案的Delay或失敗而成為一個沒有文件的專案,就是根本忘了到底新增了那些功能而成為一份殘缺不全的文件。


因此在多數的敏捷開發原則中就提出了,最好的文件就是原始碼,首先為了讓原始碼在日後維護容易,必須在撰寫程式碼時保持程式碼的可讀性,並且必須統一程式碼的風格,訂定統一的開發規範,在一定程度上降低對文件的依賴,另外透過在程式碼中順手加上的文件資訊也就是所謂的Java Doclet的註解型式,配合相關的Doclet工具,就可隨時產生符合當時狀況的Java Doc說明文件。


為了追蹤專案開發進度與協助程式碼進行分析與重構,在此列出常用的持續性整合開發程式碼分析工具:

  • CheckStyle:檢查程式碼撰寫風格是否符合規範。

  • PMD:靜態程式碼分析工具,可用於找出潛在錯誤程式。

  • JavaNCSS:協助在開發過程中檢查出程式碼的複雜度。

  • Cobertura:於執行期檢查執行與未執行程式的測試覆蓋率檢測工具。


CheckStyle是自動化程式碼風格檢查工具,在傳統的多人協同開發中,可以發現每位開發人員都有相異的程式碼風格,少數有經驗的開發人員在風格上都有一定程度的好習慣,但在程度參差不齊的參與者中,很難保證有一致的Code StyleCheckStyle工具設定了一份規則文件,於建置時期對程式碼進行檢查,將不符合規範的資訊列出,因此開發人員可透過這份資訊了解本身所撰寫的風格是否合乎需求,並進行改進。如此一來維護程式碼就變成一件容易的事,畢竟誰都不想維護一份有怪異風格的程式碼。參考圖 2所示的CheckStyle報表,提供了InfoWarning、與Error的訊息,並緊接著列出錯誤原因與行數。


PMD是一個Java原始碼分析工具,透過一系列的規則比較可以協助找出潛在的Bug,包含 16 個規則集合,涵蓋了所有 Java經常發生的問題,如不良的命名規則、無用而未移除的程式碼、藕合度分析、不良的異常使用…等。在此我們參考圖 3可看出,該PMD報告說明了有40個檔案找到了65個錯誤,並且列出了錯誤原因為import了未使用的類別,還有包含了一個空的Exception區塊。由此一來開發人員可移除無用的import而不會造成日後不使用相關jar檔後造成的編譯錯誤,處理Exception區塊以避免日後發生問題後無法得知究竟異常原因為何。當然除了PMD所內建的檢查機制都可以因專案的需求進行修改,並可自行撰寫新的規則。



JavaNCSS用於進行原始碼的複雜度分析,NCSSNon Commenting Source Statements也就是分析非註解的原始碼資訊,聽起來似乎沒什麼但實際上JavaNCSS首先幫我們列出了前30名程式碼行數最多的類別,其次列出了前30Method最多的類別,供開發人員分析是否包含了過多無用的程式碼或者該類別給予了太多的責任,由此進行重構的考量。另外最重要的部分列出了前30MethodNCSS資訊,通常當Method中的程式碼行數過長,相對的也代表了有較高的NCCNCC所指的是Cyclomatic Complexity Number意指為程式的複雜度,在MethodNCC愈高,事實上就愈不利於日後的維護,因此NCC的數值將是重構的重要依據。參考圖 4


Cobertura是西班牙語覆蓋的意思,這個工具可以協助在進行單元測試的過程中統計已測試過的類別與未測試過類別的資訊,參考圖 5中明顯可知,當測試覆蓋率到達100%時也就代表了該類別中所有功能都至少被完整執行過1次以上,而覆蓋率分為Line CoverageBranch Coverage代表著一般程式碼與有分支判斷的程式碼(例如:if elseswitch或迴圈等),當然分支愈多代表著該類別的複雜度愈高,撰寫高覆蓋率的測試程式碼就愈困難。在進行程式碼開發的過程中如何撰寫功能強大、程式複雜度低而高測試覆蓋率的系統是每位開發人員的理想,而透過工具的分析,可讓開發人員的理想更趨近於真實。


2 CheckStyle報表。

3 PMD報表。

4 JavaNCSS報表。

5 Cobertura報表


結論

持續性整合開發為專案帶來了高效率、可控管、靈活有彈性的開發方式,透過專案管理與建置工具的協助,能夠很輕鬆在專案編譯過程中產生所需的專案開發資訊與分析報表,這一切的一切都是自動化的。透過報表資訊的產出可以減少開發人員手工檢查的時間,帶來了更高的效率與生產力,透過持續性整合伺服器的快速回饋,可以即時掌握目前專案原始碼開發狀況,徹底解決了企業及大型專案協同開發的管理問題。記住所有專案的開發都會發生問題,但持續性整合開發能在專案進行過程中持續發現並盡速解決問題,在開發的每一步降低風險,要知道愈晚發生問題所衍生的維護成本會是早期發現的十倍甚至百倍。



作者介紹

盧建州 <jazz.lu0827@gmail.com>

有多年軟體開發經驗,注重軟體工程並善用Design Patterns。專研於JavaOpen Source解決方案、跨平台技術與其異質資訊系統整合。目前為自由工作者。

Read More......