技术文章   Apusic4.0下,SLSB+ EJB3.0(edr2)持久化API事务性编程指南

一、         前言


  基于POJO的持久化技术,如HibernateJDO,如今受到广大J2EE开发者的追捧。当然,一门技术受到关注不会是凭空的。基于POJO的持久化技术,到底给我们带来了什么?


1、  对象驱动开发,使得基于OO方法学的开发过程更加流畅。基于JDBC、甚至EntityBean的开发,基本上是典型的数据驱动开发。


2、  提高开发效率。您不再需要大量的JDBC编码。


3、  功能强大。


4、  便于测试。


EJB3.0持久化模型和Hibernate如出一辙,但是以其权威性备受关注。


下面,我将和大家一起来探讨SLSB+EJB3.0(edr2)架构的实施方法。这里,我们采用Hibernate EJB3.0(edr2)实现。


 


二、         准备工作


1Apusic4.0应用服务器


  下载地址:http://www.apusic.com/product/download/Apusic-4.0.exe


  说明:Apusic4.0 AS是金蝶中间件公司的产品。J2EE1.4兼容。


2、数据库系统MS SQLServer


   Microsoft SQLServer 2000 Drivermsutil.jarmsbase.jarmssqlserver.jar


3Hibernate-3.0.1


  下载地址:http://prdownloads.sourceforge.net/hibernate/hibernate-3.0.1.zip?download


  说明:Hibernate Core。是EJB3.0(edr2)持久化技术的底层基础。


4Hibernate Annotations 3.0 Preview beta 1


  下载地址:


http://prdownloads.sourceforge.net/hibernate/hibernate-annotations-3.0beta1.zip?download


  说明:EJB3.0(edr2)绝大部分持久化Meatadata(Annotation)实现,并且提供了很多实用而强大的扩展Annotation


5Eclipse


6JDK必须1.5以上


 


三、       示例:作者/著作(Author/Work)


  这个示例来自Hibernate Manual(Author/Work)。这个Sample,涉及Many-ManyOne-One关系、继承,很适合做技术演示。


  假设,在基于OO方法学的开发过程中,我们在详细设计完成后,得到下面的UML图。


图中涉及的对象,都是需要持久化到数据库的。如果,你打算基于JDBC或者EntityBean来做开发,那么,显然,你的下一步工作就是设计数据库Schema。然后,针对Schema进行JDBCEntityBean编码。但是,作为一个纯粹而挑剔的OO开发者,这种数据驱动开发很别扭,而且,往往,这种开发模型的编码类似于过程编程。


  理想的状态:详细设计后,编码。我的代码只关心商业逻辑,至于在商业逻辑中涉及的对象持久化到什么地方、怎样持久化,我都没有兴趣。充其量,为了配合O/R Tool,我提供对象关系、有利于提高持久化性能、扩展性等等MetadataOK。就是这样。在UML图的基础上,我们接下来的工作就是:


1、  UML图转换成Java代码。(借助工具或者IDE的工程,高效完成)


2、  为对象添加Meatadata(在基于EJB3.0持久化技术的开发中,就是为JavaSource添加Annotation)


3、  O/R工具根据Meatadata自动生成Database Schema.


4、  基于O/R Tool编码。



 


四、         开发过程


1、准备Jars和配置文件


  假定Apusic4.0的安装目录为:${APUSIC_HOME}


假定hibernate-3.0.1.zip的解压缩目录为${hibernate_ext}


hibernate-annotations-3.0beta1.zip的解压缩目录为${hibernate_annotation_ext}


  1)Apusic 核心包。


    ${APUSIC_HOME}\lib\apusic.jar


2)Hibernate 核心包。


    ${hibernate_ext}


3)Hibernate Runtime 依赖的Jars


Hibernate Build依赖的Jars


这些包都放在${hibernate_ext}\lib目录下。


至于哪些是运行时依赖的、哪些是Build时依赖的,q请参考同目录下的README.txt


  5)Hibernate EJB3.0(edr2) Annotataion支持Jars


${hibernate_annotation_ext}\hibernate-annotations.jar


