第二步:撰寫JPA基本維護程式
撰寫 ProductItem 與 Category 兩支Bean程式,寫好getter/setter & constructor
/** * 產品基本資料 * @author TommyKao */ public class ProductItem { /** * 產品系統編號 */ private long id; /** * 產品編號 */ private String sku; /** * 產品名稱 */ private String name; /** * 產品描述 */ private String description; /** * 以下是 constructor 的宣告 */ protected ProductItem() { super(); } public ProductItem(String sku, String name) { super(); this.sku = sku; this.name = name; this.description = ""; } /** * 以下是 getter/setter 的宣告 */ public long getId() { return id; } protected void setId(long id) { this.id = id; } public String getSku() { return sku; } protected void setSku(String sku) { this.sku = sku; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } |
利用 Annotations 宣告 JPA 管理的 Entity ,相關 Annotations 的用法其實跟採用XML 宣告的 OR Mapping 非常類似,只不過Annotations 是直接寫在 Bean 裡頭,相應的宣告及案例如下:
Annotations | 說明與案例 |
@Entity | 宣告為一般的Entity |
@Id | 宣告 Entity Bean 對應到永續層(persistence) 的primary key |
JPA Annotations 方式除了由開法者自行輸入之外,也可以透過 Eclipse 的 UI 來設定。
開啟 JPA Structure 與 JPA Detail 兩個視景,如以下圖例,從圖例當中我們可以看到,當我們編輯 Java Bean 程式時,對應的 JPA Structure 視景當中可以看到對應的屬性(attributes),同時 JPA Details 可以看到可用的 JPA Annotations 設定清單,也就是說,我們可以點選JPA Structure當中的屬性然後在JPA Details當中設定適當的值即可。
以下圖例是透過JPA Structure & JPA Details設定的簡單案例,操作步驟說明如下:
JPA Structure當中點選Category 類別名稱,將可以看到JPA Detail 當中出現對應的設定選項,於 Map AS 下拉選項當中選定Entity,該設定會立即反應回對應的程式碼當中,並出現正確的JPA Annotations 宣告 @Entity
於JPA Structure當中點選Category 類別的 id 屬性,可以看到JPA Detail 當中出現對應的設定選項,於 Map AS 下拉選項當中選定Id,JPA Annotations 宣告 @Id會立即反應回對應的程式碼當,注意到,JPA Structure 視景當中看到的 id 屬性會出現一把鑰匙,表示為 primary key。
於JPA Structure當中點選Category 類別的其他屬性,可以看到JPA Detail 當中出現對應的設定選項,於 Map AS 下拉選項當中選定預設的Basic,並可於 Name 下拉選單中直接輸入該屬性對應到永續層的欄位名稱 ,這邊要注意的是,有些JPA Annotations Tag 並未出現在JPA Detail當中,例如 length,這部分就需要手動指定了。
設定完基本的 JPA Annotations 之後,請把 META-INFpersistence.xml 打開,內容當中可以看到前面設定的 Entity 已經設定包含在<class> … </class> 標籤中了。
<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> <persistence-unit name="JPASample1"> <class>sample.jpa.Category</class> <class>sample.jpa.ProductItem</class> </persistence-unit> </persistence> |
現在要著手開始寫MVC 當中的 Model 層服務,在撰寫 Model 層服務之前,建議各位先行定義開發的界面程式。
以下是本次演練要開發的服務
/** * ICatalogService 提供的標準服務介面定義 * @author TommyKao */ public interface ICatalogService { /** * 依照商品分類的 primary key 找到對應的分類資料 * @param id * @return */ public Category findCategoryByID(long id); /** * 儲存商品分類資料 * @param category * @return * @throws Exception */ public Category saveCategory(Category category) throws Exception; } |
介面定義完成後,開發者便可以著手開發核心商業邏輯了,基於 JPA 的核心商業邏輯分段說明如下:
public class CatalogService implements ICatalogService { /** * Singleton pattern */ private static CatalogService instance = new CatalogService(); /** * 永續層的進入點 EntityManagerFactory */ private static EntityManagerFactory entityManagerFactory; private CatalogService() { super(); if (entityManagerFactory==null) { try { // 取得 EntityManagerFactory entityManagerFactory = Persistence .createEntityManagerFactory("JPASample1"); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); } } } public static CatalogService getInstance() { return instance; } /** * 依照商品分類的 primary key 找到對應的分類資料 * @param id * @return */ public Category findCategoryByID(long id) { Category result = null; try { // 取得 EntityManager EntityManager em = entityManagerFactory.createEntityManager(); // 依照 primary key 找到對應的商品分類 result = em.find(Category.class, id); return result; } catch (NoResultException err) { return null; } } /** * 儲存商品分類資料 * @param category * @return * @throws Exception */ public Category saveCategory(Category category) throws Exception { Category persistNode = null; if (category != null) { persistNode = this.findCategoryByID(category.getId()); // 取得 EntityManager EntityManager em = entityManagerFactory.createEntityManager(); EntityTransaction etx = em.getTransaction(); etx.begin(); if (persistNode != null) { persistNode.setCaption(category.getCaption()); // 永續層已經有資料, 用 Merge 方式執行修改(update)動作 category = em.merge(persistNode); } else { em.persist(category); } etx.commit(); em.close(); persistNode = category; } return persistNode; } } |
執行測試程式,為了簡單起見,本例是採用 main方法撰寫測試程式,這邊要提醒各位的是,為了完整與方便日後測試,建議各位還是採用 JUnit 撰寫測試程式較為妥當,至於JUnit 測試程式該如何撰寫,還請各位另行參閱相關的文章。
範例程式如下:
/** * @param args */ public static void main(String[] args) { ICatalogService service = CatalogService.getInstance(); Category category = new Category("JAVA JPA"); try { category = service.saveCategory(category); System.out.println("saveCategory Caption : " +(category!=null?category.getCaption():"DATA NOT FOUND")); } catch (Exception e1) { e1.printStackTrace(); } Category result = service.findCategoryByID(1); System.out.println("findCategory Caption : " +(result!=null?result.getCaption():"DATA NOT FOUND")); } |
執行範例程式時,在 Console 當中應該會看到以下的錯誤訊息,原因在於資料庫定義與相關的資料表並未設定或者出使劃完成所致,因此,我們須要回頭把資料庫相關設定寫到 META-INFpersistence.xml設定檔之中。
META-INFpersistence.xml設定檔需補充設定資料庫連線方式如下。
設定完成後,再次執行測試程式,將可得到正確結果如下圖。
<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> <persistence-unit name="JPASample1"> <class>sample.jpa.Category</class> <class>sample.jpa.ProductItem</class> <properties> <!-- 資料庫類型 --> <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/> <!-- 資料庫 JDBC 驅動程式 --> <property name="hibernate.connection.driver_class" value="net.sourceforge.jtds.jdbc.Driver" /> <!-- 資料庫連線 URL --> <property name="hibernate.connection.url" value="jdbc:jtds:sqlserver://localhost:1433/jpadb" /> <!-- 資料庫連線使用者帳號 --> <property name="hibernate.connection.username" value="jpauser" /> <!-- 資料庫連線使用者密碼 --> <property name="hibernate.connection.password" value="jpauser" /> <!-- 資料庫 Scheam 有差異時的處理方式 --> <property name="hibernate.hbm2ddl.auto" value="update"/> </properties> </persistence-unit> </persistence> |
撰寫更多的商業邏輯程式,JPA 的 Persistence 主要是使用 Entity Manager 去執行一般的增、刪、改、查動作,相關的範例逐一說明如下。
設計標準介面,例如:查詢、新增、修改與刪除等介面,以下是本實例當中開立的部分案例。
public interface ICatalogService { /** * [PK查詢範例]依照商品分類的 primary key 找到對應的分類資料 */ public Category findCategoryByID(long id); /** * [新增與修改範例]儲存商品分類資料 */ public Category saveCategory(Category category) throws Exception; /** * [刪除範例]刪除商品分類資料 */ public void removeCategoryByID(long id); /** * [一般查詢範例]依照產品編號 (SKU) 找到對應的產品基本資料 */ public ProductItem findProductItemBySku(String sku); 其他… |
PK查詢範例:Entity Manager 有一個 find() method 可以直接用物件的 Primary Key 到永續層查詢對應的物件,實際用法與範例如下:
/** * [PK查詢範例]依照商品分類的 primary key 找到對應的分類資料 */ public Category findCategoryByID(long id) { EntityManager em = entityManagerFactory.createEntityManager(); // 依照 primary key 找到對應的商品分類 Category result = em.find(Category.class, id); return result; |
新增或者修改範例:若需要修改紀錄,建議採用 merge() method
/** * [新增與修改範例]儲存商品分類資料 */ public Category saveCategory(Category category) Exception { if (category != null) { Category persistNode = this.findCategoryByID(category.getId()); EntityManager em = entityManagerFactory.createEntityManager(); EntityTransaction etx = em.getTransaction(); etx.begin(); if (persistNode != null) { persistNode.setCaption(category.getCaption()); // 永續層已經有資料, 用 Merge 方式執行修改(update)動作 category = em.merge(persistNode); } else { // 新的資料資料, 用 Persist 方式執行新增(insert)動作 em.persist(category); } etx.commit(); em.close(); } return persistNode; |
刪除範例:若需要修改刪除紀錄時,請使用 remove() method 去整理各項分類
/** * [刪除範例]刪除商品分類資料 */ public void removeCategoryByID(long id) { // 取得 EntityManager EntityManager em = entityManagerFactory.createEntityManager(); EntityTransaction etx = em.getTransaction(); etx.begin(); Category category = em.find(Category.class, id); if (category!=null) { em.remove(category); } etx.commit(); em.close(); |
一般查詢範例:除了用PK查詢之外,若開發者希望依照其他的條件去查詢時,可採用HQL語法,HQL 跟SQL 語法非常類似,差異在於 SQL 的對象是資料表與欄位,而 HQL 的對象則是類別與屬性,以下為一般查詢的範例程式。
/** * [一般查詢範例]依照產品編號 (SKU) 找到對應的產品基本資料 */ public ProductItem findProductItemBySku(String sku) { String hql = "from ProductItem where sku=:sku "; // 取得 EntityManager EntityManager em = entityManagerFactory.createEntityManager(); Query query = em.createQuery(hql); // 建立查詢語法 query.setParameter("sku", sku); // 填入查詢條件 // 依照產品編號 (SKU) 找到對應的產品基本資料 ProductItem result = (ProductItem) query.getSingleResult(); return result; } |
/** * [一般查詢範例]依照產品編號 (SKU) 找到對應的產品基本資料 */ public ProductItem findProductItemBySku(String sku) { String hql = "from ProductItem where sku=:sku "; // 取得 EntityManager EntityManager em = entityManagerFactory.createEntityManager(); // 相同的語法可以濃縮成以下這行 ProductItem result = (ProductItem) em.createQuery(hql) .setParameter("sku", sku).getSingleResult(); return result; } |
待續...JPA 應用實例 <3>
2 則留言:
我編輯我的JavaBean,可是在JPA Structure 與 JPA Detail這二個視圖中,並沒有看到如大大所說的樣子,是空白的,不知道是怎麼原因,想請大大可否把你的這個專案打包放上網路上分享。謝謝。
圖片不見了
張貼留言