什么是 Spring TopLink 集成?

我们知道所有使用 TopLink 的代码都将需要访问 TopLink Session(特别是 oracle.toplink.sessions.Session)。在 IoC 容器中,组件对资源(例如 TopLink Session)的依赖性将由容器来管理。因此,Spring TopLink 集成提供了 Spring 框架的一个扩展,该扩展将使用户能够配置 Spring ApplicationContext,后者将把 TopLink Session 注入到 DataAccess 层中。

在下图中,唯一实际使用 TopLink 的组件是 DataAccess Bean。然而,不必担心 TopLink Session 的初始化,因为这种依赖性在容器层进行了声明。类似地,DataAccessBean 的事务范围可以由容器通过 TopLink 特定的 PlatformTransactionManager 或 JtaTransactionManager 来管理。

所有的 Session Management 配置都在容器层(而不是在数据访问层)执行。

 

 

这种集成不会改变 TopLink Session 的基本使用模式。我们仍然希望用户对 TopLink Session api 有充分的了解。不过,这种集成将确保 TopLink Session 的所有环境依赖性都由 Spring 容器来管理。

简介

TopLink 用户需要访问两个重要的接口。 oracle.toplink.sessions.Session 接口是所有数据访问的起始点, oracle.toplink.sessions.UnitOfWork 接口允许数据访问层对底层对象模型进行更改。这两种资源都带有环境依赖性,例如:

  • 数据访问代码是在单线程还是多线程环境中运行?
  • 提供了哪些数据库资源?
  • 该会话的缓存是否与在其他地方运行的会话缓存相关联?
  • 事务是如何管理的:
    • 在应用程序代码中本地管理?
    • 使用外部定义的事务管理器来管理?
    • 在 J2EE 环境中使用 JTA TransactionManager 来管理?

一般而言,数据访问代码本身不关心上述问题的答案。它仅需要一个 TopLink Session 来访问对象和 TopLink UnitOfWork,以管理对对象的更改。其余的详情可以由一些外部上下文来注入。在 Spring 框架中,应用程序代码根据 BeanFactory 或 ApplicationContext 进行查找,以获取对数据访问层的访问权。这使得 Spring 框架能够执行依赖注入,以确保数据访问代码始终有一个正确配置的 TopLink Session,并且它将在正确的事务上下文中运行。

为了实现这些,需要把对 TopLink Session 的访问模板化(利用显式的模板类或利用 Spring AOP)。模板执行三种主要的功能:

  1. 它确保 Session 正确配置,并使用正确的事务上下文
  2. 它捕获所有的异常,并将所有的异常转变为标准的 Spring DataAccessException 层次结构
  3. 一旦数据操作完成,它将确保释放所有的资源(如果必要的话)

 

类似地,所有的事务管理依赖性也由 Spring 容器注入。例如,如果代码在不支持 JTA 的环境中进行了测试,而开发小组现在希望将代码部署到由 JTA 管理事务的环境中,那么,只需通过重新配置 Spring BeanFactory 就可实现此目的。无需对数据访问层的实施进行任何更改。

很明显,这些是目前(在某种程度上)已经存在的模式。TopLink 已经可以使用 ExternalTransactionController 来与 JTA 事务同步了。TopLink 已经自带了一个 SessionManager 接口。该项目的目标是,以能够从 Spring 框架方便使用的方式,将这些概念连接在一起。

对 TopLink Session 的模板化访问

为了使组件能够使用 TopLink Session,它必须声明其对 TopLink SessionFactory 的依赖性。这是通过在 Spring beans.xml 文件中创建适当项目来实现的。在下面的示例中,一个名称为 myDAOImpl 的数据访问组件将通过声明对 toplinkSessionFactory 组件的依赖性来获得对 TopLink Session 的访问。 toplinkSessionFactory 组件本身使用 TopLink sessions.xml 文件来进行配置。

<beans>
                                

  <bean id="toplinkSessionFactory"
                                

        class="org.springframework.orm.toplink.SessionFactoryBean">
                                

    <property name="sessionsConfig">
                                

      <value>sessions.xml</value>
                                

    </property>
                                

    <property name="sessionName">
                                

      <value>mySession"</value>
                                

    </property>
                                

  </bean>
                                

                                 

  <bean id="myDAOImpl" class="domain.model.MyDAOImpl">
                                

    <property name="sessionFactory">
                                

      <ref bean="toplinkSessionFactory">
                                

    </property>
                                

  </bean>
                                

</beans>
                                

                                 

                              

