3.8.  J2EE应用程序服务器的集成

HIBERNATE

3.8.  J2EE应用程序服务器的集成

针对J2EE体系,Hibernate有如下几个集成的方面:

  • 容器管理的数据源(Container-managed datasources): Hibernate能使用通过容器管理,并由JNDI提供的JDBC连接. 通常, 特别是当处理多个数据源的分布式事务的时候, 由一个JTA兼容的TransactionManager和一个 ResourceManager来处理事务管理(CMT, 容器管理的事务). 当然你可以通过 编程方式来划分事务边界(BMT, Bean管理的事务). 或者为了代码的可移植性,你也也许会想使用可选的 Hibernate Transaction API.

  • 自动JNDI绑定: Hibernate可以在启动后将 SessionFactory绑定到JNDI.

  • JTA Session绑定: Hibernate Session 可以自动绑定到JTA事务作用的范围. 只需简单地从JNDI查找SessionFactory并获得当前的 Session. 当JTA事务完成时, 让Hibernate来处理 Session的清洗(flush)与关闭. 事务的划分可以是声明式的(CMT),也可以是编程式的(BMT/UserTransaction).

  • JMX部署: 如果你使用支持JMX应用程序服务器(如, JBoss AS), 那么你可以选择将Hibernate部署成托管MBean. 这将为你省去一行从Configuration构建SessionFactory的启动代码. 容器将启动你的HibernateService, 并完美地处理好服务间的依赖关系 (在Hibernate启动前,数据源必须是可用的,等等).

如果应用程序服务器抛出"connection containment"异常, 根据你的环境,也许该将配置属性 hibernate.connection.release_mode设为after_statement.

3.8.1.  事务策略配置

在你的架构中,Hibernate的Session API是独立于任何事务分界系统的. 如果你让Hibernate通过连接池直接使用JDBC, 你需要调用JDBC API来打开和关闭你的事务. 如果你运行在J2EE应用程序服务器中, 你也许想用Bean管理的事务并在需要的时候调用JTA API和UserTransaction.

为了让你的代码在两种(或其他)环境中可以移植,我们建议使用可选的Hibernate Transaction API, 它包装并隐藏了底层系统. 你必须通过设置Hibernate配置属性hibernate.transaction.factory_class来指定 一个Transaction实例的工厂类.

有三个标准(内建)的选择:

org.hibernate.transaction.JDBCTransactionFactory

委托给数据库(JDBC)事务(默认)

org.hibernate.transaction.JTATransactionFactory

如果在上下文环境中存在运行着的事务(如, EJB会话Bean的方法), 则委托给容器管 理的事务, 否则,将启动一个新的事务,并使用Bean管理的事务.

org.hibernate.transaction.CMTTransactionFactory

委托给容器管理的JTA事务

你也可以定义属于你自己的事务策略 (如, 针对CORBA的事务服务)

Hibernate的一些特性 (比如二级缓存, Contextual Sessions with JTA等等)需要访问在托管环境中的JTA TransactionManager. 由于J2EE没有标准化一个单一的机制,Hibernate在应用程序服务器中,你必须指定Hibernate如何获得TransactionManager的引用:

表 3.10. JTA TransactionManagers

Transaction工厂类 应用程序服务器
org.hibernate.transaction.JBossTransactionManagerLookup JBoss
org.hibernate.transaction.WeblogicTransactionManagerLookup Weblogic
org.hibernate.transaction.WebSphereTransactionManagerLookup WebSphere
org.hibernate.transaction.WebSphereExtendedJTATransactionLookup WebSphere 6
org.hibernate.transaction.OrionTransactionManagerLookup Orion
org.hibernate.transaction.ResinTransactionManagerLookup Resin
org.hibernate.transaction.JOTMTransactionManagerLookup JOTM
org.hibernate.transaction.JOnASTransactionManagerLookup JOnAS
org.hibernate.transaction.JRun4TransactionManagerLookup JRun4
org.hibernate.transaction.BESTransactionManagerLookup Borland ES

3.8.2.  JNDI绑定的SessionFactory

与JNDI绑定的Hibernate的SessionFactory能简化工厂的查询,简化创建新的Session. 需要注意的是这与JNDI绑定Datasource没有关系, 它们只是恰巧用了相同的注册表!

如果你希望将SessionFactory绑定到一个JNDI的名字空间, 用属性hibernate.session_factory_name指定一个名字(如, java:hibernate/SessionFactory). 如果不设置这个属性, SessionFactory将不会被绑定到JNDI中. (在以只读JNDI为默认实现的环境中,这个设置尤其有用, 如Tomcat.)

