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 Carving算法-Siggraph 2007

这边只介绍缩小算法,且是图像宽度缩小的算法,对于图像高度缩小,只需对图像转置处理。 该过程是通过迭代实现的,每次只能移除一列,迭代直到宽度满足要求。下面介绍寻找移除能量线的方法,需要注意的是,每次移除...
  • hjimce
  • hjimce
  • 2015年04月07日 10:20
  • 6493

Seam框架学习之一(Seam vs Spring -- state vs stateless)

Seam是JBoss 的新的框架,号称Java的ROR.下面我想对它的一些特性和Spring做一番比较.        Seam的概念是基于Component的,集成了JSF+EJB3.0以及它选用的...
  • undead_xu
  • undead_xu
  • 2007年04月04日 16:37
  • 2124

Seam 的一些必备知识

Seam 的一些必备知识 Seam框架本身基于JSF和EJB3.0,所以JSF和EJB3是必须要了解的内容,为了更好的使用JSF还需要了解Facelets(JSF和JSP相关)。 JSF的教程可以参考...
  • struts2
  • struts2
  • 2007年11月12日 18:24
  • 2941

图像接缝裁剪(seam carving)算法实现-SIGGRAPH 2007

seam carving是SIGGRAPH 2007数字图形学年会上,以色列两位教授提出的算法,用于实现“内容保留”的图像伸缩。 出自论文《Seam Carving for Content-Aware...
  • czl389
  • czl389
  • 2016年12月19日 19:21
  • 1735

Seam的HelloWorld 使用Seam开发简单的Web应用

Seam版Hello World 我们下面使用Seam框架开发一个HelloWrld的例子。 Seam的难不再Seam本身,而在于Seam使用的内容,Seam使用JSF和EJB3,所以想要使用Seam...
  • struts2
  • struts2
  • 2007年11月13日 21:40
  • 5995

关于使用eclipse搭建seam框架的方法

原文出处:http://www.zhaocx.com/%E6%9C%AA%E5%88%86%E7%B1%BB/%E5%85%B3%E4%BA%8E%E4%BD%BF%E7%94%A8eclipse%E...
  • yangxiaojuan7211235
  • yangxiaojuan7211235
  • 2012年06月14日 14:38
  • 1087

spring vs seam的老文章

转自:http://blog.csdn.net/hbqyk/archive/2009/04/05/4049634.aspx Spring 自从2003年发布以来,一直是Java开源框架的奇迹之一...
  • IT_ORACLE
  • IT_ORACLE
  • 2013年06月07日 15:43
  • 818

seam carving简介

Seam carving 中文翻译成缝切割,是一种用于图像缩放的技术。这种技术可以感知图像的内容,在缩小时保留图像中的重要内容;在放大时,放大图像的重要内容。因此,该技术在保留图像中重要物体方面相对于...
  • qoqo1008
  • qoqo1008
  • 2015年07月23日 10:09
  • 1207

seam从登陆开始的安全权限框架理解

1、对于seam的安全框架和权限控制一直不是很清楚,这几天专门学习了一下。seam使用登录时,一般讲登录名suername和passward绑定到#{credentials}组件中的suername和...
  • u012514606
  • u012514606
  • 2014年08月30日 17:35
  • 761

seam 下载

Seam默认下载文件会变成直接在网页中打开内容,主要是因为Response header的问题,需要手工调节下: @Name("outExcelDown") public class OutExc...
  • wushuang5566110
  • wushuang5566110
  • 2012年02月17日 00:26
  • 1489
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章: Chapter 11. Seam 应用程序框架
举报原因:
原因补充:

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