Spring对HibernateSession的管理之封装SessionFactory

时间:2021-10-06 13:50:18

  我刚刚在上一篇博文中将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或其子类。

  Spring对HibernateSession的管理之封装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对HibernateSession的管理之封装SessionFactory

  由此可见,Spring用了某种转换来搞定这个事情,其中设计到多种设计模式,下次再深入研究。

 

 

 欢迎您移步我们的交流群,无聊的时候大家一起打发时间:Spring对HibernateSession的管理之封装SessionFactory

 或者通过QQ与我联系:Spring对HibernateSession的管理之封装SessionFactory

 (最后编辑时间2012-10-11 17:32:29)