${hibernate_annotation_ext}\lib\ejb-3.0-edr2.jar


  6)配置文件


${hibernate_ext}\etc\hibernate.properties


${hibernate_ext}\etc\log4j.properties


${hibernate_ext}\etc\ehcache.xml


    Hibernate二级缓存支持多种Cache系统,包括JbossCacheJCSOSCache等等。这里,我选择EHCache,所以,我准备ehcache.xml而不是oscache.properties或者treecache.xml


2、修改配置文件hibernate.properties


  Hibernate提供设定的属性非常多,主要涉及以下几个大的方面:Query LanguagePlatformsHibernate Connection PoolTransaction APIMiscellaneous SettingsJDBC SettingsSecond-level CacheJNDI。这里,我不会详细说明每个配置项的作用,而将说明的重点放在和HibernateEJB容器环境下事务性编程相关的配置项上。


1)      Query Language


不作改动。


2)      Platforms


Action1:设定JNDI DataSource






## JNDI Datasource


hibernate.connection.datasource jdbc/sqlserver/publisher


#hibernate.connection.username sa


#hibernate.connection.password sa


说明:


1、因为,我们需要使用容器管理事务,所以必须使用Apusic应用服务器的DataSource,应用服务器的DataSource可以自动被事务征集。而普通的DataSource是不具有这样的功能的。


2、根据各应用服务器的实际情况,决定是否需要设置usernamepassword。譬如Apusic4.0DataSource等资源的远程客户是需要通过安全性检查的,只允许特定用户或者组(Role)的用户才可以使用,所以需要设定username或者password。而本机用户,没有这样的限制,就不需要设定。


Action2屏蔽HypersonicSQL配置






## HypersonicSQL


 


#hibernate.dialect org.hibernate.dialect.HSQLDialect


#hibernate.connection.driver_class org.hsqldb.jdbcDriver


#hibernate.connection.username sa


#hibernate.connection.password


#hibernate.connection.url jdbc:hsqldb:hsql://localhost


#hibernate.connection.url jdbc:hsqldb:test


#hibernate.connection.url jdbc:hsqldb:.


 Action3:设定数据库方言






## MS SQL Server


 


hibernate.dialect org.hibernate.dialect.SQLServerDialect


#hibernate.connection.username sa


#hibernate.connection.password sa


说明:因为Hibernate为了功能的增强和性能的提高,会将对象操作翻译成特定数据库系统的SQL。所以,你需要设定Dialect


 


3) Hibernate Connection Pool


Action1屏蔽hibernate.proxool.pool






##############################


### Proxool Connection Pool###


##############################


 


## Properties for external configuration of Proxool


#hibernate.proxool.pool_alias pool1


## Only need one of the following


#hibernate.proxool.existing_pool true


#hibernate.proxool.xml proxool.xml


#hibernate.proxool.properties proxool.properties


说明:因为我们使用Apusic应用服务器的DataSource,所以,取消Hibernate默认使用的Proxool Connection Pool


Action2:设定Plugin ConnectionProvider






#################################


### Plugin ConnectionProvider ###


#################################


 


## use a custom ConnectionProvider (if not set, Hibernate will choose a built-in #ConnectionProvider using hueristics)


#hibernate.connection.provider_class #org.hibernate.connection.DriverManagerConnectionProvider


hibernate.connection.provider_class org.hibernate.connection.DatasourceConnectionProvider


#hibernate.connection.provider_class org.hibernate.connection.C3P0ConnectionProvider


#hibernate.connection.provider_class org.hibernate.connection.ProxoolConnectionProvider


说明:打开配置:


hibernate.connection.provider_class org.hibernate.connection.DatasourceConnectionProvider


 


4)Transaction API


  Action1设定TransactionAPI






## the Transaction API abstracts application code from the underlying JTA or JDBC #transactions


#hibernate.transaction.factory_class org.hibernate.transaction.JTATransactionFactory


hibernate.transaction.factory_class org.hibernate.transaction.JDBCTransactionFactory


说明:事实上,在容器管理事务的环境下,这两个配置项任意选定一个都是没有问题的。


 Action2






