D盘

workspace
posts - 165, comments - 53, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理


中文Linux9下安装Oracle9界面乱码
来源:   作者:

出现乱码是因为字符集的问题,将环境变量LANG改为en,然后再执行runinstaller。如果想在安装过程中使用中文环境,需要修改Disk1\stage\Components\oracle.swd.jre\1.3.1.0.0\1\DataFiles\Expanded\jre\linux\lib\font.properties.zh,将其中所有  
  -tlc-song-medium-r-normal--*-%d-*-*-c-*-gbk-0  
  替换为  
  -tlc-ZYSong18030-medium-r-normal--*-%d-*-*-c-*-gbk-0  
  并将LANG最好改为zh_CN,缺省的值是zh_CN18030,如果不改,在执行netca和dbca时还是乱码。  
  停住不动就不知道是什么原因了。




还有人这么说,不过没试哈:


在安装oracle前,root用户和oracle用户会遇到乱码问题,经高人指点问题解决,解决方式如下:在.bash_profile文件里添加

LANG=C

export LANG

例如:

[oracle@linuxapple ~]$ vi .bash_profile

 

# .bash_profile

 

# Get the aliases and functions

if [ -f ~/.bashrc ]; then

        . ~/.bashrc

LANG=C

export LANG

fi

 

# User specific environment and startup programs

 

PATH=$PATH:$HOME/bin

 

export PATH

unset USERNAME



 

posted @ 2008-02-19 17:55 巴西木 阅读(2067) | 评论 (1)编辑 收藏

近日在dell860上装redhat9,总是花屏,查阅很多资料仍无法解决,后来居然让这个帖子解决了哈,好神奇啊!

ATI显卡的机器上安装Linux花屏解决办法

2007-9-26 10:40:00查看学习心得

安装Redhat Linux AS4u3后,系统启动时花屏:

解决办法:

1.直接安装ati的驱动

或者

2.vi /etc/X11/xorg.conf

替换"ati" 成为 "vesa"

Section "Device"
IdentifIEr "Videocard0"
Driver "vesa"
VendorName "Videocard vendor"
BoardName "ATI ES1000"
EndSection

然后重新启动系统。



转自:http://cache.baidu.com/c?word=ati%2Ces%3B1000%2Clinux&url=http%3A//edu%2Ecnzz%2Ecn/NewsInfo/4607%2Easpx&p=c465c54ad5c606b90ebe9b7f0f409e&user=baidu

注意:在redhat9下xorg.conf 这个文件是不存在的,随便试了一个XF86Config居然对了,哈哈,狂喜。开始装oracle了

posted @ 2008-02-19 17:27 巴西木 阅读(438) | 评论 (0)编辑 收藏

发布时间:2008.02.03 04:41     来源:赛迪网    作者:xuyy_cn

JDO(Java Data Object)是JCP中较早开发出来并形成规范的JSR12,该规范对数据的持久化存储进行了一系列规范,并已有众多的商业产品和开源项目是基于该规范。作为一种需要引起重视的技术,研究并探讨其企业应用可行性是十分重要的。 

前言 

在企业级的应用开发中,常需要有良好的持久化技术来支持数据存储。通过良好的规范或API,将企业的领域业务对象进行持久化存储,大多采用O/R映射技术来进行模式化的数据转换及自动映射工作。 

JDO(Java Data Object)是JCP中较早开发出来并形成规范的JSR12,该规范对数据的持久化存储进行了一系列规范,并已有众多的商业产品和开源项目是基于该规范。作为一种需要引起重视的技术,研究并探讨其企业应用可行性是十分重要的。 

以下主要对JDO(JDO 1.0规范)的应用开发技术作扼要介绍,通过该文,可以由浅入深、并较为全面地了解JDO,掌握主要的技术细节及过程,理解其运行机制,并对企业级应用有个总体的把握,这将有助于企业应用软件的技术选型、体系架构及分析设计活动。 

该文适合企业应用架构师、及关心数据持久层设计开发人员。 

JDO基本思想及特点 

企业信息系统的一个重要问题是解决数据的存储,即持久化。在软件开发过程中,分析员分析领域业务,提取出领域业务模型,并对应设计出数据库中需要进行存储业务数据的数据库表及相应字段。 

并根据业务流程,设计业务处理逻辑单元,进行数据的加工、处理及存储、查询等业务。其中一个较为繁烦、枯燥的工作,就是处理大量的数据持久化代码。 

为了解决数据从业务对象层向数据存储层之间的转换工作,JDO提供了相应的开发规范及API,解决了由Java对象直接存储为数据库相应表的底层处理过程,有助于设计人员更加专注于面向业务流程、面向业务对象等较高层次的应用。 

由于采用JDO的映射机制,能降低了业务系统与数据存储系统的耦合,使得业务系统相对于关系数据库或对象型数据库,具有可移植性,同时,由于采用面向对象(而非传统的面向记录)的持久化技术,系统更为轻便、简洁,增强了可维护性。 

JDO应用示例及分析 

以下将通过一些示例,由浅及深地讲解JDO技术。 

临时对象与持久对象 

这是一个普通的业务对象的代码。 










package business.model; 
public class Book 

private String isbn; 
private String name; 
private Date publishDate; 
public void setISBN(String isbn) 

this.isbn = isbn; 

public String getISBN() 

return this.isbn; 

public void setName(String name) 

this.name = name; 

public String getName() 

return this.name; 

public void 
setPublishDate(Date pubDate) 

this.publishDate = pubDate; 

public Date getPublishDate() 

return this.publishDate; 






现在将它作为一个JDO中对象保存到数据库中。代码如下: 


Book book = new Book(); 
book.setISBN(“isbn-1234567”); 
book.setName(“Java设计模式”); 

PersistenceManager 
manager = persistenceManagerFactory. 
getPersistenceManager(); 
manager.currentTransaction().begin(); 
manager.makePersistence(book); 
manager.currentTransaction().commit(); 




Book类的实例book对JDO的API而言,就是一个持久对象。类Book是可持久类。那任何一个普通java类都是JDO的可持久类吗?不是的。只有具备以下的条件,一个对象才可以被JDO持久到数据库中。 

它所属类应标记为可持久的类,有以下两种方法: 

显式:实现接口,javax.jdo.PersistenceCapable即可; 

隐式:以Sun的JDO参考实现为例,Book.java类的相同路径下还须有Book.jdo文件。 


encoding = “UTF-8”?> 










并通过字节码增强工具(本例采用Sun的字节码增强工具)处理: 


javac Book.java 
java com.sun.jdori.enhancer.Main 
Book.class Book.jdo 




通过上述两种方法,获得的Book.class才是一个可持久的类。 

字节码增强的有如下功能:当应用程序通过set方法修改某个字段1时,由于通过增强过程,在其内部插入了某些代码,JDO会获得数据状态变化的信息,从而在持久过程中,进行有选择性的处理。 

按照JDO规范,增强后的类可以在不同的JDO实现上使用,而无需重新编译或增强。 

并不是所有Book对象都是持久对象,只有当makePersistence后,该对象才是持久对象,并会通过JDO实现存储到数据库中。通过JDO的供应商扩展标记符(vendor-extension),可详细描述Book类的存储特性,如为该可持久类指定数据库表和对应字段。 

持久对象查询 

JDO查询主要有以下两种方式。 

使用Extend查询 

Extend可以查询指定类及子类的持久对象。 


PersistenceManager 
manager = persistenceManagerFactory. 
getPersistenceManager(); 
manager.currentTransaction().begin(); 
Extend extend = 
manager.getExtend(Book.class,true); 
//true表明同时查询子类 
Iterator it = extend.iterator(); 
while(it.hasNext()) 

Book book = (Book)it.next(); 
System.out.println(book.getISBN()); 

extend.closeAll(); 
manager.currentTransaction().commit(); 




Extend查询方法,提供了一种基于类的查询途径,它可以与下面的Query构成更为强大的查询。 

使用Query查询 

Query可以指定过滤条件,是一种常用的查询方式。 

下例是查找条件为“书名以‘Java设计模式’开头且出版日期小于今天”的书籍。 


String filter = 
“((String)name).startsWith(\”Java设计模式\”) 
&& publishDate < today”; 
Query query = 
pm.getQuery(Book.class,filter); 
query.declareImports(“import java.util.Date”); 
query.declareParameters(“Date today); 

Date today = new Date(); 
results = (Collection) 
query.execute(today); 
//传入参数值today 
if (results.isEmpty()) 

