Seam應用程式框架

梧桐雨—168發表於2008-04-19

Seam通過編寫帶有註解的簡單Java類來讓建立應用程式的工作變得非常簡單,不需擴充套件任何特定介面和父類。但常見的程式設計任務還能進一步簡化,這是通過一組預先建立的元件進行的,它們能夠由 component.xml 檔案配置(最簡單的情況)或者類擴充套件而實現複用。

在一個Web應用程式中使用Hibernate或者JPA進行基本的資料庫操作時,Seam Application Framework(Seam應用程式框架) 能夠減少你需要書寫的程式碼量。

我們需要強調的是,這個框架非常的簡單,只是少量的易於理解和擴充套件的簡單類。 “魔力”來自於Seam自身 — 即使沒有用這個框架來建立任何Seam應用程式的時候,你也同樣用到這一“魔力”。

11.1. 簡介

有兩種不同的方法使用Seam Application Framework所提供的元件。第一種方法是像處理其他種類的Seam內建元件一樣,在 components.xml 中安裝和配置元件的例項。 舉例來說,下列 components.xml 中的片段安裝了一個能夠為 Person 實體執行基本的CRUD(建立(Create)、讀取(Retrieve) 、更新(Update)和刪除(Delete))操作的元件:


    #{param.personId}

如果上面的程式碼按你的口味來說太像“用XML程式設計”,你可以改為使用擴充套件:

@Stateful
@Name("personHome")
public class PersonHome extends EntityHome implements LocalPersonHome {
    @RequestParameter String personId;
    @In EntityManager personDatabase;

    public Object getId() { return personId; }
    public EntityManager getEntityManager() { return personDatabase; }

}

第二種方法有一個很大的優點:你能夠方便地新增額外的功能,覆蓋內建的功能(框架的類都精心設計以便於擴充套件和定製)。

第二個優點是:如果你喜歡的話,你的類可以是有狀態會話Bean(這不是必須的,也可以是普通的JavaBean元件,如果你喜歡的話)。如果你正在使用JBoss AS,你需要使用4.2.2.GA或更高的版本。

目前,Seam應用框架提供了四個內建的元件:用於CRUD操作的 EntityHomeHibernateEntityHome 以及用於查詢的 EntityQueryHibernateEntityQuery

你得編寫Home和Query元件,它們能在session、event或conversation作用範圍中執行,至於選擇哪個scope取決於你所希望在你的應用程式中使用的狀態模型。

Seam應用框架僅在Seam管理的持久化上下文中工作。預設情況下,這些元件會尋找一個叫做 entityManager 的持久化上下文。

11.2. Home物件

Home物件對特定的實體類提供持久化操作,假設我們有個可靠的 Person 類:

@Entity
public class Person {
    @Id private Long id;
    private String firstName;
    private String lastName;
    private Country nationality;

    //getters and setters...
}

我們可以通過配置定義一個 personHome 元件:

也可以通過類的擴充套件

@Name("personHome")
public class PersonHome extends EntityHome {}

Home物件提供瞭如下的操作:persist()remove()update()getInstance()。 在你能夠呼叫 remove()update() 操作之前,你必須首先使用 setId() 方法定義你感興趣的物件的識別符號。

我們可以直接從一個JSF頁面使用一個Home,如下例:

Create Person

First name:
Last name:

通常,只用person 指明person漂亮得多,所以在 components.xml 中新增一行語句來實現。



(如果我們使用配置的方法。) 或者,我們可以通過向 PersonHome 中新增一個 @Factory 方法來實現:

@Name("personHome")
public class PersonHome extends EntityHome {

    @Factory("person")
    public Person initPerson() { return getInstance(); }

}

(如果我們使用類擴充套件的方法) 這個修改使我們的JSF頁面簡化如下:

Create Person

First name:
Last name:

好,這就可以用來建立新的 Person 實體了。是的,這就是所需的全部程式碼!現在,如果我們想顯示,更新,刪除資料庫中已經存在的 Person 實體,我們需要將實體識別符號傳遞給 PersonHome。頁面引數是一種非常好的實現方式:


    
        
    

現在,我們可以向JSF頁面中增加其他的操作:

First name:
Last name:

當我們沒有帶任何請求引數連結到該頁面時,會顯示"Create Person"頁面,當我們為 personId 這個請求引數設定一個值時,會顯示“Edit Person”頁面。

假設我們需要建立一些 Person 實體,並且初始化這些人的國籍。我們可以通過配置很輕鬆地完成:






    #{country}

也可以通過擴充套件類

@Name("personHome")
public class PersonHome extends EntityHome {

    @In Country country;