在将SessionFactory绑定至JNDI时, Hibernate将使用hibernate.jndi.url, 和hibernate.jndi.class的值来实例化初始环境(initial context). 如果它们没有被指定, 将使用默认的InitialContext.

在你调用cfg.buildSessionFactory()后, Hibernate会自动将SessionFactory注册到JNDI. 这意味这你至少需要在你应用程序的启动代码(或工具类)中完成这个调用, 除非你使用HibernateService来做JMX部署 (见后面讨论).

假若你使用JNDI SessionFactory,EJB或者任何其它类都可以从JNDI中找到此SessionFactory

我们建议,在受管理的环境中,把SessionFactory绑定到JNDI,在其它情况下,使用一个static(静态的)singleton。为了在你的应用程序代码中隐藏这些细节,我们还建议你用一个helper类把实际查找SessionFactory的代码隐藏起来,比如HibernateUtil.getSessionFactory()。注意,这个类也就可以方便地启动Hibernate,参见第一章。

3.8.3. 在JTA环境下使用Current Session context (当前session上下文)管理

在Hibernate中,管理Session和transaction最好的方法是自动的"当前"Session管理。请参见第 2.5 节 “上下文相关的(Contextual)Session”一节的讨论。使用"jta"session上下文,假若在当前JTA事务中还没有HibernateSession关联,第一次sessionFactory.getCurrentSession()调用会启动一个Session,并关联到当前的JTA事务。在"jta"上下文中调用getCurrentSession()获得的Session,会被设置为在transaction关闭的时候自动flush(清洗)、在transaction关闭之后自动关闭,每句语句之后主动释放JDBC连接。这就可以根据JTA事务的生命周期来管理与之关联的Session,用户代码中就可以不再考虑这些管理。你的代码也可以通过UserTransaction用编程方式使用JTA,或者(我们建议,为了便于移植代码)使用Hibernate的Transaction API来设置transaction边界。如果你的代码运行在EJB容器中,建议对CMT使用声明式事务声明。

3.8.4.  JMX部署

为了将SessionFactory注册到JNDI中,cfg.buildSessionFactory()这行代码仍需在某处被执行. 你可在一个static初始化块(像HibernateUtil中的那样)中执行它或将Hibernate部署为一个托管的服务.

为了部署在一个支持JMX的应用程序服务器上,Hibernate和 org.hibernate.jmx.HibernateService一同分发,如Jboss AS。 实际的部署和配置是由应用程序服务器提供者指定的. 这里是JBoss 4.0.x的jboss-service.xml样例:

<?xml version="1.0"?>
<server>

<mbean code="org.hibernate.jmx.HibernateService"
    name="jboss.jca:service=HibernateFactory,name=HibernateFactory">

    <!-- 必须的服务 -->
    <depends>jboss.jca:service=RARDeployer</depends>
    <depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>

    <!-- 将Hibernate服务绑定到JNDI -->
    <attribute name="JndiName">java:/hibernate/SessionFactory</attribute>

    <!-- 数据源设置 -->
    <attribute name="Datasource">java:HsqlDS</attribute>
    <attribute name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>

    <!-- 事务集成 -->
    <attribute name="TransactionStrategy">
        org.hibernate.transaction.JTATransactionFactory</attribute>
    <attribute name="TransactionManagerLookupStrategy">
        org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
    <attribute name="FlushBeforeCompletionEnabled">true</attribute>
    <attribute name="AutoCloseSessionEnabled">true</attribute>

    <!-- 抓取选项 -->
    <attribute name="MaximumFetchDepth">5</attribute>

    <!-- 二级缓存 -->
    <attribute name="SecondLevelCacheEnabled">true</attribute>
    <attribute name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
    <attribute name="QueryCacheEnabled">true</attribute>

    <!-- 日志 -->
    <attribute name="ShowSqlEnabled">true</attribute>

    <!-- 映射定义文件 -->
    <attribute name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>

</mbean>

</server>

这个文件是部署在META-INF目录下的, 并会被打包到以.sar (service archive)为扩展名的JAR文件中. 同时,你需要将Hibernate、它所需要的第三方库、你编译好的持久化类以及你的映射定义文件打包进同一个文档. 你的企业Bean(一般为会话Bean)可能会被打包成它们自己的JAR文件, 但你也许会将EJB JAR文件一同包含进能独立(热)部署的主服务文档. 参考JBoss AS文档以了解更多的JMX服务与EJB部署的信息.