DAO 的实施有许多可以访问 TopLink Session 的方式。所有的 TopLink 代码都可以放在 TopLinkCallback 对象中,以便可以显式地将对 TopLink 的访问模板化。该模式的一个示例显示如下:

    public class MyDAOImpl extends TopLinkDaoSupport implements DAOInterface
                                

    {
                                

        public Object doTopLinkOperation()
                                

        {
                                

            return getTopLinkTemplate().execute(
                                

                                 

            new TopLinkCallback() {
                                

            public Object doInTopLink(Session session) throws TopLinkException
                                

            {
                                

                // this Session is managed externally.
                                

// It is "injected" by the currently referenced SessionFactory
                                

            }
                                

            });
                                

        }
                                

    }
                                

                                 

                              

虽然该示例使用了一个匿名内部类,但这些 回调类也可以单独开发和管理。此外,许多最常见的 TopLink 操作都在 TopLinkTemplate 类上公开,以便可以用类似以下示例的方式来编写代码:

    public class MyDAOImpl extends TopLinkDaoSupport implements DAOInterface
                                

    {
                                

        public Object doTopLinkOperation()
                                

        {
                                

            return getTopLinkTemplate().findByNamedQuery
                                

("findMyself",Person.class,args);
                                

        }
                                

    }
                                

                              

在任何情况下,DAO 都不负责构建或发布 TopLink Session,如果代码要访问 UnitOfWork 对象,那么它将保证与任何当前活动的事务类型( 一定是 JTA 事务)同步。

这种基于模板的机制的一种替代方式是使用 Spring AOP。在这种方式中,将配置一个截取程序来完成 TopLinkTemplate 的工作。用户可以通过配置 Spring ProxyFactoryBean 来确保截取他们的数据访问代码。对于 Spring 中的一切,代理 (Proxy) 和截取程序 (Interceptor) 都是以标准方式注入其依赖性的组件。在以下示例中,请注意 TopLinkInterceptor 具有对相应 TopLinkSessionFactory 的引用,代理本身使用 ProxyFactoryBean 创建。关于配置 ProxyFactoryBeans 的详细信息,请参阅有关 Spring AOP 的文档。

  <bean id="myTopLinkInterceptor"
                                

   class="org.springframework.orm.toplink.TopLinkInterceptor" >
                                

    <property name="sessionFactory">
                                

      <ref bean="toplinkSessionFactory">
                                

    </property>
                                

  </bean>
                                

                                 

  <bean id="myDao"
                                

   class="org.springframework.aop.framework.ProxyFactoryBean">
                                

    <property name="proxyInterfaces">
                                

      <value>domain.model.DAOInterface</value>
                                

    </property>
                                

    <property name="interceptorNames">
                                

      <list>
                                

        <value>myTopLinkInterceptor</value>
                                

        <value>myDaoImpl</value>
                                

      </list>
                                

    </property>
                                

  </bean>
                                

                                 

                              

访问 TopLink 的代码现在无需使用回调就可实现这一目的。一些用户可能发现这种模式更加直观。虽然这明显是一种很主观的评估。在任何情况下, MyDAOImpl 中的代码都不再需要模板:

public class MyDaoImpl extends TopLinkDaoSupport implements DAOInterface
                                

{
                                

    public Object doTopLinkOperation() throws MyException
                                

    {
                                

        Session session =
                                

         SessionFactoryUtils.getSession(getSessionFactory(), false);
                                

        try
                                

        {
                                

            // Session is ready to use in this code block
                                

        }
                                

        catch (TopLinkException ex)
                                

        {
                                

            throw TopLinkSessionFactoryUtils.convertTopLinkAccessException(ex);
                                

        }
                                

    }
                                

}
                                

                                 

                              

如果用户愿意将 MyDAOImpl 组件作为原型而不是单值 (singleton),那么也可以删除上面代码中使用的 SessionFactoryUtils。然而,对于大多数情况,数据访问组件本身是无状态的,并且应当保持为单值。

作为额外的提醒,请注意上面的配置利用了接口 DAOInterface 的存在性。不过,Spring ProxyFactoryBean 不依赖于该接口的存在性。如果指定了接口,那么 Spring 将能够使用一个动态代理 (Dynamic Proxy) 来实施行为。然而,如果没有指定接口,那么 ProxyFactoryBean 将使用 CGLib 来构建适当的代理。同样,这不是 TopLink 特有的行为。而对于 Spring AOP 一般是这样的。

模板方法和 ProxyFactoryBean 方法之间最重要的差异可能是,后者可以引发检查和未检查异常。基于模板的方法仅限于引发未检查异常(在 TopLink 的情况下这不是个大问题)。