System.out.println(“No data!”); 
}else{ 
Iterator it = results.iterator(); 
while(it.hasNext()) 

Book book = (Book)it.next(); 
System.out.println 
(“Book Name:” + book.getName() 
+ “, ISBN:” + book.getISBN()); 






注:该条件使用了一个变元‘today’,通过“declareParameters”来声明该变量,并在“execute”方法中传入该变量的实例。这种带参数的查询,很类似于我们以前采用JDBC的带?的查询方式。 

其中startsWith(String s)是JDO提供的标准字符方法,类似的方法还有endsWith(String s)。 

JDOQL:上述使用的就是一个JDOQL样例,JDOQL是JDO规范一个组成部分。使用JDOQL可以使用应用在不同的JDO实现上运行。为了解决JDOQL的某些不足,JDO规范提供了支持特定JDO供应商查询语句接口。 

查询排序 

下例是将查询结果按“出版日期降序、书名升序”进行排序。 


Query query = 
pm.newQuery(Book.class, filter); 

String orderStr = 
“publishDate decending, name ascending”; 
query.setOrdering(orderStr); 

results = query.execute(today); 


对象更新 

当客户端对业务数据进行了更新后,需要通过业务过程将其更新到持久层中。这有两个过程,首先根据主键找到该实例,接着更新字段及提交。如下例,将指定书目编号的书本的出版日期进行更改。 












public void updateBookPublishDate 
(String isbn, Date newDate) 

PersistenceManager pm = null; 
try{ 
pm = pmf.getPersistenceManager(); 
Object obj = 
pm.newObjectIdInstance(Book.class,isbn); 
Book book = 
(Book)pm.getObjectById(obj,true); 
book.setPublishDate(newDate); 
}catch(Exception e) 

xxxContext.setRollbackOnly(); 
throw new Exception(e); 
}finally{ 
try{ 
if (pm != null && !pm.isClosed()) 

pm.close(); 

}catch(Exception ex) 

System.out.println(ex); 






注,在PersistenceManager使用newObjectIdInstance()方法时,JDO是如何知道通过书目编号ISBN来找到该对象呢?其实在本可持久类Book的jdo描述文件中,还需提供如下信息: 


encoding = “UTF-8”?> 



identity-type=“application” 
objectid-class=“BookKey” > 
primary-key=“true”/> 







其中“identity-type=“application””声明可持久类Book采用程序标识方式,即应用程序传入ID(字段isbn为“primary-key”)信息,JDO实现构造出指定的“objectid-class”的实例(即newObjectIdInstance过程),并由JDO来检索出指定的持久化对象(即getObjectById)。 

BookKey类源码如下: 


package businesss.model; 
public class BookKey implements 
java.io.Serializable 

public String isbn; 
public BookKey() 


public BookKey(String oid) 

isbn = oid; 

public String toString() 

return isbn; 

public Boolean equals(Object obj) 

return isbn.equals 
((BookKey)obj).isbn); 

public int hashCode() 

return isbn.hashCode(); 






符合 JDO 的“objectid-class”类,如“BookKey”,须具备以下条件: 

类声明为 public,并实现 java.io.Serializable; 

带有一个公有且不带参数的构造方法; 

当字段作为主键时,须有公有的,且名称和类型与持久类的字段一致,如:public String isbn; 

equals 和 hashCode 须使用全部(特指多字段的联合主键)的主键字段值; 

类必须有一个构造方法,与 toString 方法的处理过程是逆向过程;即将 toString 的输出值,作为该构造方法的输入值,又可以重新生成该实例(如构造方法“public BookKey(String oid)”)。 

