Chapter 11. Seam 应用程序框架

翻译 2007年10月01日 09:34:00
(今天是2007年国庆日,能在今天加班搞农业普查数据处理,并抽闲翻译这样一篇seam文章,真的是特别特别有意义噢。)

通过写带标注的普通java类,seam使创建应用变得非常容易。这样的类不需要扩展任何特殊的接口或超类。通过提供一系列内置组件,我们可以进一步简化一些通用的编程工作。对这些内置组件的使用是通过设置xomponents.xml(适用于简单情况)或其他的设置。

在一个应用中,如果需要使用Hibernate 或 JPA对数据库进行基本操作,Seam 应用程序框架可以减少编码数量。

需要强调的是,这个框架极其简单,仅仅少量的简单易懂的、易于扩展的类。“魔法”就在seam本身-即使你创建seam应用时没有使用这个框架,你也在使用同样的魔法。

11.1. 前言

Seam 应用程序框架提供的组件可以用以下两种方式之一使用:

第一种,在components.xml安装、设置组件实例,就像我们使用其他类型seam组件。例如,以下components.
xml代码片段安装一个组件,可以对Person entity进行基本操作:

<framework:entity-home name="personHome"
                                             entity-class
="eg.Person"
                                             entity-manager
="#{personDatabase}">
                    <framework:id>#{param.personId}</framework:id>
</framework:entity-home>

如果这样看起来太像“在xml中编程”,亦可以这样使用扩展方式:

@Stateful
@Name(
"personHome")
public class PersonHome extends EntityHome<Person> implements LocalPersonHome {
        @RequestParameter String personId;
        @In EntityManager personDatabase;
        public Object getId() return personId; }
        public EntityManager getEntityManager() return personDatabase; }
}

第二种方式有较大优势:可以方便地加入额外函数,并覆盖内置函数(为便于扩展和定制,框架类已被仔细设计)。

第二个优势是,你的类可以是EJB stateful sessin beans(并非必须如此,只要你愿意,也可以是plain JavaBean components)。

截至目前,seam应用程序框架提供了四种内置组件:EntityHome 、HibernateEntityHome
for CRUD、EntityQuery 、 HibernateEntityQuery for queries。

Home 与 Query组件可以用在这些工作期中:session, event 或conversation。使用何种工作期取决于你的应用程序的状态模型。

seam应用程序框架仅适用Seam-managed persistence contexts。默认情况,组件将查找名称为entityManager的persistence context。

11.2. Home objects

一个Home object提供了对一个特定entity class的持久化操作(persistence operations)。假设有这样一个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 object提供了以下操作:persist(), remove(), update() and 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 entries。是的,这就是所需的全部代码!现在,如果我们想要显示、更新、删除数据库中的Person 记录,则需要把记录的id传给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.persist}"/>

                  <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);
        }

}

The Seam Application Framework

当然,Country 可能是一个由其他Home object管理的对象,例如,CountryHome。为了增加更多复杂操作(例如关联管理,等等),我们可以对PersonHome增加方法(methods)。

@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();
        }

}


如果一个操作成功完成,Home对象自动显示faces messages。为定制这些信息,我们再一次通过设置的方式:

<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>,不过我想把这些任务留给你。

11.3. Query objects

如果需要数据库中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>

或者,当person记录通过PersonHome保存、更新、删除后,为马上刷新查询:


<event type="org.jboss.seam.afterTransactionSuccess.Person">
    
<action execute="#{people.refresh}" />
</event>

以上例子显示了通过设置的方式达到复用。不过,对Query 对象来说,通过扩展的方式达到复用也是同样可以的。

11.4. Controller objects

在Seam应用框架中完全可选的部分是Controller 类和它的子类Entity-Controller HibernateEntityController 和 BusinessProcessController。它们提供了操作常用的内置组件及其方式的捷径。它们可以节省击键次数(自动添加字符),对新手学习seam的内在功能有很大的推动作用。

例如,Seam registration例子中的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;
      }

   }


}

如你所见,应用它们后并没有带来明显的改善... ...

以上是翻译Seam Reference Documentation 2.0.0 CR1第十一章的内容。
欢迎赐教。

Seam 是一种企业级Java 的应用程序框架

   我这是在一本书上看到的文章,推荐给大家看,喜欢seam的同志,可以去看下,真的不错! Seam 为你的应用程序中所有的业务逻辑定义了一种统一的组件模型。Seam 组件可能是有状态 的,包含与几...

Seam CRUD应用程序构架

Seam CRUD应用程序框架本质上是提供预打包的数据访问对象(DAO) 一:数据访问对象(DAO) 在Seam应用程序中,DAO是一个EJB3.0会话bean或者是一个提供引用可管理Entity...

Chapter 3 Protecting the Data(4):创建和使用应用程序角色

数据库角色用于管理数据库内部的访问和权限。数据库角色成员是能通过客户端软件如SSMS连接SQL Server的数据库用户,但是你可能希望授予某个特殊用户一些特权,但是仅限制于某个应用程序而不是SSMS...

chapter 4.1 进程,编写第一个windows应用程序

-----------------进程----------------------------------------- 进程定义:     一个内核对象,用来管理进程和统计信息     一个地...

嵌入式Linux应用程序开发详解(1-11)

  • 2014年07月29日 09:54
  • 10.7MB
  • 下载

<rich:modalPanel/> 在seam框架下的具体应用

步骤如下: 1.在WebContent的目录pages/tools下建立模版confirmModal.xhtml.具体内容如下(预先已经准备好alignCenter.xhtml文件): http:...

嵌入式Linux应用程序开发详解(1-11)

  • 2008年10月06日 09:31
  • 7.29MB
  • 下载

基于Windows8与Visual Studio11开发第一个USB驱动应用程序

USB ,是英文Universal Serial BUS(通用串行总线)的缩写,而其中文简称为“通串线,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在PC领域的接口技术。USB接口支...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章: Chapter 11. Seam 应用程序框架
举报原因:
原因补充:

(最多只允许输入30个字)