    @Factory("person")
    public Person initPerson() { return getInstance(); }

    protected Person createInstance() {
        return new Person(country);
    }

}

當然,Country 是一個被其它的Home物件管理的物件,比如說,CountryHome

為了增加更多複雜的操作(聯合管理等等),我們可以向 PersonHome 中新增方法。

@Name("personHome")
public class PersonHome extends EntityHome {

    @In Country country;

    @Factory("person")
    public Person initPerson() { return getInstance(); }

    protected Person createInstance() {
        return new Person(country);
    }

    public void migrate()
    {
        getInstance().setCountry(country);
        update();
    }

}

當事務成功之後(呼叫 persist()update()remove() 成功後),Home物件會發出一個 org.jboss.seam.afterTransactionSuccess 事件。 通過監聽這一事件,我們可以在底層實體改變後,重新整理查詢。 如果我們只需要在特定的實體儲存、修改或刪除後重新整理特定查詢,我們可以監視 org.jboss.seam.afterTransactionSuccess. 事件( 是實體的名字)。

當一個操作成功時,Home物件可以自動地顯示Faces資訊,我們可以再一次通過配置來定製資訊。




    New person #{person.firstName} #{person.lastName} created
    Person #{person.firstName} #{person.lastName} deleted
    Person #{person.firstName} #{person.lastName} updated



    #{country}

或者擴充套件:

@Name("personHome")
public class PersonHome extends EntityHome {

    @In Country country;

    @Factory("person")
    public Person initPerson() { return getInstance(); }

    protected Person createInstance() {
        return new Person(country);
    }

    protected String getCreatedMessage() { return "New person #{person.firstName} #{person.lastName} created"; }
    protected String getUpdatedMessage() { return "Person #{person.firstName} #{person.lastName} updated"; }
    protected String getDeletedMessage() { return "Person #{person.firstName} #{person.lastName} deleted"; }

}

但是指定資訊最好的方法是把資訊置於Seam所知的resource bundle中(在預設情況下,這個bundle叫做 messages )。

Person_created=New person #{person.firstName} #{person.lastName} created
Person_deleted=Person #{person.firstName} #{person.lastName} deleted
Person_updated=Person #{person.firstName} #{person.lastName} updated

這樣方便進行國際化,從表現層的角度考慮也保持了程式碼和配置的整潔。

最後一步是使用 向頁面中新增驗證功能,我會把這個留給你們自己去實現。

11.3. Query物件

如果我們需要資料庫中所有 Person 例項的列表,我們可以使用Query物件,例如:

我們可以從一個JSF頁面中使用它:

List of people

我們可能需要支援分頁:

我們可以使用page引數來決定被顯示的頁面


    
        
    

用於分頁的JSF程式碼可能有點冗長,但仍然是便於管理的:

Search for people

真實的搜尋介面能夠通過讓使用者輸入一系列的可選的搜尋標準來縮小返回的結果列表。Query物件通過讓你指定可選的“約束”來支援這個重要的用例。




    
        lower(firstName) like lower( concat(#{examplePerson.firstName},'%') )
        lower(lastName) like lower( concat(#{examplePerson.lastName},'%') )
    

注意“example”物件的使用。

Search for people

First name:
Last name:

在底層實體發生改變後,可以通過監聽 org.jboss.seam.afterTransactionSuccess 事件來重新整理查詢:


    

或者,在發生持久化、更新或者刪除時,通過 PersonHome 來重新整理查詢:


    

這個部分所有的例子都是通過配置來體現重用的,但是,對Query物件通過擴充套件來進行重用也是可行的。

11.4. Controller物件

Controller 類以及它的子類 EntityControllerHibernateEntityControllerBusinessProcessController 是Seam Application Framework的可選部分。 這些類只是提供了一些訪問常用內建元件及這些元件方法的便利手段,它們能夠減少一些鍵盤輸入量,也為探索Seam內建豐富功能的初學者提供了一個非常好的跳板。

例如,這就是Seam註冊例項中的 RegisterAction

@Stateless
@Name("register")
public class RegisterAction extends EntityController implements Register
{

   @In private User user;

   public String register()
   {
      List existing = createQuery("select u.username from User u where u.username=:username")
         .setParameter("username", user.getUsername())
         .getResultList();

      if ( existing.size()==0 )
      {
         persist(user);
         info("Registered new user #{user.username}");
         return "/registered.jspx";
      }
      else
      {
         addFacesMessage("User #{user.username} already exists");
         return null;
      }
   }

}

正如你所看到的一樣,這不是什麼驚世駭俗的提高...

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/13270562/viewspace-244095/,如需轉載,請註明出處,否則將追究法律責任。

相關文章