中间件
Application Server
我们知道所有使用 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 用户需要访问两个重要的接口。
一般而言,数据访问代码本身不关心上述问题的答案。它仅需要一个 TopLink Session 来访问对象和 TopLink UnitOfWork,以管理对对象的更改。其余的详情可以由一些外部上下文来注入。在 Spring 框架中,应用程序代码根据 BeanFactory 或 ApplicationContext 进行查找,以获取对数据访问层的访问权。这使得 Spring 框架能够执行依赖注入,以确保数据访问代码始终有一个正确配置的 TopLink Session,并且它将在正确的事务上下文中运行。 为了实现这些,需要把对 TopLink Session 的访问模板化(利用显式的模板类或利用 Spring AOP)。模板执行三种主要的功能:
类似地,所有的事务管理依赖性也由 Spring 容器注入。例如,如果代码在不支持 JTA 的环境中进行了测试,而开发小组现在希望将代码部署到由 JTA 管理事务的环境中,那么,只需通过重新配置 Spring BeanFactory 就可实现此目的。无需对数据访问层的实施进行任何更改。 很明显,这些是目前(在某种程度上)已经存在的模式。TopLink 已经可以使用 ExternalTransactionController 来与 JTA 事务同步了。TopLink 已经自带了一个 SessionManager 接口。该项目的目标是,以能够从 Spring 框架方便使用的方式,将这些概念连接在一起。 对 TopLink Session 的模板化访问为了使组件能够使用 TopLink Session,它必须声明其对 TopLink
<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 的代码现在无需使用回调就可实现这一目的。一些用户可能发现这种模式更加直观。虽然这明显是一种很主观的评估。在任何情况下,
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。然而,对于大多数情况,数据访问组件本身是无状态的,并且应当保持为单值。 作为额外的提醒,请注意上面的配置利用了接口 模板方法和 ProxyFactoryBean 方法之间最重要的差异可能是,后者可以引发检查和未检查异常。基于模板的方法仅限于引发未检查异常(在 TopLink 的情况下这不是个大问题)。 我们还没有讨论的一个细节是事务边界的概念。截取程序和模板都没有在开始或提交事务中扮演任何角色(DAO 的边界一般比典型事务的边界更小)。然而,我们必须确保数据访问层使用的任何 UnitOfWork 与当前活动的任何事务保持正确同步。我们将在稍后部分对此进行详细讨论。 TopLink Session 的使用当前 TopLink 和 Spring 之间的集成不会影响正常的 TopLink Session 的使用。换言之,希望用户对 然而,对于当前的集成而言,用户应当继续使用已记录的最佳实践。例如:
当前集成的重要差异是,由 Spring 容器管理的 Session 已被改写,以确保对以下二者中任意一个的调用都将返回应在当前事务上下文中使用的 UnitOfWork:
数据访问代码一般不涉及事务管理。大多数数据访问组件将以不可分割的方式和任意由 Spring 容器“注入”的事务一起使用。此外, 一个有争议的问题是数据访问层在当前活动的 UnitOfWork 上调用任何
如果数据访问层中的用户代码将在数据访问层的边界 内部提交更改,那么这将很可能破坏上述机制中的任何一个。TopLink 的 Spring 事务管理与会话管理类似:对外部的 TransactionManager 的依赖性由 Spring 容器注入到系统中。实际上,Spring TransactionManagement 的大多数细节完全与 TopLink 无关。同样,在 Spring 文档中有一部分很好地对该组件进行了说明。 一般而言,事务管理将以几种方式之一(它们使用事务资源的方式各不相同)来处理。 TopLink 事务管理的策略 TopLinkTransactionManager
JtaTransactionManager
应用程序代码管理 JTA
在第一个例子中,事务涉及到对仅由单个 TopLink 事务管理的对象的更改。利用这种配置,因为不存在对 JTA 的依赖性,所以可以在 J2EE 环境外部测试服务。当配置 Spring 容器时,用户必须创建一个名为
<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();
这使容器能够管理依赖任务,例如:
用户可以编写数据访问代码,而无需考虑会话管理或事务管理。因为会话管理和事务管理是包含最多环境依赖性的两个领域,所以对其分别考虑,从而可从不同环境更为轻松地测试数据访问代码。 | ||
热门下载 | ||