`
alienj
  • 浏览: 77275 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

第13章 Seam 应用程序框架

阅读更多

13 Seam 应用程序框架

 

Seam真正地使通过写带注释的简单Java类来创建应用程序变得容易,它不需要继承任何特别的接口或超类。但是,我们能更进一步简化一些通用的编程任务,通过提供预先构建的组件集,它能通过配置components.xml(为非常简单的情况)或继承被重用。

 

当在一个网页应用程序中,使用Hibernate JPA进行基本的数据访问时,Seam应用程序框架能减少你需要写的大量代码。

 

我们应该强调框架是非常简单的,只是少数简单类,容易理解和继承。“魔术”是在Seam它自身——即使没有使用这个框架,当创建任何Seam应用程序时,你使用了同样的魔术。

 

13.1.介绍

 

Seam 应用程序框架提供的组件可以被使用在两种不同的方法中。第一种方法是在components.xml中安装并配置一个组件实例,就如我们用其他的种类的内建Seam组件已做的那样。例如,来自components.xml文件的下面片断安装了一个组件,它能对一个Person实体执行基本的CRUD(弄脏)操作:

 

<framework:entity-home name="personHome"

                       entity-class="eg.Person"

                       entity-manager="#{personDatabase}">

    <framework:id>#{param.personId}</framework:id>

</framework:entity-home>

 

 

如果你觉得看起来太象“在XML中编程”,你可以使用继承来代替:

 

@Name("personHome")

public class PersonHome extends EntityHome<Person> {

   

   @In EntityManager personDatabase;

   

   public EntityManager getEntityManager() {

      return personDatabase;

   }

   

}

 

第二个方法有一个巨大的优点:你能容易增加额外的功能,覆盖内建功能(为了扩展和自定义,框架类被小心设计)。

 

第二个优点是你的类可以是EJB有状态会话bean,如果你喜欢。(它们不必是,它们可以是你喜爱的简单JavaBean组件)如果你使用JBoss AS,你需要4.2.2.GA或以后的版本:

@Stateful

@Name("personHome")

public class PersonHome extends EntityHome<Person> implements LocalPersonHome {

   

}

 

你也可以使你的类成为无状态会话bean。在这个情形中,你必须使用注入来提供持久化上下文,即便它被称为entityManager

 

@Stateless

@Name("personHome")

public class PersonHome extends EntityHome<Person> implements LocalPersonHome {

   

   @In EntityManager entityManager;

   

   public EntityManager getPersistenceContext() {

      entityManager;

   }

   

}

 

 

在这个时候,Seam应用程序框架提供了四个主要的内建组件:用于CRUD(弄脏)操作的EntityHome HibernateEntityHome,相应的用于查询操作的EntityQuery HibernateEntityQuery

 

编写Home Query组件的目的在于它们可能与会话、事件或对话作用域一起运行。你使用那个作用域,取决于你希望在你的应用程序中使用的状态模型。

 

Seam应用程序框架只与受管理的Seam持久化上下文一起工作。在缺省时,组件会查找名为entityManager的持久化上下文。

 

 

13.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 组件:

 

<framework:entity-home name="personHome" entity-class="eg.Person" />

 

或者通过继承

 

@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-class="eg.Person" />

 

(如果我们使用配置。)或者通过增加一个@Factory方法给PersonHome

 

@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-class="eg.Person"

                       new-instance="#{newPerson}"/>

<component name="newPerson"

           class="eg.Person">

    <property name="nationality">#{country}</property>

</component>

 

 

或者用继承:

 

@Name("personHome")

public class PersonHome extends EntityHome<Person> {

   

    @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<Person> {

   

    @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.<name>事件(这里的<name>是实例的名字)。

 

<factory name="person"

         value="#{personHome.instance}"/>

<framework:entity-home name="personHome"

                       entity-class="eg.Person"

                       new-instance="#{newPerson}">

    <framework:created-message>New person #{person.firstName} #{person.lastName} created</framework:created-message>

        <framework:deleted-message>Person  #{person.firstName}  #{person.lastName}  deleted</framework:deleted-message>

    <framework:updated-message>Person #{person.firstName} #{person.lastName} updated</

framework:updated-message>

</framework:entity-home>

<component name="newPerson"

           class="eg.Person">

    <property name="nationality">#{country}</property>

</component>

 

或者用继承:

 

@Name("personHome")

public class PersonHome extends EntityHome<Person> {

   

    @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所知的一个资源“束”中(缺省时,“束”被命名为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

 

 

这使国际化成为可能,并且保持了你的代码和配置的描述关系的整洁

 

最后一步是给页面增加校验功能,使用<s:validateAll><s:decorate>,但是,我留给你去解决。

 

13.3. Query对象

 

如果我们需要在数据库中的所有Person实例的一个列表,我们可以使用一个Query对象,例如:

 

<framework:entity-query name="people"  ejbql="select p from Person p"/>

 

我们可以从一个JSF页面使用它:

 

<h1>List of 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>

 

我们或许需要支持分页:

 

<framework:entity-query name="people"

                        ejbql="select p from Person p"

                        order="lastName"

                        max-results="20"/>

 

我们会使用一个页面参数来决定显示页面:

 

<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" class="Person"/>

       

<framework:entity-query name="people"

                        ejbql="select p from Person p"

                        order="lastName"

                        max-results="20">

    <framework:restrictions>

        <value>lower(firstName) like lower( concat(#{examplePerson.firstName},'%') )</value>

        <value>lower(lastName) like lower( concat(#{examplePerson.lastName},'%') )</value>

    </framework:restrictions>

</framework:entity-query>

 

注意"example"对象的使用:

 

<h1>Search for people</h1>

<h:form>

    <div>First name: <h:inputText value="#{examplePerson.firstName}"/></div>

    <div>Last name: <h:inputText value="#{examplePerson.lastName}"/></div>

    <div><h:commandButton value="Search" 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>

 

 

或者,当pearson实体通过 PersonHome持久化、更新或删除时,只刷新查询:

 

<event type="org.jboss.seam.afterTransactionSuccess.Person">

    <action execute="#{people.refresh}" />

    </event>

 

不幸地,Query 对象不能很好地支持join fetch 查询——用这些查询的分页使用不被推荐,并且你不得不实现你自己的计算结果总数的方法(通过覆盖getCountEjbql())。

在这部分的例子通过配置已完全展示了重用性。然而,对Query对象通过继承的重用同样可能的。

 

13.4. Controller对象

Seam应用程序框架的一个完全可选部分是类Controller和它的子类EntityController,HibernateEntityController 和 BusinessProcessController。这些类无非为访问常用内置组件和内建组件的方法提供一些方便的方法。它们帮助节省一些键盘输入(字符能增加!),并为探索在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;

      }

   }

}

 

与你所看到的一样,这不是什么极其重大的改进……

分享到:
评论
1 楼 super0555 2011-01-25  
managed

相关推荐

Global site tag (gtag.js) - Google Analytics