Seam学习(十一)-Seam应用程序框架

Seam学习(十一)-----Seam应用程序框架Seam应用程序框架Seam通过编写带有注解的简单Java类来让创建应用程序

Seam学习(十一)-----Seam应用程序框架
  Seam应用程序框架
   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))操作的组件:

<framework:entity-home name="personHome"
                       entity-entity-/>
也可以通过类的扩展

@Name("personHome")
public class PersonHome extends EntityHome<Person> {}
Home对象提供了如下的操作:persist()、remove()、update() 和 getInstance()。 在你能够调用 remove() 或 update() 操作之前,你必须首先使用 setId() 方法定义你感兴趣的对象的标识符。

我们可以直接从一个JSF页面使用一个Home,如下例:

<h1>Create Person</h1>
<h:form>
    <div>First name: <h:inputText value="#{personHome.instance.firstName}"/></div>
    <div>Last name: <h:inputText value="#{personHome.instance.lastName}"/></div>
    <div>
        <h:commandButton value="Create Person" action="#{personHome.persist}"/>
    </div>
</h:form>
通常,只用person 指明person漂亮得多,所以在 components.xml 中添加一行语句来实现。

<factory name="person"
         value="#{personHome.instance}"/>

<framework:entity-home name="personHome"
                       entity-/>
(如果我们使用配置的方法。) 或者,我们可以通过向 PersonHome 中添加一个 @Factory 方法来实现:

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

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

}
(如果我们使用类扩展的方法) 这个修改使我们的JSF页面简化如下:

<h1>Create Person</h1>
<h:form>
    <div>First name: <h:inputText value="#{person.firstName}"/></div>
    <div>Last name: <h:inputText value="#{person.lastName}"/></div>
    <div>
        <h:commandButton value="Create Person" action="#{personHome.persist}"/>
    </div>
</h:form>
好,这就可以用来创建新的 Person 实体了。是的,这就是所需的全部代码!现在,如果我们想显示,更新,删除数据库中已经存在的 Person 实体,我们需要将实体标识符传递给 PersonHome。页面参数是一种非常好的实现方式:

<pages>
    <page view-id="/editPerson.jsp">
        <param name="personId" value="#{personHome.id}"/>
    </page>
</pages>
现在,我们可以向JSF页面中增加其他的操作:

<h1>
    <h:outputText rendered="#{!personHome.managed}" value="Create Person"/>
    <h:outputText rendered="#{personHome.managed}" value="Edit Person"/>
</h1>
<h:form>
    <div>First name: <h:inputText value="#{person.firstName}"/></div>
    <div>Last name: <h:inputText value="#{person.lastName}"/></div>
    <div>
        <h:commandButton value="Create Person" action="#{personHome.persist}" rendered="#{!personHome.managed}"/>
        <h:commandButton value="Update Person" action="#{personHome.update}" rendered="#{personHome.managed}"/>
        <h:commandButton value="Delete Person" action="#{personHome.remove}" rendered="#{personHome.managed}"/>
    </div>
</h:form>
当我们没有带任何请求参数链接到该页面时,会显示"Create Person"页面,当我们为 personId 这个请求参数设定一个值时,会显示“Edit Person”页面。

假设我们需要创建一些 Person 实体,并且初始化这些人的国籍。我们可以通过配置很轻松地完成:

<factory name="person"
         value="#{personHome.instance}"/>

<framework:entity-home name="personHome"
                       entity-var="person">
    <h:column>
        <s:link view="/editPerson.jsp" value="#{person.firstName} #{person.lastName}">
            <f:param name="personId" value="#{person.id}"/>
        </s:link>
    </h:column>
</h:dataTable>
我们可能需要支持分页:

<framework:entity-query name="people"
                        ejbql="select p from Person p"
                        order="lastName"
                        max-results="20"/>
我们可以使用page参数来决定被显示的页面

<pages>
    <page view-id="/searchPerson.jsp">
        <param name="firstResult" value="#{people.firstResult}"/>
    </page>
</pages>
用于分页的JSF代码可能有点冗长,但仍然是便于管理的:

<h1>Search for people</h1>
<h:dataTable value="#{people.resultList}" var="person">
    <h:column>
        <s:link view="/editPerson.jsp" value="#{person.firstName} #{person.lastName}">
            <f:param name="personId" value="#{person.id}"/>
        </s:link>
    </h:column>
</h:dataTable>

<s:link view="/search.xhtml" rendered="#{people.previousExists}" value="First Page">
    <f:param name="firstResult" value="0"/>
</s:link>

<s:link view="/search.xhtml" rendered="#{people.previousExists}" value="Previous Page">
    <f:param name="firstResult" value="#{people.previousFirstResult}"/>
</s:link>

<s:link view="/search.xhtml" rendered="#{people.nextExists}" value="Next Page">
    <f:param name="firstResult" value="#{people.nextFirstResult}"/>
</s:link>

<s:link view="/search.xhtml" rendered="#{people.nextExists}" value="Last Page">
    <f:param name="firstResult" value="#{people.lastFirstResult}"/>
</s:link>
真实的搜索界面能够通过让用户输入一系列的可选的搜索标准来缩小返回的结果列表。Query对象通过让你指定可选的“约束”来支持这个重要的用例。

<component name="examplePerson" action="/search.jsp"/></div>
</h:form>

<h:dataTable value="#{people.resultList}" var="person">
    <h:column>
        <s:link view="/editPerson.jsp" value="#{person.firstName} #{person.lastName}">
            <f:param name="personId" value="#{person.id}"/>
        </s:link>
    </h:column>
</h:dataTable>
在底层实体发生改变后,可以通过监听 org.jboss.seam.afterTransactionSuccess 事件来刷新查询:

<event type="org.jboss.seam.afterTransactionSuccess">
    <action execute="#{people.refresh}" />
</event>
或者,在发生持久化、更新或者删除时,通过 PersonHome 来刷新查询:

<event type="org.jboss.seam.afterTransactionSuccess.Person">
    <action execute="#{people.refresh}" />
</event>
这个部分所有的例子都是通过配置来体现重用的,但是,对Query对象通过扩展来进行重用也是可行的。

11.4. Controller对象
Controller 类以及它的子类 EntityController,HibernateEntityController 和 BusinessProcessController 是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;
      }
   }

}
正如你所看到的一样,这不是什么惊世骇俗的提高...