Spring中的TopLink ServerSession

开发 后端
本文介绍Spring中的TopLink ServerSession,配置TopLink session文件。

SessionFactory 抽象层
TopLink本身并没有提供SessionFactory抽象层逻辑,多线程的数据访问是建立在中央 ServerSession 上的。对于单线程访问, 这个中央 ServerSession 会为它一个 ClientSession 的实例供其使用。为了提供灵活便捷的创建选项, Spring为TopLink定义了一个 SessionFactory 接口,从而使你可以任意地在不同的 Session 创建策略之间进行切换。

作为一个一站式的商店,Spring提供了一个 LocalSessionFactoryBean 类,允许你以bean风格的配置方式来定义一个TopLink SessionFactory。 需要进行配置的地方主要是TopLink session配置文件,通常来说还需配置一个受到Spring管理的JDBC DataSource。

  1. <beans> 
  2. <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource                                                            " destroy-method="close"> 
  3.     <property name="driverClassName" value="${jdbc.driverClassName}"/> 
  4.     <property name="url" value="${jdbc.url}"/> 
  5.     <property name="username" value="${jdbc.username}"/> 
  6.     <property name="password" value="${jdbc.password}"/> 
  7. bean> 
  8. <bean id="mySessionFactory" class="org.springframework.orm.toplink.                                            LocalSessionFactoryBean"> 
  9.     <property name="configLocation" value="toplink-sessions.xml"/> 
  10.     <property name="dataSource" ref="dataSource"/> 
  11. bean> 
  12.      
  13. beans> 
  14. <toplink-configuration> 
  15. <session> 
  16.     <name>Sessionname> 
  17.     <project-xml>toplink-mappings.xmlproject-xml> 
  18.     <session-type> 
  19.       <server-session/> 
  20.     session-type> 
  21.     <enable-logging>trueenable-logging> 
  22.     <logging-options/> 
  23. session> 
  24. toplink-configuration>  

通常情况下,LocalSessionFactoryBean 在底层将持有一个多线程的TopLink ServerSession 并创建合适的客户端 Session: 它或者是一个普通的 Session(典型情况) —— 一个受管理的 ClientSession;或者是一个具备事务功能的 Session (后者主要在Spring内部对TopLink的支持中被使用)。还有一种情况,LocalSessionFactoryBean 可能会持有一个单线程的TopLink的 DatabaseSession,这是非常特殊的情况了。