## to use JTATransactionFactory, Hibernate must be able to locate the UserTransaction in #JNDI


## default is java:comp/UserTransaction


## you do NOT need this setting if you specify hibernate.transaction.manager_lookup_class


 


#jta.UserTransaction jta/usertransaction


#jta.UserTransaction javax.transaction.UserTransaction


#jta.UserTransaction UserTransaction


说明:因为我们选择容器管理事务,不会自己管理任何的事务,也不需要JTATransaction


所以屏蔽此项。


5Miscellaneous SettingsJDBC Settings


   不作改动。


6Second-level Cache


  Action1:配置cache implementation






 


## choose a cache implementation


 


hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider


#hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider


hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider


#hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider


#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider


#hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider


说明:我们上面提及将使用EHCache作为二级缓存的Provider。所以,这里打开


hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider项。


 


7JNDI


Action1






 


############


### JNDI ###


############


 


## specify a JNDI name for the SessionFactory


 


#hibernate.session_factory_name hibernate/session_factory


 


 


## Hibernate uses JNDI to bind a name to a SessionFactory and to look up the JTA UserTransaction;


## if hibernate.jndi.* are not specified, Hibernate will use the default InitialContext() which


## is the best approach in an application server


 


#file system


#hibernate.jndi.class com.sun.jndi.fscontext.RefFSContextFactory


#hibernate.jndi.url file:/


 


#WebSphere


#hibernate.jndi.class com.ibm.websphere.naming.WsnInitialContextFactory


#hibernate.jndi.url iiop://localhost:900/


说明:


1、  hibernate.session_factory_name hibernate/session_factory


如果,你希望HibernateSessionFactory绑定到JNDI上,从而方便J2EE AS环境下Hibernate编程,那么需要指定这个项目。本实例中,我们不采用这种方式,所以,取消这项配置。


2、  hibernate.jndi.url


如果,你需要以远程的方式来使用JTA或者将SessionFactory绑定到JNDI,那么你需要配置应用服务器特定的ContextFactory ClassURI、甚至PrincipalCreditial


   本实例中,我们不会采用Hibernate JTA,也不会远程绑定SessionFactory,所以取消此项配置。


 


 


3Eclipse工程命名Publisher3


   结构如下图:



说明:


1)      为工程新建目录:dependedjars,存放所以build time/runtime 依赖的jars


出于工程编译和测试的需要,你需要将dependedjars中所有jars引用到工程中。


2)      ehcache.xmlhibernate.propertieslog4j.properties放到src目录下。


3)      tomhornson.ejb3.publisher.appexcepton:存放工程的应用级异常。


tomhornson.ejb3.publisher.client:存放Remote测试客户端。


tomhornson.ejb3.publisher.entity:存放带有AnnotataionPOJO形式的持久化Entity


tomhornson.ejb3.publisher.slsb:存放SLSB,作为应用的fa?ade,通过O/R Tool操作持


POJO


   tomhornson.ejb3.publisher.util:工具类。


 


 


五、         部署


1、配置数据源


1)MS SQLServer JDBC Drivermsbase.jarmsutil.jarmssqlserver.jar放到


${APUSIC_HOME}\lib\ext目录下。


2)启动Apusic4.0应用服务器。


3)打开Apusic RemoteManager,通过默认用户名admin、默认密码admin登陆远程管理界


面。


4Apusic为几种流行的数据库,提供默认的URL格式和驱动类名称,所以你只需要输入


用户名(数据库登陆名)和密码,就可以了。当然,你也可以针对一些高级选项进行配置。


效果如下:



 


2、  部署应用到Apusic服务器



出于系统维护性和多Hibernate工程同应用服务器运行引起互相干扰的考虑,我们将以EAR为单位部署Hibernate运行环境。而是不是,将Hibernate运行依赖Jars防止到${APUSIC_HOME}\lib或者${APUSIC_HOME}\lib\ext目录中。


 


参考,Apusic4.0 Manual的《部署工具用户手册》新建一个EAR工程,添加一个EJB模块,为EJB模块添加AuthorFacade SLSB。你的EAR工程目录结构,如下:



 


