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

优质
小牛编辑
124浏览
2023-12-01

针对 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.JBossTransactionManagerLookupJBoss
org.hibernate.transaction.WeblogicTransactionManagerLookupWeblogic
org.hibernate.transaction.WebSphereTransactionManagerLookupWebSphere
org.hibernate.transaction.WebSphereExtendedJTATransactionLookupWebSphere 6
org.hibernate.transaction.OrionTransactionManagerLookupOrion
org.hibernate.transaction.ResinTransactionManagerLookupResin
org.hibernate.transaction.JOTMTransactionManagerLookupJOTM
org.hibernate.transaction.JOnASTransactionManagerLookupJOnAS
org.hibernate.transaction.JRun4TransactionManagerLookupJRun4
org.hibernate.transaction.BESTransactionManagerLookupBorland 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 上下文)管理

The easiest way to handle Sessions and transactions is Hibernate's automatic "current" Session management. For a discussion of contextual sessions see 第 2.5 节 “上下文相关的会话(Contextual Session)”. Using the "jta" session context, if there is no Hibernate Session associated with the current JTA transaction, one will be started and associated with that JTA transaction the first time you call sessionFactory.getCurrentSession(). The Sessions retrieved via getCurrentSession() in the "jta" context are set to automatically flush before the transaction completes, close after the transaction completes, and aggressively release JDBC connections after each statement. This allows the Sessions to be managed by the life cycle of the JTA transaction to which it is associated, keeping user code clean of such management concerns. Your code can either use JTA programmatically through UserTransaction, or (recommended for portable code) use the Hibernate Transaction API to set transaction boundaries. If you run in an EJB container, declarative transaction demarcation with CMT is preferred.

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">

    <!-- Required services -->
    <depends
>jboss.jca:service=RARDeployer</depends>
    <depends
>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>

    <!-- Bind the Hibernate service to JNDI -->
    <attribute name="JndiName"
>java:/hibernate/SessionFactory</attribute>

    <!-- Datasource settings -->
    <attribute name="Datasource"
>java:HsqlDS</attribute>
    <attribute name="Dialect"
>org.hibernate.dialect.HSQLDialect</attribute>

    <!-- Transaction integration -->
    <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>

    <!-- Fetching options -->
    <attribute name="MaximumFetchDepth"
>5</attribute>

    <!-- Second-level caching -->
    <attribute name="SecondLevelCacheEnabled"
>true</attribute>
    <attribute name="CacheProviderClass"
>org.hibernate.cache.EhCacheProvider</attribute>
    <attribute name="QueryCacheEnabled"
>true</attribute>

    <!-- Logging -->
    <attribute name="ShowSqlEnabled"
>true</attribute>

    <!-- Mapping files -->
    <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 部署的信息。