Spring对HibernateSession的管理之封装SessionFactory
我刚刚在上一篇博文中将Spring对HibernateSession的管理做了一些皮毛的分析,主要围绕着Spring是怎样平衡Session的关闭时间。即在是否需要延时Session有效期以保证页面的调用。
那么现在我们来看看Spring是怎样管理Session的生产者:SessionFactory的。
首先,我们在学习Spring的时候一般都会涉及到将SSH框架整合起来使用了,还记得我们怎样配置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:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 7 8 …… 9 <bean id="dataSource" 10 class="org.apache.commons.dbcp.BasicDataSource"> 11 <property name="driverClassName" 12 value="oracle.jdbc.driver.OracleDriver"> 13 </property> 14 <property name="url" 15 value="jdbc:oracle:thin:@localhost:1521:orcl"> 16 </property> 17 <property name="username" value="……"></property> 18 <property name="password" value="……"></property> 19 </bean> 20 <bean id="sessionFactory" 21 class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 22 <property name="dataSource"> 23 <ref bean="dataSource"></ref> 24 </property> 25 <property name="hibernateProperties"> 26 <props> 27 <prop key="hibernate.dialect"> 28 org.hibernate.dialect.Oracle10gDialect 29 </prop> 30 <prop key="hibernate.hbm2ddl.auto">update</prop> 31 </props> 32 </property> 33 <property name="mappingResources"> 34 <list><value>orm/Users.hbm.xml</value></list> 35 </property> 36 </bean> 37 38 <bean id="userDao" class="……"> 39 <property name="sessionFactory" ref="sessionFactory"/> 40 </bean> 41 <bean id="userServer" class="……"> 42 <property name="userdao" ref="userDao"/> 43 </bean> 44 <bean id="userAction" class="……"> 45 <property name="userserver" ref="userServer"/> 46 </bean> 47 …… 48 </beans>
在不涉及到声明性事务和自动装配时我们一般按上述配置(上述配置为MyEclipse环境下的自动生成)。
不知道大家有没有注意到:我们的Dao需要的SessionFactory是org.hibernate.SessionFactory(我们的Dao为了代码重用,继承自HibernateDaoSupport),而上面的装配却没有给一个SessionFactory或其子类。
org.springframework.orm.hibernate3.support.HibernateDaoSupport源代码(省略了注释):
1 package org.springframework.orm.hibernate3.support; 2 3 import org.hibernate.HibernateException; 4 import org.hibernate.Session; 5 import org.hibernate.SessionFactory; 6 7 import org.springframework.dao.DataAccessException; 8 import org.springframework.dao.DataAccessResourceFailureException; 9 import org.springframework.dao.support.DaoSupport; 10 import org.springframework.orm.hibernate3.HibernateTemplate; 11 import org.springframework.orm.hibernate3.SessionFactoryUtils; 12 public abstract class HibernateDaoSupport extends DaoSupport { 13 14 private HibernateTemplate hibernateTemplate; 15 16 public final void setSessionFactory(SessionFactory sessionFactory) { 17 if (this.hibernateTemplate == null || sessionFactory != this.hibernateTemplate.getSessionFactory()) { 18 this.hibernateTemplate = createHibernateTemplate(sessionFactory); 19 } 20 } 21 22 protected HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) { 23 return new HibernateTemplate(sessionFactory); 24 } 25 26 public final SessionFactory getSessionFactory() { 27 return (this.hibernateTemplate != null ? this.hibernateTemplate.getSessionFactory() : null); 28 } 29 30 public final void setHibernateTemplate(HibernateTemplate hibernateTemplate) { 31 this.hibernateTemplate = hibernateTemplate; 32 } 33 34 public final HibernateTemplate getHibernateTemplate() { 35 return this.hibernateTemplate; 36 } 37 38 @Override 39 protected final void checkDaoConfig() { 40 if (this.hibernateTemplate == null) { 41 throw new IllegalArgumentException("'sessionFactory' or 'hibernateTemplate' is required"); 42 } 43 } 44 45 46 protected final Session getSession() 47 throws DataAccessResourceFailureException, IllegalStateException { 48 49 return getSession(this.hibernateTemplate.isAllowCreate()); 50 } 51 52 53 protected final Session getSession(boolean allowCreate) 54 throws DataAccessResourceFailureException, IllegalStateException { 55 56 return (!allowCreate ? 57 SessionFactoryUtils.getSession(getSessionFactory(), false) : 58 SessionFactoryUtils.getSession( 59 getSessionFactory(), 60 this.hibernateTemplate.getEntityInterceptor(), 61 this.hibernateTemplate.getJdbcExceptionTranslator())); 62 } 63 64 protected final DataAccessException convertHibernateAccessException(HibernateException ex) { 65 return this.hibernateTemplate.convertHibernateAccessException(ex); 66 } 67 68 protected final void releaseSession(Session session) { 69 SessionFactoryUtils.releaseSession(session, getSessionFactory()); 70 } 71 72 }
从源代码可以看到,属性所需类型确实为org.hibernate.SessionFactory(上述代码16行处可看出)。
我是偶然发现这一点的,在后来,我顺着LocalSessionFactoryBean的父类或接口向上追溯:
LocalSessionFactoryBean extends AbstractSessionFactoryBean implements BeanClassLoaderAware
AbstractSessionFactoryBean extends HibernateExceptionTranslator
implements FactoryBean<SessionFactory>, InitializingBean, DisposableBean
在经过分析后,我把重点放在了FactoryBean<SessionFactory>这个接口上,它实际上是一个泛型接口,原型如下:
org.springframework.beans.factory.FactoryBean源代码(省略了注释):
1 package org.springframework.beans.factory; 2 3 public interface FactoryBean<T> { 4 5 T getObject() throws Exception; 6 7 Class<?> getObjectType(); 8 9 boolean isSingleton(); 10 11 }
看到这里,我一度认为自己掌握了这种方式,于是我开始着手进行实验(以下是我进行实验的项目,只是一个添加了Spring支持的Java Project。注意添加Spring的aop支持):
1 import org.springframework.context.ApplicationContext; 2 import org.springframework.context.support.ClassPathXmlApplicationContext; 3 4 public class Test { 5 // main方法,从Spring容器中拿到对象 6 public static void main(String[] args) { 7 ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); 8 System.out.println(ac.getBean("tt")); 9 } 10 } 11 // 用作实体类,可以看做org.hibernate.SessionFactory 12 class E { 13 14 } 15 // 用做泛型接口,可以看作org.springframework.beans.factory.FactoryBean 16 interface MyFactoryBean<T> { 17 18 } 19 // 用做真正构造得到的类,可以看作org.springframework.orm.hibernate3.LocalSessionFactoryBean 20 class T implements MyFactoryBean<E> { 21 22 } 23 // 用作使用实体作为属性的类,可以看作org.springframework.orm.hibernate3.support.HibernateDaoSupport 24 class M{ 25 private E e; 26 public void setE(E e){ 27 this.e = e; 28 } 29 }
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:p="http://www.springframework.org/schema/p" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 7 8 <!-- 我写得不是很语义化,大家重在理解 --> 9 10 <!-- 简单理解,此处的dd是实现了泛型接口的类 class T implements MyFactoryBean<E> 而实际上,它和E无关 --> 11 <!-- 此处的M需要一个E来作为属性,它的set方法需要一个E类型的参数,但我们给它一个T --> 12 <bean id="dd" class="T"></bean> 13 <bean id="tt" class="M"> 14 <property name="e" ref="dd" /> 15 </bean> 16 </beans>
这样就可以吗?事实证明:NO!
ps:也对,这样都行的话,国足能进世界杯(进过吗?不清楚)。
得到一个异常(大家翻译一下就明了):java.lang.IllegalStateException: Cannot convert value of type [T] to required type [E] for property 'e': no matching editors or conversion strategy found。
看到这个异常,我忽然想到:难道是因为没有类似org.springframework.beans.factory.FactoryBean中的getObject方法?
于是,我连夜进行测试,在接口和实现中添加方法,改进后如下
1 interface MyFactoryBean<T> { 2 T getObject() throws Exception; 3 4 Class<?> getObjectType(); 5 6 boolean isSingleton(); 7 } 8 class T implements MyFactoryBean<E> { 9 10 @Override 11 public E getObject() throws Exception { 12 return new E(); 13 } 14 15 @Override 16 public Class<?> getObjectType() { 17 return E.class; 18 } 19 20 @Override 21 public boolean isSingleton() { 22 return false; 23 } 24 25 }
结果证明:纯属坑爹,这不是换汤不换药吗?
最后,我使用了Spring自带的FactoryBean:
1 import org.springframework.beans.factory.FactoryBean; 2 //…… 3 class T implements FactoryBean<E> { 4 5 @Override 6 public E getObject() throws Exception { 7 return new E(); 8 } 9 10 @Override 11 public Class<?> getObjectType() { 12 return E.class; 13 } 14 15 @Override 16 public boolean isSingleton() { 17 return false; 18 } 19 20 } 21 //……
这才成功了!
由此可见,Spring用了某种转换来搞定这个事情,其中设计到多种设计模式,下次再深入研究。
(最后编辑时间2012-10-11 17:32:29)