综上所述,如果Book由两个字段作为主键,如isbn和name,则可能的代码是pm.newObjectIdInstance(Book.class,isbn+“#”+name),且BookKey的构造方法作相应更改,并有两个公有字段“isbn”和“name”。 

对象删除 

对象删除采用方法deletePersistence。示例如下: 








pm.currentTransaction().begin(); 
Object obj = 
pm.newObjectIdInstance 
(Book.class,isbn); 
Book book = 
(Book)pm.getObjectById(obj,true); 
pm.deletePersistence(book); 
pm.currentTransaction().commit(); 




获得PersistenceManager实例 

上述的所有操作与需要PersistenceManager实例,它可以在两种环境方法下获得:非受管环境和受管环境。 

非受管环境 

非受管环境是多指两层开发模式,应用程序直接获得资源对象,进行业务操作。一般事务管理、安全管理或资源管理都需要应用程序自行维护。 


Properties properties = 
new Properties(); 
properties.put(“javax.jdo. 
PersistenceManagerFactoryClass”, 
“com.xxx.jdo.xxxPMFClass”); 
properties.put(“javax.jdo. 
option.ConnectionURL”, “xxx”); 
properties.put(“javax.jdo. 
option.ConnectionUserName”, “xxx”); 
properties.put(“javax.jdo. 
option.ConnectionPassword”, “xxx”); 
PersistenceManagerFactory pmf = 
JDOHelper.getPersistence 
ManagerFactory(properties); 
PersistenceManager pm = 
pmf.getPersistenceManager(); 




与JDBC获取类似。 

受管环境 

受管环境一般是多层开发模式,尤其是处于J2EE应用环境中,程序通过容器获得资源对象,进行业务操作,由于在容器环境下,事务、安全及资源管理都由容器进行统一集中管理,从而简化了代码结构。 

以下是EJB(EntityBean、SessionBean、MessageDrivenBean)中的setXXXContext中的代码示例。 


private PersistenceManagerFactory pmf; 
public void setXXXContext 
(XXXContext context) 

try{ 
InitialContext ctx = 
new InitialContext(); 
Object obj = ctx.lookup 
(“java:compenvjdofactory”); 
pmf = (PersistenceManagerFactory) 
PortableRemoteObject.narrow 
(o,PersistenceManagerFactory.class); 
}catch(NamingException e) 

throw new Exception(e); 






PMF是如何绑定到J2EE环境下的JNDI上,有兴趣可参考JCA相关的技术文档。 

事务管理 

事务管理及使用,主要有以下三种情形。 

使用JDO事务的Bean管理情形 

一般在非J2EE环境下,使用该事务管理模式。 


PersistenceManager pm = 
pmf.getPersistenceManager(); 
pm.currentTransaction().begin(); 
//do some business with jdo 
pm.currentTransaction().commit(); 
pm.close(); 




使用JTA事务的Bean管理情形 

一般在J2EE环境下,采用Bean管理的事务情形下,使用以下方式。 

该方式可用在EJB的事务环境下。 


xxxContext.getUser 
Transaction().begin(); 
PersistenceManager pm = 
pmf.getPersistenceManager(); 
//do some business with jdo 
xxxContext.getUserTransaction().commit(); 
pm.close(); 


使用JTA事务的容器管理情形 

一般在J2EE环境下,采用容器管理的事务情形下,使用如下方式。 

如下是某个会话Bean的业务方法。 










public void doBusiness(){ 
PersistenceManager pm ; 
try{ 
pm = pmf.getPersistenceManager(); 
//do some business with jdo 
}catch(Exception e){ 
xxxContext.setRollbackOnly(); 
System.out.println(e); 
}finally{ 
try{ 
if (pm != null && !pm.isClosed()) 
pm.close(); 
}catch(Exception ex){ 
System.out.println(ex); 







综上,可以得出结论,JDO的操作完全与JDBC的操作相差无几。 

JDO高级应用 

复杂对象的持久化 

在实际的应用中,一个可持久化类要远比Book类复杂很多。它可能会引用其它的Java类型、类、集合或数组,及可能复杂的继承关系。当这些对象的状态发生变化时,JDO是如何感知及跟踪状态变化? 

JDO提供了相应的API及规范来实现该功能。 

基本类型及引用 

可持久化类的字段能被JDO实现进行持久化的原则。原始类型、java.util.Date等被支持(其它较为复杂或可选特性,详见JDO规范);如果引用是一个可持久类,则JDO进行持久化处理;通过元数据(如jdo文件)声明的字段,一般是非可持久化类的引用,JDO进行持久化; 

前面两种情形,当状态发生变化时,JDO会自动感知,但如果引用是非可持久化类,则需要代码进行显式通知,否则JDO不会将变化进行存储。如下例: 


public class Book 

…… 
private Object picture; 
public void setPicture(Object pic) 

picture = pic; 

public Object getPicture() 

Return picture; 






该类字段picture需要持久化,但java.lang.Object不是一个可持久类,故声明如下: 







persistence-modifier=“persistent”/> 







如果其它模块通过getPicture获得对象,并在JDO不可感知的外部,修改对象,则变化不会存储,较好的办法是修改setPicture方法,如下: 


public void setPicture(Object pic) 

JDOHelper.makeDirty(this, “picture”); 
picture = pic; 





并通过setPicture方法来更新数据。JDO的“makeDirty”方法,主要负责通知JDO实现,可持久化类Book某个实例(this)的“picture”字段发生了变化。 

集合 

可持久类的字段引用为集合时,按照JDO规范,强制支持java.util.HashSet,对HashMap、HashTable、TreeMap、TreeSet、LinkedList、ArrayList及Vector的支持对JDO实现而言是可选的,通过PersistenceManagerFactory的supportedOptions方法获得实现特性。 

数组 

如果可持久类的引用是数组类型,当数组单元发生变化时,需要调用“makeDirty”来通知JDO实现,该实例的数组引用内容发生了变化。与引用是非可持久类实例不同,不需要进行JDO的元数据声明。 

继承 

如果使用可持久性,一般继承的子类与父类都为可持久类,以减少系统复杂性,这时需要在子类的元数据中指出其可持久超类,如下: 



class name=“TechBook” 
persistence-capable-superclass=“Book”/> 




可为非持久类扩展持久类,或可为持久类扩展非可持久类;这两种情形JDO实现都将忽略非 

可持久类的字段部分,而不保存到数据库。 

JDO的一些不足之处 

JDO对数据的持久化技术相比于成熟的SQL,还有不足之处,这些不足在某些情况下将可能会影响数据处理部分的设计实现。以下列举了常用数据访问的必要功能 

查询增强 

如字符串不支持通配符、大小写比较; 

不支持聚合操作,无法实现MIN、MAX、SUM和AVG; 

不支持投影操作,JDO查询返回为对象,而不像SQL那样返回字段; 

不支持联合、交叉(UNION/INTERSECT); 

不支持JOIN、IN和EXISTS; 

企业应用探究 

由于JDO采用面向对象的持久化处理技术,从而为解决企业业务系统的持久化问题提供了一个新技术解决方案。但是先进的未必就最适用。在某些应用场景下,需要结合各种因素,采取灵活的策略。 

面向对象与面向记录 

现在大多业务系统都采用面向对象分析设计,这就需要每个应用系统都自行实现将对象映射成记录,并存储到数据库中,而有JDO这样的面向对象的持久化技术,在某种程度上解放了这种转化设计的不规范性,进而获得相对更优的系统结构。 

另一方面,一个业务系统的数据持久化,一般都有这样的过程:对象层->记录层->物理层,JDO无疑使分析设计人员从记录层的苦海中解脱出来,从而更加专注于对象层,这对开发人员无疑是一个令人欢欣鼓舞的技术。 

JDO并不能完全代替JDBC。 

根据经典的“8-2原理”,如果用JDO来解决80%的问题,余下的20%由JDBC来实现,这种相互补充,各取所长的策略,是一个很有效的办法。 

这样一方面可以获得较好的结构及提升开发质量,另一方面解决了JDO的某些技术不足,并可根据以后的技术变化,再做适当转化。 

性能问题 

JDO与JDBC究竟谁的性能更优,目前还没有一个权威、且令人满意的答案。但对于一些JDO实现而言,通过采用缓存机制,使得性能有了较大提高。 

跨数据库 

使用JDO的系统能更好地进行数据库移植,甚至可以在对象数据库上运行;当然JDBC处理层如果完全遵循SQL-92标准,也同样具有很好的跨数据库能力。 
           (责任编辑:包春林)

posted @ 2008-02-19 14:39 巴西木 阅读(230) | 评论 (0)编辑 收藏


本文是对Java常用域对象持久化技术的比较。在本文中介绍了域对象的概念,讨论了为什么要引入持久化技术,并对目前5种Java常用的域对象持久化技术进行比较,评价它们各自的优缺点和适用范围。

一. 应用程序的分层体系结构 
3层结构是目前典型的应用软件结构,3层即表述层、业务逻辑层和数据库层。其中表述层提供与用户的交互界面,GUI和Web页面是表述层的两个典型的例子;业务逻辑层实现各种业务逻辑;数据库层负责存放和管理应用的持久性数据。  
     在上述的三层结构中,业务逻辑层不仅负责业务逻辑,而且直接访问数据库,提供对业务数据的保存、更新、删除和查询操作。如果数据库改变或数据库的表结构发生变化,对业务逻辑层的影响非常大。为了把数据访问细节与业务逻辑分开,可以把数据访问作为单独的持久化层。持久化层封装了数据访问细节,为业务逻辑层提供了面向对象的API。完善的持久化层应达到以下目标: 
代码可重用性高,能够完成所有的数据库访问操作; 
能够支持多种数据库平台; 
具有相对独立性,当持久化层发生变化时,不会影响上层的实现。 
二. 软件的模型 
在软件开发领域,模型用来表示真实世界的实体。在软件开发的不同阶段,需要为目标系统创建不同类型的模型。在分析阶段,需要创建概念模型。在设计阶段,需要创建域模型和数据模型。 
构成域模型的基本元素就是域对象,即Domain Object,是对真实世界的实体的抽象。域对象可以代表业务领域中的人、地点、事务或概念。域对象包括3种:实体域对象、过程域对象和事件域对象。在三层应用结构中,以上3种域对象都位于业务逻辑层,实体域对象是应用的业务数据在内存中的表现形式,而过程域对象用于执行业务逻辑。 
当实体域对象在内存中创建后,他们不可能永远存在,因此必须对域对象进行持久化。狭义的理解,“持久化”仅仅是把域对象永久保存到数据库中;广义的理解,“持久化”包括和数据库的各种相关操作,如:保存、更新、删除、加载、查询等。 
三. Java中常用域对象持久化技术的比较 
目前Java中共有5种常用的实现持久化的模式: 
JDBC直接访问数据库 
主动域对象模式 
CMP模式 
ORM模式 
JDO模式 
1、JDBC直接访问数据库 
在这几种模式中JDBC的历史最为悠久,从Java诞生的那天起就有了JDBC,目前已经发展到JDBC3.0了。JDBC是一套规范,她规定了统一的标准接口,各个数据库厂商提供标准接口的实现。因此,只需要掌握标准的SQL语言就可以访问各种不同的数据库了。JDBC规范的出台,向世界宣告从此有了访问关系数据库的标准通用接口了。JDBC规范一经发布,获得了空前成功,很快成为java访问数据库的标准,JDBC获得了几乎所有数据库厂商的支持。这种数据库间的可移植性和Java一直高喊的口号Compile Once, Run everywhere遥相呼应。Java能有今天这么风光,JDBC可以说是功不可末。了。在没有JDBC的时候,各家数据库厂商都有自己的一套API,开发人员访问数据库非常困难,换个数据库,应用程序的修改量极大。JDBC今天还是java访问数据库的基石,本文中讨论的其他几种模式说到底只是更好的封装了JDBC, 提供了更为上层的更为强大的接口而已。 
JDBC实现数据库访问的方式是在业务方法中直接嵌入SQL语句,SQL语句是面向关系的,依赖于关系模型。所以JDBC方式的优点是简单直接,特别是对于小型应用十分方便。 
但是JDBC这种实现方式也给应用程序带来以下缺点: 
(1)、实现业务逻辑的代码和数据库访问代码掺杂在一起,使程序结构不清晰,可读性差。 
(2)、在程序代码中嵌入面向关系的SQL语句,使开发人员不能完全运用面向对象的思维来编写程序。 
(3)、业务逻辑和关系数据模型绑定,如果关系数据发生变化,必须手工修改代码中所有相关的SQL语句,这曾经了维护软件的难度。 
(4)、如果程序代码中SQL语句包含语法错误,在编译时不能检查这种错误,只有在运行时才能发现这种错误,这增加了调试程序的难度。 
正是由于上述的缺点,为了使业务逻辑和数据访问细节分离,出现了下面的几种模式。 

 

 

2、主动域对象模式 
主动域对象是实体域对象的一种形式,它在实现中封装了关系数据模型和数据访问的细节。在 J2EE 架构中,EJB组件分为会话EJB和实体EJB。会话EJB通常实现业务逻辑,而实体EJB表示业务实体。实体EJB又分为两种:由EJB本身管理持久化,即BMP(Bean-Managed Persistence);由EJB容器管理持久化,即CMP(Container-Managed Persistence)。BM P就是主动域对象模式的一个例子,BMP 表示由实体 EJB 自身管理数据访问细节。 
主动域对象模式有以下优点: 
(1)、在实体域对象中封装自身的数据访问细节,过程域对象完全负责业务逻辑,使程序结构更加清晰。 
(2)、如果关系数据模式发生变化,只需要修改主动域对象的代码,不需要修改过程域对象的业务方法。 
主动域对象模式有以下缺点: 
(1)、在实体域对象的实现中仍然包含SQL语句。 
(2)、每个实体域对象都负责自身的数据访问实现。把这一职责分散在多个对象中,这会导致实体域对象重复实现一些共同的数据访问操作,从而造成重复编码。 
主动域对象本身位于业务逻辑层,因此采用主动域对象模式时,整个应用仍然是三层应用结构,并没有从业务逻辑层分离出独立的持久化层。 
3.CMP模式 
在J2EE架构中,CMP(Container-Managed Persistence)表示由EJB容器来管理实体EJB 的持久化,EJB容器封装了对象-关系的映射及数据访问细节。CMP 和ORM的相似之处在于,两者都提供对象-关系映射服务,都把对象持久化的任务从业务逻辑中分离出来。区别在于CMP负责持久化实体EJB组件,而ORM负责持久化 POJO,它是普通的基于 Java Bean 形式的实体域对象。 
CMP模式的优点在于: 
(1)、他是基于EJB技术,是SUN J2EE体系的核心部分,获得了业界的普遍支持,包括各大厂商和开源组织等。如果选择它作企业级开发,技术支持会非常完备。 
(2)、功能日趋完善,包括了完善的事务支持,EJBQL查询语言,透明的分布式访问等等 
CMP模式的缺点在于: 
(1)、开发人员开发的实体必须遵守复杂的J2EE规范,而多少ORM中间件没有类似要求。 
(2)、实体域EJB只能运行在EJB容器中,而POJO可以运行在任何一种Java环境中。 
(3)、尽管按照J2EE的规范,EJB应该是一种可移植组件,实际应用时确受到很大限制。而ORM中间件就不存在这样的问题。 
4.ORM模式 
ORM-Object/Relational Mapper,即“对象-关系型数据映射组件”。对于O/R,即 Object(对象)和 Relational(关系型数据),表示必须同时使用面向对象和关系型数据进行开发。建模领域中的 ORM 为 Object/Role Modeling(对象角色建模)。另外这里是“O/R Mapper”而非“O/R Mapping”。相对来讲,O/R Mapping 描述的是一种设计思想或者实现机制,而 O/R Mapper指以O/R原理设计的持久化框架(Framework),包括 O/R机制、SQL自生成、事务处理和Cache管理等。 
一般把基于 Java Bean 形式的实体域对象称为 POJO(Plain Old Java Object),意为又普通又古老的 Java 对象的意思。随着各种 ORM 映射工具的日趋成熟和流行,POJO有重现光彩,它和基于 CMP 的实体 EJB 相比,既简单又具有很高的可移植性,因此联合使用ORM 映射工具和 POJO,已经成为一种越来越受欢迎的且用来取代 CMP 的持久化方案。POJO的缺点就是无法做远程调用,不支持分布式计算。 
常用的ORM中间件有:Hibernate、Apache OJB、Cayenne、Jaxor、TopLink等。其中 Hibernate 的轻量级 ORM 模型逐步确立了在 Java ORM 架构中领导地位,甚至取代复杂而又繁琐的 EJB 模型而成为事实上的 Java ORM 工业标准。而且其中的许多设计均被 J2EE 标准组织吸纳而成为最新 EJB 3.0 规范的标准。 
5.JDO模式 
JDO是近几年新兴的数据持久性技术,Java Data Objects(JDO)是 SUN 公司制定的描述对象持久化语义的标准API。严格的说,JDO 并不是对象-关系映射接口,因为它支持把对象持久化到任意一种存储系统中,包括关系数据库、面向对象的数据库、基于XML的数据库,以及其他专有存储系统。由于关系数据库是目前最流行的存储系统,许多JDO的实现都包含了对象-关系映射服务。 JDO模式的优点是: 
(1)、简单易用,不需要写大量无用的接口,不需要继承什么特殊的类,唯一所要做的就是对你的class文件做一下enhance。 
(2)、真正面向对象。用了JDO的java程序是真正的面向对象,无需再理会数据库里面有什么表格,存取都是以java object为对象,所有数据库表格都是自动生成的。 
(3)、方便的数据库移植。应用程序换数据库时候除了换一个JDBC driver, 换一下数据库URL,无需对程序做任何改动。 
JDO模式的缺点是: 
由于是新兴技术,一些IT巨头还没有给予足够的支持,其中包括IBM、Oracle、BEA。 
四. 总结 
关于这些技术优劣之争从它们刚刚出生那天起从来就没有停止过,而各家各派也从来没有能够说服过对方。对于应用开发者而言,撇开应用纯粹来争论技术优劣并没有多大意义。没有最好的,只有最合适的。在做开发的时候能够选择一个最合适于自己应用的技术,那就足够了。总的来说,JDBC面向RDBMS,比较适合关系数据库模式驱动的应用,例如统计表格数据,生成报表之类的应用。EJB 技术以J2EE应用服务器为中心,如果应用确实需要灵活的可声明的事务边界,需要支持大容量的访问和不间断的服务,需要应用服务器的集群,那么选EJB吧。JDO则面向对象,对于以域对象为中心的应用,包含图,树模型的应用,JDO是首选。

posted @ 2008-02-19 14:38 巴西木 阅读(370) | 评论 (0)编辑 收藏

作者 Michael Yuan译者 包亮 发布于 2007年11月2日 上午1时3分

社区
Java
主题
Web框架

本文节选了Michael Yuan和Thomas Heute所著的即将出版JBoss Seam: Power and Flexibility Beyond Java EE 5.0第一章和第二章,内容有所删减。

什么是Seam?

JBoss Seam是“Java EE 5.0的一个轻量级的框架”。这是什么意思?难道Java EE(Enterprise Edition) 5.0本身不是一套“框架吗”?为什么在官方规范之外,还需要另外一个框架?好吧,我们就将seam看作是本应该被包括在Java EE 5.0中的一个“遗漏的框架”吧。它在Java EE 5.0框架的上层,为所有的在企业Web应用中的组件提供了一个统一的、易于理解的编程模型。它同样使基于状态的应用和业务流程驱动的应用的开发易如反掌。换句话说,Seam致力于开发者生产力和应用扩展性。

1. 整合和强化Java EE框架

Java EE5.0的核心框架是EJB(Enterprise JavaBeans)3.0和JSF(JavaServer Faces)1.2。EJB 3.0(以下简称EJB3)是基于一个POJO(Plain Old Java Objects)的业务服务和数据库持久化的轻型框架。JSF是一个基于MVC(Model-View-Controller)的Web应用框架。大多数的Web应用都将包含有业务逻辑的EJB3组件和Web应用前端显示的JSF组件。EJB3和JSF虽然互补,但是他们是根据各自的理念设计的独立的框架。例如,EJB3使用注解(annotation)来配置服务,而JSF使用的是XML文件。更进一步讲,EJB3和JSF组件在框架层面上是互不敏感的。要整合EJB3和JSF,开发者必须手动地构造facade对象(如:JSF支持bean),将业务组件与Web页面和样板代码(又称plumbing代码)联结起来,以便能跨框架调用方法。将这些技术粘合起来是Seam的职责之一。

Seam打破了EJB3和JSF之间的人工层,它为整合EJB3和JSF提供了一个一致的,基于注解的途径。只需要个别简单的注解,Seam中的EJB3业务组件就能直接被用来支持JSF Web表单或者处理Web UI事件。Seam允许开发者将“同一种东西”——有注解的POJOs——应用与所有的应用组件。与其他Web框架开发的应用相比,Seam应用概念简洁,同样的功能却需要较少的代码(在JAVA和XML中)。如果没有耐心,或者想要快速预览,一个Seam到底有多简单,你可以现看看本文描述的hello world一例。

在JSP来说困难的任务,Seam可以轻易的完成。例如,JSF头疼的一个问题就是过分依赖HTTP POST。这使得将一个添加到书签中的JSF网页,通过HTTP GET访问相当困难。但是有了Seam,生成一个REST网页是非常容易的。Seam提供了一系列JSF组件标签和注解,增加了“web友好”和JSF应用的网页效率。

同时,Seam拓展了EJB3到POJO的组件模式, 从web层到业务层都有了状态上下文。进一步说,Seam整合了一系列主要的其他开放源代码框架,例如jBPM、JBoss Rules(又名Drools)、JBoss Portal、JBoss Microcontainer等等。Seam不仅能将它们“有机结合”起来,而且可以像整合JSF和EJB3一样强化原有的框架。

Seam位于Java EE 5.0底层,但它的应用并不局限与Java EE 5.0服务器。一个Seam应用可以部署在J2EE 1.4应用服务器和Tomcat服务器上。这意味着现在能在Seam应用中得到产品化支持。

1 + 1 > 2

或许有这样一种误解,认为Seam仅仅是将各种不同框架串起来的另外一个集成框架。Seam提供了它自身管理的状态上下文,允许框架通过注解和EL(表达式语言)表达式与其他框架进行深度整合。整合的程序来自于Seam开发者对第三方框架的认知。

2. 一个为ORM设计的Web框架

对象关系映射(ORM)解决方案在当今企业应用中广为使用。但是,大多数当前的业务和web框架并不是为ORM设计的,它们并不在整个Web交互生命周期——从请求来临到响应完成——管理持久上下文。这就导致了包括可怕的LazyInitializationException在内的各种ORM异常,带来了如“数据传输对象(DTO)”等丑陋的伎俩(ugly hacks)。

Gavin King发明了Seam,同时他也发明了在世界上广为使用的ORM解决方案Hibernate。为了继承和发扬ORM的最佳实践,Seam进行了重新设计。有了Seam,就不必再写DTO,你所做的就是延迟加载。因为扩展后的持久上下文就如同一个自然的高速缓存,可以减少和数据库的交互,ORM的性能就会被极大地改进。

进一步讲,因为Seam整合了ORM层、业务层和表示层,开发者就能够在表示层直接展示ORM对象,也能把数据库验证注解用于输入表单,以及重新定向ORM例外到定制的错误页面。

3.专为有状态Web应用而设计

Seam是专为有状态Web应用而设计的。Web应用是天生的多用户应用,电子商务应用天生也是有状态的和有事务的。但是,大多数已有Web应用框架是面向无状态应用的。开发者必须操作HTTP会话(session)对象来管理用户状态,与核心业务逻辑无关的代码不仅会混乱你的应用,而且带来了一系列的性能问题。

在Seam中,所有的基础应用组件天生地有状态。它们使用起来要比HTTP session容易,因为它们的状态由Seam公开管理。没有必要在Seam应用中编写引起麻烦的状态管理代码——只需在其组件上注解其做用域、生命周期方法以及其他状态属性,Seam就会掌管其他[译者注:指这些组件的生命周期]。Seam状态组件要比HTTP会话(session)能更好的管理用户状态。例如,你能有多个“会话”进行,每个“会话”由在一个HTTP会话(session)中一系列的Web请求和业务方法调用组成。

进一步说,在Seam中,数据库缓存和事务能自动与应用的状态相连。Seam在内存中自动保存数据库更新,等到对话结束后提交到数据库。内存中的缓存能大大减轻复杂状态应用中数据库的负载。

除了以上这些,Seam支持整合开源JBoss jBPM业务程序引擎,大大提升了Web应用中的状态管理。你现在能为一个机构中不同工作人员(诸如客户、经理、技术支持人员等等)的指定工作流程,利用工作流程来驱动应用,而不是依赖用户界面事件处理和数据库。

4. 支持Web 2.0

Seam为Web2.0应用进行了充分的优化。它给AJAX(异步JavaScript和XML,增加网页交互的一种技术)提供了多种支持——从内置“零Javascript”的AJAX组件到有AJAX支持的JSF组件,再到定制的JavaScript库,Seam为浏览器端的Javascript对象提供了直接访问Seam服务器组件的途径。Seam提供了一个先进的并发模型,有效的管理来自同一用户的多个AJAX请求。

对于AJAX应用,不断增长的数据库负载是一个巨大的挑战。与一个非AJAX应用相比,一个AJAX应用要向服务器发送的更频繁的请求。一但数据库必须响应这些AJAX请求,那么数据库就不堪重荷。Seam中的状态持久上下文正如一个内存中的缓存,它能在会话始末保存信息,最终帮助减少数据库交互。

Web2.0应用往往为其数据使用复杂关系模型(例如,一个网络交际站点所做的就是处理和显示“用户”之间的关系),对于这些站点,延迟加载对于ORM层至关重要。否则,一个简单的查询就能级联地加载整个数据库。正如我们前面所讨论过的,Seam是现今唯一一个正确支持Web应用延时加载的Web框架。

5.依赖双向映射的Pojo服务

Seam是一个“轻量级”框架,因为它使用POJO(plain old Java objects)作为服务组件。在应用中,POJO没有使用接口或抽象类来"钩住"组件。当然,问题是如何使POJO交互来组成这个应用?它们如何与容器服务(例如,数据库持久化服务)交互?

Seam通过使用一个流行的、被称作依赖注入(DI)的设计模式联结所有POJO组件。在这个模式下,Seam框架管理着所有组件的生命周期。当一个组件需要使用另外一个时,它通过注解(annotation)向Seam声明此依赖。Seam依据应用当前状态得到这个依赖组件,并将它注入到所需求的组件中。

通过拓展依赖注入概念,一个Seam组件A不但可以构造另外一个组件B,而且把此组件B“抛还”给Seam以备其他组件(例如组件C)以后使用。

这类双向依赖管理甚至都广泛的应用于简单的Seam web应用中(例如第二章的hello world一例)。在Seam术语中,我们称这个为“依赖双向映射”。

6.非常规的配置

[译者注:指以隐式映射为主题,以显式映射为例外的配置方式]

使Seam易用的主要设计原则是“非常规的配置”。其思想是为这些组件提供一系列默认行为,开发者只需要在预期行为非默认的时候,显示地配置组件。例如, 当Seam将组件A作为属性注入到组件B时,默认地,组件A刚会以组件B被注入的属性的名称命名。Seam里还有很类似的细节。总的结果是Seam中配置元数据要比其他Java框架简单的多。因此,大多数的Seam应用能通过一系列简单的Java注解进行充分配置。开发者从减化的复杂度中受益匪浅,最后,与其他Java框架相比,用更少的代码实现同样的功能。

7.避免滥用XML

或许你已经注意到,Java注解在表述和处理Seam配置元数据时扮演着重要的角色。通过这样的设计使框架更易于操作。

在J2EE发展早期,XML曾经被看作配置管理的“圣杯”。框架设计者将所有的配置信息,包括Java类和方法名称都统统丢进XML文档,而不考虑对开发者所带来的后果。反省后,发现这是个严重的错误。XML配置文档太过重复。开发者必须重复代码中已有的信息,从而将配置和代码联结起来。这些重复使应用易于出错(例如,一个拼写错误的类名可能在运行时显示为一个难于调试错误)。缺少合理的默认配置进一步使这一问题复杂化。事实上,在一些框架中,相当数量的样板代码伪装为XML,可能相当于或者超过实际应用中JAVA代码的数量。对于J2EE开发者,XML的滥用通常被称为“XML地狱”。

Java社区认识到了XML的滥用问题,并且已经非常成功地用Java代码中的注解取代了XML。EJB3是Java官方标准化机构促进Java企业组件中注解使用的一项成果。EJB3完全可选择的使用XML文档,它向正确方向迈出了积极的一步。Seam加入了EJB3的注解,为整个web应用拓展了基于注解的编程模型。

当然,XML对于配置数据并非完全不利。Seam设计者认识到XML适用于指定页面流程或者定义业务流程的web应用。XML文档使开发者集中地管理整个web应用的工作流程成为可能,同时也反对将配置信息分散于java源文件中。工作流程很少能与源代码耦合,因此XML文档中并不需要重复键入已存在于代码中的信息。

8.为测试而设计

Seam为了易于测试而重新设计。因为所有的Seam组件都是注解过的POJO,它们易于进行单元测试。开发者仅仅通过利用常规的Java new关键词来构造实例,然后在测试框架(例如JUnit 或者TestNG)中运行任何方法。如果需要测试多个Seam组件的交互,开发者则逐个实例化这些组件,然后手动建立它们的相互关系(也就是显示地使用setter 方法,而不是依靠Seam依赖注入功能)。

集成测试整个Seam应用比较复杂,因为开发者必须在Seam容器中运行应用。Seam用嵌入的轻量级容器来帮助该类测试。在测试框架中,开发者能按步骤地加载Seam容器,然后运行测试。

9. 卓越的工具支持

对于一个聚焦于开发者生产力的应用框架,开发工具的支持至关重要。Seam发布了一个基于命令行的生成器,称作 SeamGen。SeamGen类似于Ruby-On-Rails中的生成器,它支持诸如从一个数据库生成完整CRUD应用的功能,聪明的开发者会通过诸如“编辑/保存/在浏览器重新载入”的步骤、有测试支持的特性,来改进web应用。

但更重要的是,SeamGen生成项目不依赖于主流的Java集成开发环境,如Eclipse和NetBeans。有了SeamGen,开发者可以随时入门。

10. 让我们开始编码吧

总而言之,Seam为JavaEE应用削减了开发费用,同时,增加了Java EE 5.0不具有的强大的新功能。在下节(节选自本书第二章),我们将给您展示一些实际代码例子来阐述Seam如何工作的。你能通过网站http://www.michaelyuan.com/seam/下载到本书中所有的例子的源代码。

Seam Hello World

JBoss Seam是EJB3和JSF中间的粘合剂,这是Jboss Seam最基本的和最广泛的应用。通过被Seam管理的组件,Seam允许这两个框架之间无缝(不是有意双关的)的集成。它为整个web应用拓展了基于注解的EJB3 POJO编程模型。在层与层之间,没有了必需的手动JNDI查找,没有了冗长的JSF支持bean的声明,没有了过多facade方法,没有了艰辛的对象传递,快哉!

继续在Seam中使用JavaEE模式

在传统的java EE应用中,一些设计模式,例如JNDI查找、XML声明组件、值对象、facade是被强制使用的。Seam用基于注解的POJO消除了这些人为的需求。但是,当Seam应用中真正需要它们的时候,仍然可以自由地使用这些模式。

编写一个Seam web应用概念上很简单。你只需要编码出下列组件:

  • 实体对象代表数据模型。实体对象可能是JPA或者Hibernate中的POJO对象。它们自动地映射到关系数据库表。
  • SF web页面展示了用户界面。页面通过表单捕获用户的输入,并且显示结果。表单域与其数据显示数据库表,这些表被映射到实体bean或者实体bean的集合上。
  • EJB3 会话bean或者注解过的Seam POJO可以作为JSF Web页面的UI事件处理器。它们处理封装在实体bean中的用户输入,为下一步(或者页面)生成显示的数据对象。

所有以上组件均由Seam自行管理,它们在运行时被自动注入到正确的页面或者对象。例如,当用户单击按钮提交一个JSF表单,Seam就会自动解析表单域并构造一个实体bean。然后,Seam将实体bean传入同样被Seam构造的事件处理器会话bean中来处理。开发者不需要在代码中管理组件的生命周期和组件之间的相互关系。依赖处理过程中,没有样板代码和XML文件。

本章中,我们使用hello world一例来明确展示Seam如何粘合一个web应用。该例子工作如下:用户能在web表单中输入其名字来“问候”Seam。一旦她提交了表单,应用则保存她的名字到一个关系数据库中,并且显示所有已经“问候”过Seam的用户。该项目示例在该书下载的源代码中的HelloWorld文件夹中。为了建立它,你必须安装Apache ANT 1.6版本以上 (http://ant.apache.org/)。进入HelloWorld目录,运行命令ant,则会生成build/jars/helloworld.ear文件,可以直接拷贝该文件到Jboss AS实例的server/default/deploy目录下。现在,启动JBoss AS并且打开网址http://localhost:8080/helloworld/。

为了运行本书中的例子,我们建议您使用JEMS GUI安装程序安装一个与Seam兼容的JBoss AS。您可以从http://labs.jboss.com/portal/jemsinstaller/downloads下载JEMS安装程序。如果您需要更多安装JBoss AS和应用部署帮助,请参见附录A,“安装和部署JBoss AS”

欢迎使用示例作为模板,快速开始你自己Seam项目(参见附录B “使用应用示例作为模板”)。或者,你能使用命令行工具Seam Gen (参见第四章“快速应用开发工具”)自动生成项目模板,包括所有的配置文件。本章中,我将花少量的时间来阐释源代码项目中的目录结构。相反,我们将集中讨论代码和配置,这也是开发者建立一个Seam 应用必需的。如此,我们就能将知识应用到任何一个项目结构,而不需要受模板的限制。

源代码目录

一个Seam应用由java类和XML或文本配置文件组成。本书的项目例子中,java源代码文件在src目录中,网页在view 目录中,所有的配置文件都在resources目录中。更多信息请看附件B,使用应用示例作为模板。

1. 创建一个数据模型

Helloworld应用中的数据模型仅仅是一个有name和id属性的person 类。注解@Entity告诉容器映射该类到一个关系数据库表,每个属性对应表中一个字段,每个person实例相当于表中的一条记录。因为Seam采用非常规的配置方式,容器为表名和字段中仅仅使用类名和属性名。属性id上的@Id@GeneratedValue注解暗示id字段是主键,它的值是应用服务器为每个保存到数据库的peron对象自动生成。

@Entity
@Name("person")
public class Person implements Serializable {

private long id;
private String name;

@Id @GeneratedValue
public long getId() { return id;}
public void setId(long id) { this.id = id; }

public String getName() { return name; }
public void setName(String name) {this.name = name;}
}

Person类中最重要的注解是@Name,它为这个将要注册于Seam中的Person bean指定了名称。在其他Seam组件中(例如,页面和会话bean)中,开发者能指直接使用“person”来引用被管理的Person bean

2. 将数据模型映射到web表单

在JSF页面中,我们使用Person bean来支持表单输入文本域。#{person.name}符号指代名为“person”的Seam组件的name属性,名为“person”的Seam组件是Person实体bean的一个实例。

<h:form>
Please enter your name:<br/>
<h:inputText value="#{person.name}" size="15"/><br/>
<h:commandButton type="submit" value="Say Hello"
action="#{manager.sayHello}"/>
</h:form>

通过以下的实体表单,JSF页面显示了数据库中所有已经向Seam说“hello”的用户。用户名单列表存储在一个名为“fans”的Seam组件中,它是一个List 对象。JSFdataTable通过遍历列表,每一行显示一个Person对象。Fan标记是fans列表的迭代子。

<h:dataTable value="#{fans}" var="fan">
<h:column>
<h:outputText value="#{fan.name}"/>
</h:column>
</h:dataTable>

图2.1显示了Hello World网页

当用户点击“Say Hello”按钮提交表单,Seam用输入数据构造了该person组件。然后它调用了名为“manager”的Seam 组件的sayhello()的方法(像这样,#{manager.sayHello}是表单提交按钮的UI事件处理器),这就在数据库中保存了person对象并且刷新了fans列表。名为manager的组件是一个EJB3的会话bean, 我们将在下节讨论该话题。

2. 处理web事件

Seam 中的名为manager的组件是会话bean ManagerAction,正如该类中@Name注解指定的。ManagerAction类personfans两个属性,这两个属性被@In@Out所注解。

@Stateless
@Name("manager")
public class ManagerAction implements Manager {

@In @Out
private Person person;

@Out
private List <Person> fans;

注解@In和@Out在Seam编程模型中处于核心。因此,让我们看看到底它们在这里是做什么的。

注解@In告诉Seam,在此会话bean中,执行任何一个方法之前,Seam就会把由JSF表单构造的名为person组件赋给该person字段(通过依赖注入)。开发者能为@In中的注入的组件指定一个任意的名称,但是如果没有指定,如这里所示,Seam会将同类型以及同名称的组件注入到该字段中。注解@Out告诉Seam,在执行任何方法后,Seam会将属性fans值和属性person的值都赋给被Seam管理的同名的组件。在Seam中,我们将这个操作称作 “依赖抛出”。以此,在ManagerAction.sayHello()方法中,我们仅仅需要更新属性fans和属性person的值,它们会自动显示在页面上。

什么是双向映射

在Seam 文件中,有时你就会看到术语“双向映射”。它指的是被Seam管理的组件和Seam管理上下之间的注入和抛出。

因为person属性已经通过注入持有了表单数据,sayHello()方法仅仅是通过JPA EntityManager将它保存到数据库中,JPA EntityManager也是通过@PersistenceContext注入的。当方法返回之后,它便更新了fansperson对象并且把这两个对象抛出。方法sayHello()一般会返回null,预示着在调用之后,更新的数据模型将在当前的JSF页面显示。

  @PersistenceContext
private EntityManager em;

public String sayHello () {
em.persist (person);
person = new Person ();
fans = em.createQuery("select p from Person p")
.getResultList();

return null;
}

除了一些细节,我们基本完成了。可能你已经注意到,ManagerAction bean类实现了Manager接口。为了符合EJB3会话bean 规范,我需要一个能列出bean中所有业务方法的方法。下面是接口Manager代码,幸运的是,用任何高级IDE工具都能轻松地自动生成这个接口。

@Local
public interface Manager {
public String sayHello ();
}

这就是在Hello World例子中需要的所有代码。后面两章小节将涵盖Seam应用的其他方法和配置。如果开发者为了自己的小型数据库应用想立即编码和定制helloworld项目,那么现在就可以跳过本章的剩余部分。

4. 更易于理解的seam编程模型

现在我们已经大致了解了Hello World的应用。但是我们还有一些重要的话题继续,例如其他折中途径以及前面代码没有涉及到重要特性,我们将在本节讨论这些话题。它们能帮助开发者对seam更深刻的理解,但是如果你没有耐心,可以直接跳过本节,需要的时再来阅读。

4.1 Seam POJO组件

上例中,我们用一个EJB3会话bean实现了应用逻辑,但是我们并不局限于EJB3组件。事实上,Seam中任何一个有@Name注解的POJO都能被转化为一个可管理的组件。

例如,我们能将ManagerAction转化为一个 POJO,而不是一个EJB3 session bean。

@Name("manager")
public class ManagerAction {

@In (create=true)
private EntityManager em;

... ...
}

使用POJO取代EJB3 bean有正反两方面意见,使用POJO编程时很简单,因为它们不需要EJB3特有的注解和接口(参见上文)。如果你的所有业务组件都是Seam POJO, 那么你就能不依赖EJB3应用服务器,运行你的Seam 应用(参见23章,没有EJB3的Seam)。

但是,POJO比EJB3的功能少,因为POJO不能获得EJB3容器服务。在不依赖EJB3的Seam 中丧失的EJB3服务就包括以下几点:

  • @PersistenceContext注入在POJO中不在管用。为了在一个Seam POJO中得到EntityManager,开发者不得不在Seam配种文件中初始化EntityManager,然后使用Seam注解@In将它注入到POJO中。
  • POJOs中将不在支持方法级别事务声明(declarative method-level transaction)。相反,你可以配置Seam来划分事务,可以从收到web请求开始直到响应页面产生结束。
  • Seam POJO不是消息驱动组件。
  • 不支持注解为@Asynchronous的方法。
  • 不支持容器安全管理。
  • 没有事务或者组件级别的持久上下文。Seam POJO中的所有的持久上下文都是经过拓展的(更多细节请参见7.1 “默认的对话作用域”)。
  • 没有集成容器管理的体系结构(例如,JMX控制台服务)。
  • Seam POJO方法中没有Java RMI。
  • Seam POJO不能是注解为@WebService组件。
  • 没有JCA集成。

所以当在EJB3容器中进行部署时,为什么每个人都想使用POJO组件?答案就是,POJO组件对于纯“业务逻辑”组件非常有益。POJO为其他组件代理了数据访问、消息传递和其他基本功能。例如,我们能使用POJO组件操纵Seam数据访问对象,这对“业务逻辑”POJO是非常有用的,因为它们可以在需要的时候,在其他框架中被重用。但是总的来说,它们的应用要比EJB3组件少,特别是在中小型应用中。所以,本书的大多数例子我们都使用EJB3组件。

4.2 易于测试

我们已经在第一章中提到,Seam为了不依赖容器的方便的测试,进行了重新设计。在helloworld项目中,我们在测试文件夹中包括了单元测试和集成测试这两个测试用例。在纯Java SE环境下,Seam 测试体系模拟了数据库、JSF、Seam上下文以及其他应用服务器服务,只要运行ant test命令就能运行所有的测试。

4.3 基于Getter和Setter的双向映射

在Hello World一例中,我们已经展示了通过成员变量对Seam组件进行的双向映射,你也能通过Getter和Setter方法对组件进行双向映射。例如,以下代码就工作的很好。

private Person person;
private List <Person> fans;

@In
public void setPerson (Person person) {
this.person = person;
}
@Out
public Person getPerson () {
return person;
}
@Out
public List <Person> getFans () {
return fans;
}

虽然以上的getter和setter方法看似轻微,利用getter和setter方法的双向映射真正的价值在于能其加入定制逻辑来操纵双向映射的过程。例如,你可以验证被注入的对象或者快速地从数据库重新得到被抛出的对象。

4.4避免过度的双向映射

在Hello World一例中 ,通过将数据组件作为业务组件的属性,可以轻易的减少或者消除双向映射。在JSF页面中,通过这种方式,开发者只需要引用业务组件,而不需要在业务组件和数据组件之间的双向映射。例如,开发者可以修改 ManagerAction类为以下所述。

依赖双向映射是一个非常实用的设计模式。但是,正如其他设计模式,过度使用就会有害。过度的依赖双向映射让代码变得难以阅读,因为开发者必须理解每个注入的组件出自何处。过度的依赖双向映射也能增加性能消耗,因为双向映射是在运行时进行。

@Stateless
@Name("manager")
public class ManagerAction implements Manager {

private Person person;
public Person getPerson () {return person;}
public void setPerson (Person person) {
this.person = person;
}

private List <Person> fans;
public List<Person> getFans () {return fans;}

... ...
}

接下来,我们在页面上引用的属性如下:

<h:form>

Please enter your name:<br/>

<h:inputText value="#{manager.person.name}"/>
<br/>
<h:commandButton type="submit" value="Say Hello"
action="#{manager.sayHello}"/>
</h:form>
... ...
<h:dataTable value="#{manager.fans}" var="fan">
<h:column>
<h:outputText value="#{fan.name}"/>
</h:column>
</h:dataTable>

最后,具有了依赖管理的Seam是多用的。通常用数据访问业务组件封装数据是一项好的实践,特别是针对有状态业务组件。

4.5 JSF中的页面导航

本例中,只有一个页面。每次点击按钮后,JSF页面会重新显示更新过的数据模型。显然,大多数web应用多于一个页面。在JSF中,一个用户界面事件处理器能通过返回导航规则名称,决定下一步该显示哪个页面。例如,开发者可以在navigation.xml中定义以下导航规则。

<navigation-case>
<from-outcome>anotherPage</from-outcome>
<to-view-id>/anotherPage.jsp</to-view-id>
</navigation-case>

之后,如果sayHello()方法返回一个名为“another page”的字符串,JSF下一步就该展示anotherPage.jsp。UI事件处理器决定了接下来要显示哪个页面,从而为我们带来了有步骤的控制。

4.6 通过EntityManager访问数据库

JPA(Java Persistence API)EntityManager管理着关系数据库表与实体bean 之间的映射。EntityManager 在运行时由应用服务器创建。你能使用注解@PersistenceContext,注入一个EntityManager的实例。

EntityManager.persist()方法将实体bean存为与之对应数据表的一条记录。EntityManager.query()方法运行SQL化的查询,并以实体bean集合形式从数据库返回数据。更多细节请参考JPA文件中关于如何使用EntityManager和查询语言。在本书中,我们只用最简单的查询语句。

默认地,EntityManager将数据存于嵌入的HSQL数据库中。如果在本机上运行Jboss AS,可以通过以下步骤,为HSQL数据库开启一个GUI控制台:访问http://localhost:8080/jmx-console/,点击database=localDB,service=Hypersonic MBean服务,之后,点击在startDatabaseManager方法下方的“invoke”按钮。你就可以从控制台执行任意SQL指令。

5. 配置和打包

下面,我们将转移话题,讨论配置文件和应用程序打包。实际上,你可以通过Seam Gen命令行工具,生成几乎所有的配置文档和构造脚本文件。或者你也可以简单的重用在示例中的源文件。所以,如果你想首先学习Seam编程技术,但又担心接下来的配置和部署,这个是很正常的。你可以完全放心地跳过本节,需要的时候可以再次阅读。

本节中,我们集中探讨Seam EJB3组件配置,JBoss AS外的Seam POJO配置和部署当然是可行的。

大多数Seam配置文件都是XML文档。但是等等!我们刚才不是承诺Seam能让我们摆脱J2EE和Spring中的XML地狱吗?为什么它又有了XML文档呢? 是的,XML文档确实有很多用处。XML文档非常适合部署阶段的配置(例如web应用的根URL和后台数据库的定位)。因为它允许我们在部署阶段改变配置而不需要改变和重新编译源代码。它也适合粘合应用服务器中的不同子系统(例如,配置如何让JSF组件与Seam EJB3组件交互)。XML文档也非常适合表示层相关内容(例如网页和页面导航流程)。

我们反对在XML文档中重复已经存在于Java源代码中的信息。开发者很快就会发现,这个简单的SeamEJB3 应用有多个XML配置文档,每个文档那个都非常简短,并且没有一个包含存在于Java代码中的信息。换句话说,Seam中没有“XML代码”。

进一步讲,XML文档中的大多数内容都是静态的。所以开发者能在自己的Seam应用中轻松地重用这些文档。如何使用示例作为自己的应用模板的介绍,请参见附录B——使用应用示例作为模板。

我们将用下面几页来详细讲解示例应用的配置文档和打包后的目录结构。如果你没有耐心看下去,而且很满意这个应用模板,你可以跳过以下内容。不管怎样,不再罗嗦, 我们一起来了解hello world示例是如何进行配置和打包的。为了构建一个JBoss AS的部署Seam 应用,我们必须将以上所有java 类和配置文档打包为企业应用程序归档(EAR)。该例中,EAR文件是helloworld.ear。它包含了三个JAR文件那个和两个XML配置文档。

helloworld.ear
|+ app.war //包含Web页面等
|+ app.jar //包含Seam组件
|+ jboss-seam.jar // Seam库
|+ META-INF
|+ application.xml
|+ jboss-app.xml
源代码目录

在此项目的源代码中,resources/WEB-INF目录包含属于app.war/WEB-INF目录的配置文档。resources/META-INF目录包含属于app.jar/META-INF和helloworld.ear/META-INF的文档。Resources根目录包含属于根目录app.jar的文档。

application.xml文档列出了在EAR中的JAR文件,并为该应用指定了根URL。

<application>
<display-name>Seam Hello World</display-name>

<module>
<web>
<web-uri>app.war</web-uri>
<context-root>/helloworld</context-root>
</web>
</module>

<module>
<ejb>app.jar</ejb>
</module>

<module>
<java>jboss-seam.jar</java>
</module>

</application>

jboss-app.xml文档为该应用指定了类加载器,每个EAR应用的类加载器应该有一个唯一的名称。这里我们使用应用程序名作为类加载器的名称,以避免重复。

<jboss-app>
<loader-repository>
helloworld:archive=helloworld.ear
</loader-repository>
</jboss-app>

jboss-seam.jar是Seam发布Seam类库。app.war和app.jar文档由我们来建构。所以,下面我们要研究app.war和app.jar。

5.1. WAR文件

app.war是按照Web应用程序归档规范打包的JAR文件,它包含页面和标准的JSF/Seam配置文档。你还可以将JSF特有的类库文件放入WEB-INF/lib目录 (例如jboss-seam-ui.jar)。

app.war
|+ hello.jsp
|+ index.html
|+ WEB-INF
|+ web.xml
|+ faces-config.xml
|+ components.xml
|+ navigation.xml

web.xml文档是所有java EE web应用必需的。JSF用它来配置JSF servlet控制器,Seam用它来拦截所有的web请求。该配置文档的相当标准。

<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="..."
xsi:schemaLocation="...">

<!-- Seam -->
<listener>
<listener-class>
org.jboss.seam.servlet.SeamListener
</listener-class>
</listener>

<!-- MyFaces -->
<listener>
<listener-class>
org.apache.myfaces.webapp.StartupServletContextListener
</listener-class>
</listener>

<context-param>
<param-name>
javax.faces.STATE_SAVING_METHOD
</param-name>
<param-value>client</param-value>
</context-param>

<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>
javax.faces.webapp.FacesServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- Faces Servlet Mapping -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.seam</url-pattern>
</servlet-mapping>
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>/WEB-INF/navigation.xml</param-value>
</context-param>
</web-app>

faces-config.xml文档是JSF标准的配置文档,Seam用它来将其拦截器添加到JSF生命周期中。

<faces-config>

<lifecycle>
<phase-listener>
org.jboss.seam.jsf.SeamPhaseListener
</phase-listener>
</lifecycle>

</faces-config>

navigation.xml文档为多页面应用包含JSF页面导航规则。因为hello world示例只有一个简单的页面,因此该文档是空的。

components.xml文档包含Seam特有的配置选项,除jndi-pattern属性以外,其他都不依赖于应用。该属性必须包括EAR文档的名称,以便Seam通过其的JNDI全名访问EJB3 bean。

<components ...>

<core:init
jndi-pattern="helloworld/#{ejbName}/local"
debug="false"/>

<core:manager conversation-timeout="120000"/>

</components>

5.2. Seam组件JAR包

app.jar文档包含所有的EJB3bean 类(实体bean和会话bean)以及EJB3相关的配置文档。

app.jar
|+ Person.class // entity bean
|+ Manager.class // session bean interface
|+ ManagerAction.class // session bean
|+ seam.properties // empty file but needed
|+ META-INF
|+ ejb-jar.xml
|+ persistence.xml

seam.properties文档这儿是空但必需的,因为Jboss要通过它知道此JAR文件包含Seam EJB3 bean类,并且相应地处理注解。

ejb-jar.xml文档包含额外的配置信息,这些信息能重载或者增补EJB3 bean上的注解。在一个Seam应用中,它能将所有的EJB3 类加入Seam拦截器。我们能在所有的Seam应用中重用该文档。

<ejb-jar>
<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>
org.jboss.seam.ejb.SeamInterceptor
</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>

persistence.xml文档为EJB3 实体bean配置了后台数据源。本例中,我们只是使用了被嵌入到JBoss AS中默认的HSQL数据库(也就是java:/DefaultDS数据源)。

<persistence>
<persistence-unit name="helloworld">
<provider>
org.hibernate.ejb.HibernatePersistence
</provider>
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.hbm2ddl.auto"
value="create-drop"/>
<property name="hibernate.show_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>

这样,以上就是一个简单Seam应用所需的所有配置和打包。我们将在以后讨论到本书的更高级的主题时,涵盖更多的配置选项和类库。再次强调一下,Seam应用入门最简单的方法就是,不要担心这些配置文件,只需要从已有的应用模板做起。

6. 为何这么简单?

这就是hello world 应用,三个简单的Java类,一个JSF页面,一组静态配置文件。我们已经有了一个完整的数据库驱动的web应用。整个应用只需要的是少于30行的Java代码,没有一处“XML代码”。但是如果开发者有PHP背景,你可能仍然会问“何以这么简单?我能在php中使用更少的代码吗?”

好吧,答案就是Seam应用在理论上要比PHP(或者其他任何一种脚本语言)应用简单的多。Seam 组件模式允许我们,有所控制地,可维护地给应用增加更多的功能。我们很快就会发现,Seam组件使开发有状态的和有事务的web应用变得易如反掌。对象关系映射框架(例如:实体bean)允许我们将注意力放在抽象数据模型上,而不需要处理数据库特有的SQL语句。

本文是基于该书的第一章和第二章。后面的章节中,我们继续讨论如何使用Seam组件继续开发复杂的Seam 应用。参见本书目录来查看本书的所有主题。

请看Gavin King的两个访谈录来查看以往关于Seam的话题。

  • JBoss Seam 1.1 Indepth: An Interview with Gavin King
  • JBoss Seam 1.0: rethinking web application architecture

关于作者

Dr. Michael Yuan是JBoss Seam: Simplicity and Power Beyond Java EE 5.0一书的作者。该书探讨了下一代web应用框架。他同时也是Nokia Smartphone Hacks一书和其他三本技术读物的作者。Michael潜心研究轻量级企业web应用,终端对终端的移动应用开发。你可以通过他的博客联系他。

查看英文原文:Introduction to JBoss Seam
译者简介: 包亮,一名普通的程序员,喜欢敏捷实践,喜欢"懒惰",减少重复,尽可能让工作变得简单。几年来,一直通过网络汲取知识,也希望通过网络将知识与人分享 。志愿参与InfoQ中文站内容建设,请邮件至china-editorial@infoq.com

posted @ 2008-02-19 14:15 巴西木 阅读(1418) | 评论 (0)编辑 收藏

仅列出标题
共33页: First 24 25 26 27 28 29 30 31 32 Last