3、为EJB模块编写MANIFEST.MF文件


   内容如下:


Class-Path:  hibernate3.jar asm.jar cglib-2.1.jar commons-collections-2.1.1.jar


commons-logging-1.0.4.jar dom4j-1.5.2 ehcache-1.1.jar ejb-3.0-edr2 hibernate-annotations versioncheck.jar xerces-2.6.2.jar


说明:这个MANIFEST.MF文件,保证AuthorFacade SLSB能够引入依赖的类。


MANIFEST.MF文件防止到AuthorFacadeMETA-INF目录中。


 


4Hibernate运行依赖Jars和配置文件到EAR工程目录。


这样,EAR根目录的结构如下所示:



5、  使用部署工具,将publisher3 EAR工程部署到Apusic服务器。


 


OK


 


一切工作完成。


 


六、         编程和原理探讨


1、  使用HibernateAnnotationed POJO JavaSource生成数据库Schema


Annotation 示例:






/*


 * Created on Apr 21, 2005


 * Author: TomHornson(at)hotmail.com


 */


package tomhornson.ejb3.publisher.entity;


 


import java.io.Serializable;


import javax.persistence.*;


import org.hibernate.annotations.*;


 


@org.hibernate.annotations.Entity(


        dynamicInsert = true,


        dynamicUpdate = true,


        selectBeforeUpdate = true,


        polymorphism = PolymorphismType.IMPLICIT


 )


@javax.persistence.Entity(access = AccessType.PROPERTY)


@BatchSize(size = 50)


@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)


/*


@Inheritance(


        strategy = InheritanceType.SINGLE_TABLE,


        discriminatorType=DiscriminatorType.STRING,


        discriminatorValue="person"


    )


@DiscriminatorColumn(name="person")


*/


@Inheritance(strategy = InheritanceType.JOINED)


public class Person implements Serializable{


    static final long serialVersionUID = -3387516993124229940L;


    protected long id;


    protected String name;


    protected int version;


   


    @Id(generate=GeneratorType.AUTO)


    @Column(updatable=false)


    public long getId() {


        return id;


    }


    public void setId(long id) {


        this.id = id;


    }


   


    @Column(nullable=false,updatable=false)


    public String getName() {


        return name;


    }


    public void setName(String name) {


        this.name = name;


    }


   


    @Version


    public int getVersion(){


        return version;


    }


    public void setVersion(int version){


        this.version = version;


    }


}


   如上面所示,我们的POJO都是Annotation标志过的,这些标志定义了一系列的对象关系、持久化策略、缓存策略、事务隔离性、乐观并发策略等等。


而,依赖于这些标志,HibernateSchemaExport Command可以自动生成Database Shema


代码片断如下:






        AnnotationConfiguration anno = new AnnotationConfiguration();


        Class["> cArr = {Person.class,Author.class,Work.class,Song.class,Book.class};


        List al = Arrays.asList(cArr);


        anno.addAnnotatedClasses(al);


        anno.setProperty(Environment.HBM2DDL_AUTO, "create-drop");


        sf = anno.buildSessionFactory();


        Session session = sf.openSession();


 


2、容器管理事务环境下Hibernate编程


   容器管理事务的原理概述:当设定启用CMT的方法被调用时,容器在方法调用前调动一个事务,并且和线程绑定(ThreadLocal)






               Session session = SessionHelper.openSession();


   上面的代码,别后发生的事情:HibernateApusic应用服务器的DataSource取得Connection,而来自服务器的DataSource是事务感应的,它在返回Connection之前,已经检测当前线程是否绑定事务,如果是,那么将Connection征集到事务中。所以,CMT Hibernate编程,你只需要任意的openSession,closeSession,不需要考虑事务回滚问题,当运行时异常或者系统级别异常发生时,容器会透明回滚所有的被征集的Connection。保证事务的原子性。


而对于事务隔离性和交叉存取问题,Hibernate也是支持的,所以基于SLSB+Hibernate可以开发事务要求很高的应用。