我们还没有讨论的一个细节是事务边界的概念。截取程序和模板都没有在开始或提交事务中扮演任何角色(DAO 的边界一般比典型事务的边界更小)。然而,我们必须确保数据访问层使用的任何 UnitOfWork 与当前活动的任何事务保持正确同步。我们将在稍后部分对此进行详细讨论。

TopLink Session 的使用

当前 TopLink 和 Spring 之间的集成不会影响正常的 TopLink Session 的使用。换言之,希望用户对 SessionUnitOfWork api 完全了解。然而,在未来的工作中,我们希望将不同类型的 TopLink Sessions 注入到数据访问层中。例如,Session,其查询将返回可以立即编辑而无需注册到 UnitOfWork 中的对象。当前的框架非常适合于引进新的 Session 特性,因为注入增强的 TopLink Session 仅涉及 SessionFactory 组件的简单重组。

然而,对于当前的集成而言,用户应当继续使用已记录的最佳实践。例如:

  • 最大限度使用命名查询。
  • 从 Session 查询返回的所有对象都是严格只读的,不能被编辑的。
  • 对象在更改之前必须在活动的 UnitOfWork 中注册。

当前集成的重要差异是,由 Spring 容器管理的 Session 已被改写,以确保对以下二者中任意一个的调用都将返回应在当前事务上下文中使用的 UnitOfWork:

  • getActiveUnitOfWork()
  • acquireUnitOfWork()

数据访问代码一般不涉及事务管理。大多数数据访问组件将以不可分割的方式和任意由 Spring 容器“注入”的事务一起使用。此外, getActiveUnitOfWorkacquireUnitOfWork 之间的差异意味着要了解一些当前 Session 的配置方式,这违反了“依赖注入”的想法。因此,对于 Spring 注入的 Session,上面两个方法的行为是相同的。

一个有争议的问题是数据访问层在当前活动的 UnitOfWork 上调用任何 commit() 方法是否有意义。正如您将在下一部分中看到的那样,UnitOfWork 的生命周期一般由以下组件之一来管理:

  • 非 J2EE 环境中的 TopLinkTransactionManager
  • J2EE 环境中的 JTATransactionManager
  • 不使用 Spring PlatformTransactionManager 的 J2EE 环境中的 UserTransaction 和 TopLink ExternalTransactionController

如果数据访问层中的用户代码将在数据访问层的边界 内部提交更改,那么这将很可能破坏上述机制中的任何一个。TopLink 的 ExternalTransactionController 接口通过在 UnitOfWork 与外部事务保持同步时,禁止在其上进行提交来处理这种情况。在当前的集成中,我们采用了一种类似的方式,当 UnitOfWork 由某些 Spring 的 PlatformTransactionManager 实施管理时禁止在其上执行“提交”操作。

事务管理

Spring 事务管理与会话管理类似:对外部的 TransactionManager 的依赖性由 Spring 容器注入到系统中。实际上,Spring TransactionManagement 的大多数细节完全与 TopLink 无关。同样,在 Spring 文档中有一部分很好地对该组件进行了说明。

一般而言,事务管理将以几种方式之一(它们使用事务资源的方式各不相同)来处理。

TopLink 事务管理的策略

TopLinkTransactionManager
( org.springframework.orm.toplink)

  • 应用:当事务仅涉及对一个 TopLink Session 的更改时非常适用
  • 依赖性:TopLink 管理数据库提交。 不依赖 JTA 的存在性。
  • TopLink 配置
    • TopLink 不使用 ExternalTransactionController
    • 可以使用数据源或 TopLink 管理的 连接池

JtaTransactionManager
( org.springframework.transaction)

  • 应用:当事务涉及对几个 TopLink Session 的修改时非常适用
  • 依赖性:需要 JTA。
  • TopLink 配置
    • TopLink Session 不使用 ExternalTransactionController(这似乎有点令人吃惊)
    • 必须配置 TopLink 登录来使用事务数据源
    • 必须用 useExternalTransactionController 来配置 TopLink 登录

应用程序代码管理 JTA
( UserTransaction)

  • 应用:与 JtaTransactionManager 类似,但没有使用 Spring 来配置边界
  • 依赖性:需要 JTA。
  • TopLink 配置
    • 必须配置 TopLink Session 来使用 ExternalTransactionController
    • 必须配置 TopLink 登录来使用外部数据源
    • 必须用 useExternalTransactionController 来配置 TopLink 登录

