Chapter 11. Seam 应用程序框架

(今天是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第十一章的内容。
欢迎赐教。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值