為什麼我會突然開始關心起JavaEE 6呢?
實在是因為這次JavaEE 6所包裝的一些技術與標準讓我覺得 -- 也許JavaEE 6會是讓開發人員開始考慮回歸JavaEE 6標準主流的一個轉機。幾個吸引我的主題包括:模組化設計(Web Profile)的設計、CDI的引進、大量使用annotation開發、廢除EntityBean使用JPA以及JAX-RS Restful Service的標準。
我的想法是,試著開發出一個簡單的應用場景,看看使用JavaEE 6對開發人員是否有一些吸引力。我將場景設定為:使用EJB開發一個Business Object,其中應用JPA將資料庫中兩個table的一對多資料抓出;同時,為此EJB提供對外兩種介面:HttpServlet網頁的顯示與Restful Service應用程式查詢介面。
開發環境準備
這邊我所使用的開發工具與伺服器是Eclipse + GlassFish,如果你想要省事的話,可以到Oracle Enterprise Pack for Eclipse的網站下載最新版本的開發工具,其中已經將Eclipse與GlassFish Tools plug-in安裝預設安裝好。然後在這邊下載GlassFish 3.1版。
註:這篇不是Eclipse教學,我不會說明怎麼安裝Eclipse、設定Server...等,網路上已經有夠多的文件,有需要的人可以拜一下孤狗大神另外,我選用Oracle資料庫做為資料儲存的地方,你可以用下列DDL產生兩個所開發需要的table:
CREATE TABLE MASTER (
MASTER_ID NUMBER(22 , 0) NOT NULL,
COL1 VARCHAR2(30),
COL2 VARCHAR2(30));
ALTER TABLE MASTER ADD CONSTRAINT PK_MASTER PRIMARY KEY (MASTER_ID);
CREATE TABLE DETAIL (
DETAIL_ID NUMBER(22 , 0) NOT NULL,
MASTER_ID NUMBER(22 , 0),
COL1 VARCHAR2(30));
ALTER TABLE DETAIL ADD CONSTRAINT PK_DETAIL PRIMARY KEY (DETAIL_ID);
ALTER TABLE DETAIL ADD CONSTRAINT FK_MASTER FOREIGN KEY (MASTER_ID) REFERENCES MASTER (MASTER_ID);
Part I -- JPA開發
產生一個Dynamic Web Project -- BlogJEE6。
我們從底層部分開始做起,首先開啟Project Properties,在Project Facet中勾選JPA。
在Project上右鍵 --> JPA Tools --> Generate Entities from Tables。會有個對話框讓你指定DB connection、Schema以及Tables。下圖我指定了TimDB的Oracle DB連線,其中我們會用到的就是環境準備時使用DDL所產生的MASTER與DETAIL兩個table。
下一步,由於在兩個table之間有foreign key的設定,Eclipse自動幫我們抓出了中間的關係:
下一步,指定Entity的一些特性,特別需要注意的是,根據使用情境,我希望抓取Master時一次將Detail的資料也一併取出,因此Associations fetch指定為Eager;反之,可以指定為Lazy。
下一步,分別為你的兩個Entity個別設定,這裡因為沒有指定使用Oracle DB的SEQUENCE做為PK的值,我的Key generator使用的是 "none"。
完成之後,你會發現有兩個class自動被產生 -- Master與Detail,偷看一下內容吧:
@Entity
public class Master implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="MASTER_ID")
private long masterId;
private String col1;
private String col2;
//bi-directional many-to-one association to Detail
@OneToMany(mappedBy="master", fetch=FetchType.EAGER)
private List<Detail> details;
public Master() {
}
...
@Entity
public class Detail implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="DETAIL_ID")
private long detailId;
private String col1;
//bi-directional many-to-one association to Master
@ManyToOne
@JoinColumn(name="MASTER_ID")
private Master master;
public Detail() {
}
...
有看到許多的annotation嗎? JPA就是使用這些annotation來指定OR-mapping的方式,不需要額外的XML configuration檔案設計對應。首先,JPA的Entity class定義上,需要先指定@Entity代表此class需做OR-mapping,在此class中的property只要不是定義為transient的,都會被對應至table的欄位之中,其對應方式用@Column (name="<COLUMN_NAME>")。眼尖的你如果發現上面產生出來的code中,有些properties沒有被定義@Column,通常代表這些property的名稱就是和table中欄位名稱一致。
@Id被用來定義為Primary Key欄位,在我們的例子中,Primary Key是我們自己產生,所以並沒有指定產生的方式,若是有需要用DB內建的SEQUENCE或是自動欄位產生該值的話,可以使用@GeneratedValue(strategy=GenerationType.AUTO)(其實GenerationType有SEQUENCE、TABLE與IDENTITY可選,你需要根據你所使用的資料庫與Table做相對應的設計)。
在一對多與多對一的情境則使用@OneToMany與@ManyToOne的方式來指定。當你不是額外使用一個table紀錄master與detail table之間的關係時,@OneToMany與@ManyToOne的設定就需要互相呼應,先看在@ManyToOne這邊,需要@JoinColumn(name="MASTER_ID")指定在Table foreign key所設定的欄位名稱為MASTER_ID;而另外在@OneToMany的設定上,需要對應mappedBy="master",用來指定在Detail class中指向Master class的property名稱。
接下來,只剩下一個資訊要指定了,我們需要告訴GlassFish這個DB的位置在哪裡,一般來說大家都會利用container本身的data source與connection pool來管理資料庫連線,因此我們只需要指定這個OR-Mapping是使用到哪一個data source即可。
打開Project內的JPA Content --> persistence.xml (沒錯,少數我們還需要關心的XML檔案,其餘大部分都給annotation取代了),在connection tab下指定JTA data source的名稱。
記得你需要在GlassFish中設定好對應的data source與connection pool:
到此為止,你已經完成了database OR-Mapping的部分了,接下來,需要個business interface 讓前端的Servlet或Restful WS呼叫執行資料的產生或查詢,這裡我們將開發一個Stateless Session Bean做為此Business Object。
在Project中新建一個名稱為InfoBean的EJB 3的Session Bean:
自動產生的程式碼如下:
package blogjee6;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
/**
* Session Bean implementation class InfoBean
*/
@Stateless
@LocalBean
public class MasterFacade{
/**
* Default constructor.
*/
public InfoBean() {
// TODO Auto-generated constructor stub
}
}
從EJB3開始,我只要用@Stateless去指定該POJO為一EJB,夠簡單吧?
在EJB中使用JPA做簡單的查詢,我們只需要加入下列的code在class之中:
@PersistenceContext EntityManager em;
public Master queryMaster(long id) {
return em.find(Master.class, id);
}
特別注意的是,這裡使用了Dependency Injection的方式,用@PersistenceContext取得EntityManager物件,你可以試著使用em.persist()、em.merge()與em.remove()執行新增刪除與修改的動作。
到這裡你已經完成了傳統在business object、data access object所該做的事情了,比比看以前的EntityBean與SessionBean,是不是少了很多東西? EntityBean、value object、home interface... 等等討厭的東西看來都不見囉~
下一篇我們再來看看前端的HttpServlet與Restful Service該怎麼做吧!
沒有留言:
張貼留言