在第一个例子中,事务涉及到对仅由单个 TopLink 事务管理的对象的更改。利用这种配置,因为不存在对 JTA 的依赖性,所以可以在 J2EE 环境外部测试服务。当配置 Spring 容器时,用户必须创建一个名为 myTransactionManagerTransactionManager 组件,然后必须声明它们的服务组件依赖于这个 transactionManager。此种配置示例如下:

  <bean id="myTransactionManager"
                                

   class="org.springframework.orm.toplink.TopLinkTransactionManager">
                                

    <property name="sessionFactory">
                                

      <value>toplinkSessionFactory</value>
                                

    </property>
                                

  </bean>
                                

                                 

  <bean id="serviceRunningInTransaction" class="domain.ServiceImpl">
                                

    <property name="transactionManager">
                                

      <value>myTransactionManager</value>
                                

    </property>
                                

    <property name="dao">
                                

      <ref bean="myDao"/>
                                

    </property>
                                

  </bean>
                                

                                 

                              

该服务使用在上一部分中定义的 "myDAO" 数据访问组件。对该 DAO 的所有操作都由 TopLinkTransactionManager 来管理。在 Spring 文档中,您将注意到服务可以选择使用事务模板来定义事务边界,或者与会话管理一样,事务管理可以利用 Spring AOP 以声明的方式来执行。此处没有提供关于这种模式的特定于 TopLink 的信息,因此用户应当参考 Spring 文档,以了解更多的详情。然而,出于完整性的考虑,我们将给出一个示例,以说明如何能够使用 Spring TransactionProxyFactoryBean 来以声明的方式确定事务的边界:

  <bean id="serviceUsingDeclarativeTransaction"
                                

    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
                                

    <property name="transactionManager">
                                

      <ref bean="myTransactionManager"/>
                                

    </property>
                                

    <property name="target>
                                

      <ref bean="myService"/>
                                

    </property>
                                

    <property
                                

      name="transactionAttributes">
                                

      <props>
                                

        <prop key="insert*">PROPAGATION_REQUIRED,-MyCheckedException</prop>
                                

        <prop key="update*">PROPAGATION_REQUIRED</prop>
                                

        <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
                                

      </props>
                                

    </property>
                                

  </bean>
                                

                                 

  <bean id="myService" class="domain.MyServiceBean">
                                

    <property name="dao">
                                

      <ref bean="myDAO"/>
                                

    </property>
                                

  </bean>
                                

                              

实质上,上面的定义将使 Spring 知道 serviceImpl 接口中的所有方法都 需要一个活动事务,或者加入一个现有事务(如果已经存在一个活动事务)。此外,通过插入开始的方法如果引发特定异常,则应当将其回滚,而所有的方法(除了以插入或更新开始的方法)都是严格只读的。而且,服务实施程序依赖于 "myDAO" 组件,该组件自身依赖于 TopLink SessionFactory。服务本身没有包含特定于 TopLink 的代码。它只是将其需要的数据访问组件组装在一起,并控制整体的事务。

如果 "myService" 需要执行涉及到对两个不同的 TopLink Session 的更改的操作,那么用户需要包含 JTA UserTransaction。这完全不涉及对服务组件的配置的更改。然而,服务组件使用的 TransactionManager 将需要按如下方式进行重新配置:

 

<bean id="myTransactionManager"
                                

 class="org.springframework.transaction.jta.JtaTransactionManager"/>
                                

                              

 

现在,"myService" 可以使用数据访问组件从多个 TopLink Session 访问资源。很明显,这里最重要的细节是在切换 TransactionManagement 策略的过程中无需更改任何 Java 代码。通过使用许多不同的事务管理模式,数据访问组件本身在许多不同的环境中保持可用以及可测试。

总结

我们介绍了 TopLink 和 Spring Framework 之间的集成,它将依赖注入的概念应用到了需要 TopLink Session 服务的任意代码中。在配置之后,容器可以在组件查找执行时将依赖性注入到运行时系统中。最后,服务使用诸如以下的代码来运行:

MyTxInterface service =
                                

  (MyTxInterface) factory.getBean("serviceUsingDeclarativeTransaction");
                                

service.doInTransaction();
                              

这使容器能够管理依赖任务,例如:

  • 利用截获程序来设置代理以处理事务的开始和提交
  • 利用对其依赖的各个服务或数据访问组件的引用来初始化服务实施
  • 准备好数据访问组件以注入正确配置的 TopLink Session。

用户可以编写数据访问代码,而无需考虑会话管理或事务管理。因为会话管理和事务管理是包含最多环境依赖性的两个领域,所以对其分别考虑,从而可从不同环境更为轻松地测试数据访问代码。

Left Curve
热门下载
Right Curve