一、 前言
基于POJO的持久化技术,如Hibernate、JDO,如今受到广大J2EE开发者的追捧。当然,一门技术受到关注不会是凭空的。基于POJO的持久化技术,到底给我们带来了什么?
1、 对象驱动开发,使得基于OO方法学的开发过程更加流畅。基于JDBC、甚至EntityBean的开发,基本上是典型的数据驱动开发。
2、 提高开发效率。您不再需要大量的JDBC编码。
3、 功能强大。
4、 便于测试。
而EJB3.0持久化模型和Hibernate如出一辙,但是以其权威性备受关注。
下面,我将和大家一起来探讨SLSB+EJB3.0(edr2)架构的实施方法。这里,我们采用Hibernate EJB3.0(edr2)实现。
二、 准备工作
1、Apusic4.0应用服务器
下载地址:http://www.apusic.com/product/download/Apusic-4.0.exe
说明:Apusic4.0 AS是金蝶中间件公司的产品。J2EE1.4兼容。
2、数据库系统MS SQLServer
Microsoft SQLServer 2000 Driver:msutil.jar、msbase.jar、mssqlserver.jar
3、Hibernate-3.0.1
下载地址:http://prdownloads.sourceforge.net/hibernate/hibernate-3.0.1.zip?download
说明:Hibernate Core。是EJB3.0(edr2)持久化技术的底层基础。
4、Hibernate Annotations 3.0 Preview beta 1
下载地址:
http://prdownloads.sourceforge.net/hibernate/hibernate-annotations-3.0beta1.zip?download
说明:EJB3.0(edr2)绝大部分持久化Meatadata(Annotation)实现,并且提供了很多实用而强大的扩展Annotation。
5、Eclipse
6、JDK必须1.5以上
三、 示例:作者/著作(Author/Work)
这个示例来自Hibernate Manual(Author/Work)。这个Sample,涉及Many-Many、One-One关系、继承,很适合做技术演示。
假设,在基于OO方法学的开发过程中,我们在详细设计完成后,得到下面的UML图。
图中涉及的对象,都是需要持久化到数据库的。如果,你打算基于JDBC或者EntityBean来做开发,那么,显然,你的下一步工作就是设计数据库Schema。然后,针对Schema进行JDBC、EntityBean编码。但是,作为一个纯粹而挑剔的OO开发者,这种数据驱动开发很别扭,而且,往往,这种开发模型的编码类似于过程编程。
理想的状态:详细设计后,编码。我的代码只关心商业逻辑,至于在商业逻辑中涉及的对象持久化到什么地方、怎样持久化,我都没有兴趣。充其量,为了配合O/R Tool,我提供对象关系、有利于提高持久化性能、扩展性等等Metadata。OK。就是这样。在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系统,包括JbossCache、JCS、OSCache等等。这里,我选择EHCache,所以,我准备ehcache.xml而不是oscache.properties或者treecache.xml。
2、修改配置文件hibernate.properties
Hibernate提供设定的属性非常多,主要涉及以下几个大的方面:Query Language、Platforms、Hibernate Connection Pool、Transaction API、Miscellaneous Settings、JDBC Settings、Second-level Cache、JNDI。这里,我不会详细说明每个配置项的作用,而将说明的重点放在和Hibernate在EJB容器环境下事务性编程相关的配置项上。
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、根据各应用服务器的实际情况,决定是否需要设置username、password。譬如,在Apusic4.0下,DataSource等资源的远程客户是需要通过安全性检查的,只允许特定用户或者组(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,
所以屏蔽此项。
5)Miscellaneous Settings、JDBC Settings、
不作改动。
6)Second-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项。
7)JNDI
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
如果,你希望Hibernate的SessionFactory绑定到JNDI上,从而方便J2EE AS环境下Hibernate编程,那么需要指定这个项目。本实例中,我们不采用这种方式,所以,取消这项配置。
2、 hibernate.jndi.url
如果,你需要以远程的方式来使用JTA或者将SessionFactory绑定到JNDI,那么你需要配置应用服务器特定的ContextFactory Class、URI、甚至Principal、Creditial。
本实例中,我们不会采用Hibernate JTA,也不会远程绑定SessionFactory,所以取消此项配置。
3、Eclipse工程,命名Publisher3。
结构如下图:
说明:
1) 为工程新建目录:dependedjars,存放所以build time/runtime 依赖的jars。
出于工程编译和测试的需要,你需要将dependedjars中所有jars引用到工程中。
2) 将ehcache.xml、hibernate.properties、log4j.properties放到src目录下。
3) 包tomhornson.ejb3.publisher.appexcepton:存放工程的应用级异常。
包tomhornson.ejb3.publisher.client:存放Remote测试客户端。
包tomhornson.ejb3.publisher.entity:存放带有Annotataion的POJO形式的持久化Entity。
包tomhornson.ejb3.publisher.slsb:存放SLSB,作为应用的façade,通过O/R Tool操作持
久POJO。
包tomhornson.ejb3.publisher.util:工具类。
五、 部署
1、配置数据源
1)将MS SQLServer JDBC Driver:msbase.jar、msutil.jar、mssqlserver.jar放到
${APUSIC_HOME}\lib\ext目录下。
2)启动Apusic4.0应用服务器。
3)打开Apusic RemoteManager,通过默认用户名admin、默认密码admin登陆远程管理界
面。
4)Apusic为几种流行的数据库,提供默认的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文件防止到AuthorFacade的META-INF目录中。
4、Hibernate运行依赖Jars和配置文件到EAR工程目录。
这样,EAR根目录的结构如下所示:
5、 使用部署工具,将publisher3 EAR工程部署到Apusic服务器。
OK。
一切工作完成。
六、 编程和原理探讨
1、 使用Hibernate和Annotationed 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标志过的,这些标志定义了一系列的对象关系、持久化策略、缓存策略、事务隔离性、乐观并发策略等等。
而,依赖于这些标志,Hibernate的SchemaExport Command可以自动生成Database Shema。
代码片断如下:
AnnotationConfiguration anno = new AnnotationConfiguration();
Class[] cArr = {Person.class,Author.class,Work.class,Song.class,Book.class};
List<Class> 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();
上面的代码,别后发生的事情:Hibernate从Apusic应用服务器的DataSource取得Connection,而来自服务器的DataSource是事务感应的,它在返回Connection之前,已经检测当前线程是否绑定事务,如果是,那么将Connection征集到事务中。所以,CMT Hibernate编程,你只需要任意的openSession,closeSession,不需要考虑事务回滚问题,当运行时异常或者系统级别异常发生时,容器会透明回滚所有的被征集的Connection。保证事务的原子性。
而对于事务隔离性和交叉存取问题,Hibernate也是支持的,所以基于SLSB+Hibernate可以开发事务要求很高的应用。