`
alienj
  • 浏览: 79794 次
  • 性别: 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

相关推荐

    JBoss Seam: Simplicity and Power Beyond Java EE

    - **第十三章:管理业务流程** - **流程定义**:说明如何定义业务流程,并将其集成到 Seam 应用中。 - **流程执行**:探讨如何控制业务流程的执行顺序和条件。 - **第十四章:状态管理式页面流** - **页面流...

    JBoss Seam 工作原理、seam和hibernate的范例、RESTFul的seam、seam-gen起步、seam组件、配置组件、jsf,jboss、标签、PDF、注解等等

    1.6. 一个完整的Seam 应用程序:宾馆预订范例............................................................................................................ 45 1.6.1. 介绍......................................

    seam in action

    保护Seam应用程序** 安全是现代Web应用程序不可或缺的一部分。Seam提供了强大的安全特性,包括认证、授权等功能,帮助开发者构建安全的应用程序。 **12. AJAX与JavaScript远程调用** Seam支持AJAX和JavaScript...

    Seam in Action - MEAP - 2008

    **JBoss Seam** 是一个针对 Java EE 的应用框架,旨在通过统一组件架构简化 Web 应用程序的开发过程。Seam 构建在 EJB 3 规范的基础上,充分利用了该规范中的创新变化,如注解而非容器接口、异常配置而非冗长的 XML ...

    Seam Framework Web开发宝典(第2版).zip

    Seam Framework是一个全面的Java EE应用框架,专为构建企业级Web应用程序而设计。它集成了许多关键的技术,如JSF(JavaServer Faces)、EJB(Enterprise JavaBeans)、CDI(Contexts and Dependency Injection)以及...

    Seam Tutorial文档

    Seam是一个强大的Java EE框架,它简化了企业级应用程序的开发过程。该框架通过整合现有的技术如JSF、EJB 3、JPA等来提供更为简洁和高效的开发模式。Seam提供了丰富的功能集合,包括会话管理、页面流、工作流集成以及...

    hibernate验证框架学习

    **Hibernate Validator** 是一个流行的 Java 验证框架,它实现了 **JSR 349**(即 Java Bean Validation API)规范,提供了强大的验证功能,帮助开发者确保应用程序中的数据符合预定的规则。本文档将详细介绍 **...

    JavaEye新闻月刊 - 2008年10月 总第8期.pdf

    Sun Microsystems在2008年第三季度遭受了17亿美元的净亏损,这反映了全球金融危机对IT行业的影响,同时也引发了关于Sun未来战略和市场地位的讨论。 1.8 JDK Logger问题 JDK中的日志系统Logger出现了一些问题,这...

    Java EE 6 Overview 新特性

    随着技术的不断演进,2006年发布的第五个版本正式更名为Java EE 5,并对整个Java平台进行了统一命名,分为SE、ME、EE三个主要分支。 Java EE 5 的主题是简化企业级应用开发的复杂度,而到了Java EE 6,这一理念得到...

    JPDL3.1规范手册[良少]

    这份手册不仅是一份关于jBPM 3.1指南第16章的翻译,更是作者结合自身对jBPM及JPDL(Job Business Process Definition Language)深入理解的一次全面扩展和解读。文档中包含了对JPDL规范的详细解释,并提供了丰富的...

Global site tag (gtag.js) - Google Analytics