TopLinkTemplate and TopLinkDaoSupport
每个基于TopLink的DAO将通过IoC被注入一个 SessionFactory,你可以通过Setter方式注入,也可以用构造函数方式注入。这样的DAO可以直接操作原生的TopLink API,通过 SessionFactory 来获取一个 Session, 但是通常情况下,你更愿意使用Spring的 TopLinkTemplate:

  1. <beans> 
  2. <bean id="myProductDao" class="product.ProductDaoImpl"> 
  3.     <property name="sessionFactory" ref="mySessionFactory"/> 
  4. bean> 
  5. beans> 
  6. public class TopLinkProductDao implements ProductDao {  
  7.     private TopLinkTemplate tlTemplate;  
  8.     public void setSessionFactory(SessionFactory sessionFactory) {  
  9.         this.tlTemplate = new TopLinkTemplate(sessionFactory);  
  10.     }  
  11.     public Collection loadProductsByCategory(final String category)                                            throws DataAccessException {  
  12.       return (Collection) this.tlTemplate.execute(new TopLinkCallback() {  
  13.        public Object doInTopLink(Session session) throws TopLinkException {  
  14.         ReadAllQuery findOwnersQuery = new ReadAllQuery(Product.class);  
  15.         findOwnersQuery.addArgument("Category");  
  16.         ExpressionBuilder builder = this.findOwnersQuery.getExpressionBuilder();  
  17.          findOwnersQuery.setSelectionCriteria(  
  18.               builder.get("category").like(builder.getParameter("Category")));  
  19.                 Vector args = new Vector();  
  20.                 args.add(category);  
  21.                 List result = session.executeQuery(findOwnersQuery, args);  
  22.                 // do some further stuff with the result list  
  23.                 return result;  
  24.             }  
  25.         }
  26.     }  
  27. }  

一个回调的实现能够有效地在任何TopLink数据访问中使用。TopLinkTemplate 会确保当前的 Session 对象的正确打开和关闭,并自动参与到事务管理中去。 Template实例不仅是线程安全的,同时它也是可重用的。因而他们可以作为外部对象的实例变量而被持有。对于那些简单的诸如 executeQuery、readAll、readById 和 merge 操作的调用,TopLinkTemplate提供可选择的快捷函数来替换这种回调的实现。 不仅如此,Spring还提供了一个简便的 TopLinkDaoSupport 基类,这个类提供了 setSessionFactory(..) 方法来接受一个 SessionFactory 对象,同时提供了 getSessionFactory() 和 getTopLinkTemplate() 方法给子类使用。综合了这些,对于那些典型的业务需求,就有了一个非常简单的DAO实现。

  1. public class ProductDaoImpl extends TopLinkDaoSupport implements ProductDao {  
  2.     public Collection loadProductsByCategory(String category) throws DataAccessException {  
  3.         ReadAllQuery findOwnersQuery = new ReadAllQuery(Product.class);  
  4.         findOwnersQuery.addArgument("Category");  
  5.         ExpressionBuilder builder = this.findOwnersQuery.getExpressionBuilder();  
  6.         findOwnersQuery.setSelectionCriteria(  
  7.             builder.get("category").like(builder.getParameter("Category")));  
  8.         return getTopLinkTemplate().executeQuery(findOwnersQuery, new Object[] {category});  
  9.     }  
  10. }  

边注:TopLink查询对象是线程安全的,并且能够在DAO层被缓存。在一开始被创建时以实例变量的方式被保持。

作为不使用Spring的 TopLinkTemplate 来实现DAO的替代解决方案, 你依然可以通过原生TopLink API对那些基于Spring的DAO进行编程,此时你必须明确地打开和关 闭一个 Session。正如在相应的Hibernate章节描述的一样,这种做法的主要优点在于你的数据访问代码可以在整个过程中抛出checked exceptions。 TopLinkDaoSupport 为这种情况提供了多种函数支持,包括获取和释放 一个具备事务的 Session 并做相关的异常转化。

基于原生的TopLink API的DAO实现
我们可以直接操作TopLink API来实现DAO,直接使用一个注入的 Session 而无需对Spring产生的任何依赖。它通常基于一个由 LocalSessionFactoryBean 定义的 SessionFactory,并通过Spring的 TransactionAwareSessionAdapter 暴露成为一个 Session 类型的引用。

TopLink的 Session 接口中定义的 getActiveSession() 方法将返回当前具备事务管理功能的 Session 对象。如果当前没有处于活跃状态的事务, 这个函数将返回一个共享的TopLink ServerSession,也就是说,这种情况应该只是一个直接使用的只读访问。另外还有一个 getActiveUnitOfWork() 方法, 返回TopLink的与当前事务绑定的 UnitOfWork (如果没有当前事务则返回 null)。

一个相应的DAO实现类看上去就像下面那样:

  1. public class ProductDaoImpl implements ProductDao {  
  2.     private Session session;  
  3.     public void setSession(Session session) {  
  4.         this.session = session;  
  5.     }  
  6.     public Collection loadProductsByCategory(String category) {  
  7.         ReadAllQuery findOwnersQuery = new ReadAllQuery(Product.class);  
  8.         findOwnersQuery.addArgument("Category");  
  9.         ExpressionBuilder builder = this.findOwnersQuery.getExpressionBuilder();  
  10.         findOwnersQuery.setSelectionCriteria(  
  11.             builder.get("category").like(builder.getParameter("Category")));  
  12.         Vector args = new Vector();  
  13.         args.add(category);  
  14.         return session.getActiveSession().executeQuery(findOwnersQuery, args);  
  15.     }  
  16. }  

上面我们所列出的DAO完全遵循IoC:它如同使用Spring的 TopLinkTemplate 进行编码那样,非常适合在application context中进行配置。Spring的 TransactionAwareSessionAdapter 将暴露一个 Session 类型的bean的引用,并传入到DAO中去:

  1. <beans> 
  2. <bean id="mySessionAdapter" 
  3.       class="org.springframework.orm.toplink.support.                                                            TransactionAwareSessionAdapter"> 
  4.     <property name="sessionFactory" ref="mySessionFactory"/> 
  5. bean> 
  6. <bean id="myProductDao" class="product.ProductDaoImpl"> 
  7.     <property name="session" ref="mySessionAdapter"/> 
  8. bean> 
  9. beans>  

这种DAO风格的主要好处在于它仅仅依赖于TopLink自身的API,而无需引入任何的Spring 的类。从无入侵性的角度来看,这一点非常吸引人。同时,对于TopLink的开发人员来说也更自然。

然而,这样的DAO访问方式会抛出 TopLinkException (这是一个无需声明或捕获的unchecked exception),这意味着,DAO的调用者只能以普通的错误来处理这些异常,除非完全依赖TopLink自身的异常体系。因而,除非你将DAO的调用者绑定到具体的实现策略上去,否则你将无法捕获特定的异常原因(诸如乐观锁异常)。这种折中平衡或许可以被接受,如果你的应用完全基于TopLink或者无需进行特殊的异常处理。

这样的DAO风格有一个不利因素在于TopLink的标准的 getActiveSession() 函数仅仅在JTA事务中有效。而对于其他的事务管理策略尤其时本地的TopLink事务,它将 无法 工作。

幸运的是,Spring的 TransactionAwareSessionAdapter 为TopLink ServerSession 暴露了一个相应的代理类。 这个代理类能够在任何的事务策略之上支持TopLink的 Session.getActiveSession() 和 Session.getActiveUnitOfWork() 函数,返回当前收到Spring管理 (即便由 TopLinkTransactionManager 管理)的具备事务管理功能的 Session 实例。当然,这个函数的标准行为依然有效:返回与当前的JTA事务绑定的 Session 对象。 (无论这个JTA事务是由Spring的 JtaTransactionManager、 EJB CMT或者普通的JTA所驱动的事务)。

总体来说,DAO可以基于TopLink的原生API实现,同时,它依旧需要能够参与到Spring的事务管理中。这对于那些已经对TopLink非常熟悉的人来说很有吸引力,因为这种方式更加自然。不过,这种DAO将抛出 TopLinkException,因而,如果有必要的话需要明确地去做由 TopLinkException 到Spring的 DataAccessException 的转化。

事务管理
将事务管理纳入到Service操作的执行中,你可以使用Spring通用的声明式的事务管理功能,参加下面的例子:

  1. xml version="1.0" encoding="UTF-8"?> 
  2. <beans 
  3.         xmlns="http://www.springframework.org/schema/beans" 
  4.         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  5.         xmlns:aop="http://www.springframework.org/schema/aop" 
  6.         xmlns:tx="http://www.springframework.org/schema/tx" 
  7.         xsi:schemaLocation="  
  8.    http://www.springframework.org/schema/beans                                  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
  9.    http://www.springframework.org/schema/tx                                              http://www.springframework.org/schema/tx/spring-tx-2.5.xsd  
  10.    http://www.springframework.org/schema/aop                                        http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> 
  11. <bean id="myTxManager" class="org.springframework.orm.toplink.                                       TopLinkTransactionManager"> 
  12.     <property name="sessionFactory" ref="mySessionFactory"/> 
  13. bean> 
  14. <bean id="myProductService" class="product.ProductServiceImpl"> 
  15.     <property name="productDao" ref="myProductDao"/> 
  16. bean> 
  17. <aop:config> 
  18.     <aop:pointcut id="productServiceMethods" expression="execution                                          (* product.ProductService.*(..))"/> 
  19.     <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/> 
  20. aop:config> 
  21. <tx:advice id="txAdvice" transaction-manager="myTxManager"> 
  22.     <tx:attributes> 
  23.       <tx:method name="increasePrice*" propagation="REQUIRED"/> 
  24.       <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/> 
  25.       <tx:method name="*" propagation="SUPPORTS" read-only="true"/> 
  26.     tx:attributes> 
  27. tx:advice> 
  28. beans>  

注意,TopLink要求你必须在一个活跃的 工作单元(UnitOfWork) 中修改一个持久化对象(你通常不能修改由普通的TopLink的 Session 查询返回的对象,因为这些对象通常是一些从二级缓存中读出的只读对象)。与Hibernate相比,在TopLink中并没有一种类似脱离事务刷出(non-transactional flush)的概念。基于这种原因,TopLink需要被建立在特定的环境中,尤其是它需要为JTA同步做明确的创建,由此来 自行检测一个JTA事务以及暴露一个相应的活跃的 Session 和 UnitOfWork。这一点对于本地事务不是必要的,由于它已经被 Spring的 TopLinkTransactionManager 处理,但是对于 需要参与到JTA事务中的情况,是必须的(无论是由Spring的 JtaTransactionManager、EJB CMT或者普通的JTA所驱动的事务)。

在你的基于TopLink的DAO代码中,你可以使用 Session.getActiveUnitOfWork() 方法来访问当前的 UnitOfWork 并通过它来执行写操作。这将只在一个活跃的事务中有效(在一个收到Spring管理的事务或者JTA事务中)。对于特殊的需求,你同样可以获取单独的 UnitOfWork 实例,它将不参与到当前的事务中去,不过这种情况非常少。

TopLinkTransactionManager 能够将一个TopLink事务暴露给 访问相同的JDBC DataSource 的JDBC访问代码。 前提条件是,TopLink在底层是以JDBC方式工作的并且能够暴露底层的JDBC Connection。这种情况下,用于暴露事务的 DataSource 必须被明确指定, 它是无法被自动检测到的。

【编辑推荐】

  1. 当Spring遇到Hibernate的时候
  2. 将Flex与Spring集成框架
  3. 如何集成Struts和Spring
  4. Spring2.0升级Spring2.0.7的变化
  5. Spring 2.0新功能
责任编辑:佚名 来源: JavaEye
相关推荐

2010-04-22 10:22:31

Oracle开放Top

2024-02-23 10:33:34

SpringBean容器

2009-06-17 17:04:37

BeanFactorySpring

2021-03-08 00:11:02

Spring注解开发

2009-06-26 14:04:15

Quartz配置

2021-04-23 07:33:10

SpringSecurity单元

2010-03-05 13:28:34

SpringObject XML

2023-12-08 07:52:51

Spring项目开发

2019-11-29 16:21:22

Spring框架集成

2023-07-03 07:39:43

Spring框架设计模式

2011-03-18 09:27:00

Spring

2009-06-29 17:17:57

Spring

2022-06-28 08:02:44

SPISpringJava

2017-12-22 09:59:43

2012-07-02 15:26:19

Spring架构框架

2011-03-25 10:00:23

Spring3.0事务的配置

2022-07-21 09:31:58

Actuator密码框架

2023-07-12 08:23:50

父容器子容器Spring

2022-08-27 14:14:06

Spring事务开发

2022-11-26 00:00:02

点赞
收藏

51CTO技术栈公众号