Spring源代码解析(八):Spring驱动Hibernate的实现

O/R工具出现之后,简化了许多复杂的信息持久化的开发。Spring应用开发者可以通过Spring提供的O/R方案更方便的使用各种持久化工具,比如Hibernate;下面我们就Spring+Hibernate中的Spring实现做一个简单的剖析。 
Spring对Hinberanate的配置是通过LocalSessionFactoryBean来完成的,这是一个工厂Bean的实现,在基类AbstractSessionFactoryBean中: 

Java代码  收藏代码

  1. /** 

  2.  * 这是FactoryBean需要实现的接口方法,直接取得当前的sessionFactory的值 

  3.  */  

  4. public Object getObject() {  

  5.     return this.sessionFactory;  

  6. }  


这个值在afterPropertySet中定义: 

Java代码  收藏代码

  1. public void afterPropertiesSet() throws Exception {  

  2.     //这个buildSessionFactory是通过配置信息得到SessionFactory的地方  

  3.     SessionFactory rawSf = buildSessionFactory();  

  4.     //这里使用了Proxy方法插入对getCurrentSession的拦截,得到和事务相关的session  

  5.     this.sessionFactory = wrapSessionFactoryIfNecessary(rawSf);  

  6. }  


我们先看看SessionFactory是怎样创建的,这个方法很长,包含了创建Hibernate的SessionFactory的详尽步骤: 

Java代码  收藏代码

  1. protected SessionFactory buildSessionFactory() throws Exception {  

  2.     SessionFactory sf = null;  

  3.   

  4.     // Create Configuration instance.  

  5.     Configuration config = newConfiguration();  

  6.   

  7.     //这里配置数据源,事务管理器,LobHander到Holder中,这个Holder是一个ThreadLocal变量,这样这些资源就和线程绑定了  

  8.     if (this.dataSource != null) {  

  9.         // Make given DataSource available for SessionFactory configuration.  

  10.         configTimeDataSourceHolder.set(this.dataSource);  

  11.     }  

  12.   

  13.     if (this.jtaTransactionManager != null) {  

  14.         // Make Spring-provided JTA TransactionManager available.  

  15.         configTimeTransactionManagerHolder.set(this.jtaTransactionManager);  

  16.     }  

  17.   

  18.     if (this.lobHandler != null) {  

  19.         // Make given LobHandler available for SessionFactory configuration.  

  20.         // Do early because because mapping resource might refer to custom types.  

  21.         configTimeLobHandlerHolder.set(this.lobHandler);  

  22.     }  

  23.   

  24.     //这里是使用Hibernate的各个属性的配置,这里使用了Configuration类来抽象这些数据  

  25.     try {  

  26.         // Set connection release mode "on_close" as default.  

  27.         // This was the case for Hibernate 3.0; Hibernate 3.1 changed  

  28.         // it to "auto" (i.e. "after_statement" or "after_transaction").  

  29.         // However, for Spring's resource management (in particular for  

  30.         // HibernateTransactionManager), "on_close" is the better default.  

  31.         config.setProperty(Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.ON_CLOSE.toString());  

  32.   

  33.         if (!isExposeTransactionAwareSessionFactory()) {  

  34.             // Not exposing a SessionFactory proxy with transaction-aware  

  35.             // getCurrentSession() method -> set Hibernate 3.1 CurrentSessionContext  

  36.             // implementation instead, providing the Spring-managed Session that way.  

  37.             // Can be overridden by a custom value for corresponding Hibernate property.  

  38.             config.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS,  

  39.                     "org.springframework.orm.hibernate3.SpringSessionContext");  

  40.         }  

  41.   

  42.         if (this.entityInterceptor != null) {  

  43.             // Set given entity interceptor at SessionFactory level.  

  44.             config.setInterceptor(this.entityInterceptor);  

  45.         }  

  46.   

  47.         if (this.namingStrategy != null) {  

  48.             // Pass given naming strategy to Hibernate Configuration.  

  49.             config.setNamingStrategy(this.namingStrategy);  

  50.         }  

  51.   

  52.         if (this.typeDefinitions != null) {  

  53.             // Register specified Hibernate type definitions.  

  54.             Mappings mappings = config.createMappings();  

  55.             for (int i = 0; i < this.typeDefinitions.length; i++) {  

  56.                 TypeDefinitionBean typeDef = this.typeDefinitions[i];  

  57.                 mappings.addTypeDef(typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters());  

  58.             }  

  59.         }  

  60.   

  61.         if (this.filterDefinitions != null) {  

  62.             // Register specified Hibernate FilterDefinitions.  

  63.             for (int i = 0; i < this.filterDefinitions.length; i++) {  

  64.                 config.addFilterDefinition(this.filterDefinitions[i]);  

  65.             }  

  66.         }  

  67.   

  68.         if (this.configLocations != null) {  

  69.             for (int i = 0; i < this.configLocations.length; i++) {  

  70.                 // Load Hibernate configuration from given location.  

  71.                 config.configure(this.configLocations[i].getURL());  

  72.             }  

  73.         }  

  74.   

  75.         if (this.hibernateProperties != null) {  

  76.             // Add given Hibernate properties to Configuration.  

  77.             config.addProperties(this.hibernateProperties);  

  78.         }  

  79.   

  80.         if (this.dataSource != null) {  

  81.             boolean actuallyTransactionAware =  

  82.                     (this.useTransactionAwareDataSource || this.dataSource instanceof TransactionAwareDataSourceProxy);  

  83.             // Set Spring-provided DataSource as Hibernate ConnectionProvider.  

  84.             config.setProperty(Environment.CONNECTION_PROVIDER,  

  85.                     actuallyTransactionAware ?  

  86.                     TransactionAwareDataSourceConnectionProvider.class.getName() :  

  87.                     LocalDataSourceConnectionProvider.class.getName());  

  88.         }  

  89.   

  90.         if (this.jtaTransactionManager != null) {  

  91.             // Set Spring-provided JTA TransactionManager as Hibernate property.  

  92.             config.setProperty(  

  93.                     Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName());  

  94.         }  

  95.   

  96.         if (this.mappingLocations != null) {  

  97.             // Register given Hibernate mapping definitions, contained in resource files.  

  98.             for (int i = 0; i < this.mappingLocations.length; i++) {  

  99.                 config.addInputStream(this.mappingLocations[i].getInputStream());  

  100.             }  

  101.         }  

  102.   

  103.         if (this.cacheableMappingLocations != null) {  

  104.             // Register given cacheable Hibernate mapping definitions, read from the file system.  

  105.             for (int i = 0; i < this.cacheableMappingLocations.length; i++) {  

  106.                 config.addCacheableFile(this.cacheableMappingLocations[i].getFile());  

  107.             }  

  108.         }  

  109.   

  110.         if (this.mappingJarLocations != null) {  

  111.             // Register given Hibernate mapping definitions, contained in jar files.  

  112.             for (int i = 0; i < this.mappingJarLocations.length; i++) {  

  113.                 Resource resource = this.mappingJarLocations[i];  

  114.                 config.addJar(resource.getFile());  

  115.             }  

  116.         }  

  117.   

  118.         if (this.mappingDirectoryLocations != null) {  

  119.             // Register all Hibernate mapping definitions in the given directories.  

  120.             for (int i = 0; i < this.mappingDirectoryLocations.length; i++) {  

  121.                 File file = this.mappingDirectoryLocations[i].getFile();  

  122.                 if (!file.isDirectory()) {  

  123.                     throw new IllegalArgumentException(  

  124.                             "Mapping directory location [" + this.mappingDirectoryLocations[i] +  

  125.                             "] does not denote a directory");  

  126.                 }  

  127.                 config.addDirectory(file);  

  128.             }  

  129.         }  

  130.   

  131.         if (this.entityCacheStrategies != null) {  

  132.             // Register cache strategies for mapped entities.  

  133.             for (Enumeration classNames = this.entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) {  

  134.                 String className = (String) classNames.nextElement();  

  135.                 String[] strategyAndRegion =  

  136.                         StringUtils.commaDelimitedListToStringArray(this.entityCacheStrategies.getProperty(className));  

  137.                 if (strategyAndRegion.length > 1) {  

  138.                     config.setCacheConcurrencyStrategy(className, strategyAndRegion[0], strategyAndRegion[1]);  

  139.                 }  

  140.                 else if (strategyAndRegion.length > 0) {  

  141.                     config.setCacheConcurrencyStrategy(className, strategyAndRegion[0]);  

  142.                 }  

  143.             }  

  144.         }  

  145.   

  146.         if (this.collectionCacheStrategies != null) {  

  147.             // Register cache strategies for mapped collections.  

  148.             for (Enumeration collRoles = this.collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) {  

  149.                 String collRole = (String) collRoles.nextElement();  

  150.                 String[] strategyAndRegion =  

  151.                         StringUtils.commaDelimitedListToStringArray(this.collectionCacheStrategies.getProperty(collRole));  

  152.                 if (strategyAndRegion.length > 1) {  

  153.                     config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0], strategyAndRegion[1]);  

  154.                 }  

  155.                 else if (strategyAndRegion.length > 0) {  

  156.                     config.setCollectionCacheConcurrencyStrategy(collRole, strategyAndRegion[0]);  

  157.                 }  

  158.             }  

  159.         }  

  160.   

  161.         if (this.eventListeners != null) {  

  162.             // Register specified Hibernate event listeners.  

  163.             for (Iterator it = this.eventListeners.entrySet().iterator(); it.hasNext();) {  

  164.                 Map.Entry entry = (Map.Entry) it.next();  

  165.                 Assert.isTrue(entry.getKey() instanceof String, "Event listener key needs to be of type String");  

  166.                 String listenerType = (String) entry.getKey();  

  167.                 Object listenerObject = entry.getValue();  

  168.                 if (listenerObject instanceof Collection) {  

  169.                     Collection listeners = (Collection) listenerObject;  

  170.                     EventListeners listenerRegistry = config.getEventListeners();  

  171.                     Object[] listenerArray =  

  172.                             (Object[]) Array.newInstance(listenerRegistry.getListenerClassFor(listenerType), listeners.size());  

  173.                     listenerArray = listeners.toArray(listenerArray);  

  174.                     config.setListeners(listenerType, listenerArray);  

  175.                 }  

  176.                 else {  

  177.                     config.setListener(listenerType, listenerObject);  

  178.                 }  

  179.             }  

  180.         }  

  181.   

  182.         // Perform custom post-processing in subclasses.  

  183.         postProcessConfiguration(config);  

  184.   

  185.         // 这里是根据Configuration配置创建SessionFactory的地方  

  186.         logger.info("Building new Hibernate SessionFactory");  

  187.         this.configuration = config;  

  188.         sf = newSessionFactory(config);  

  189.     }  

  190.     //最后把和线程绑定的资源清空  

  191.     finally {  

  192.         if (this.dataSource != null) {  

  193.             // Reset DataSource holder.  

  194.             configTimeDataSourceHolder.set(null);  

  195.         }  

  196.   

  197.         if (this.jtaTransactionManager != null) {  

  198.             // Reset TransactionManager holder.  

  199.             configTimeTransactionManagerHolder.set(null);  

  200.         }  

  201.   

  202.         if (this.lobHandler != null) {  

  203.             // Reset LobHandler holder.  

  204.             configTimeLobHandlerHolder.set(null);  

  205.         }  

  206.     }  

  207.   

  208.     // Execute schema update if requested.  

  209.     if (this.schemaUpdate) {  

  210.         updateDatabaseSchema();  

  211.     }  

  212.   

  213.     return sf;  

  214. }  


而直接调用org.hibernate.cfg.Configuration来得到需要的SessionFactory: 

Java代码  收藏代码

  1. protected SessionFactory newSessionFactory(Configuration config) throws HibernateException {  

  2.     return config.buildSessionFactory();  

  3. }  


所以我们这里看到LocalSessionFactory大致起到的一个读取资源配置然后生成SessionFactory的作用;当然这里在得到 SessionFactory之后,还需要对session的事务管理作一些处理 - 使用了一个Proxy模式对getCurrentSession方法进行了拦截; 

Java代码  收藏代码

  1. //这里先根据当前的SessionFactory的类型得到Proxy,然后插入Spring定义好的getCurrentSession拦截器  

  2.     protected SessionFactory getTransactionAwareSessionFactoryProxy(SessionFactory target) {  

  3.         Class sfInterface = SessionFactory.class;  

  4.         if (target instanceof SessionFactoryImplementor) {  

  5.             sfInterface = SessionFactoryImplementor.class;  

  6.         }  

  7.         return (SessionFactory) Proxy.newProxyInstance(sfInterface.getClassLoader(),  

  8.                 new Class[] {sfInterface}, new TransactionAwareInvocationHandler(target));  

  9.     }  


拦截器的实现如下: 

Java代码  收藏代码

  1. private static class TransactionAwareInvocationHandler implements InvocationHandler {  

  2.   

  3.     private final SessionFactory target;  

  4.   

  5.     public TransactionAwareInvocationHandler(SessionFactory target) {  

  6.         this.target = target;  

  7.     }  

  8.   

  9.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  

  10.         // Invocation on SessionFactory/SessionFactoryImplementor interface coming in...  

  11.         // 这里对getCurrentSession方法进行拦截,得到一个和当前事务绑定的session交给用户  

  12.         if (method.getName().equals("getCurrentSession")) {  

  13.             // Handle getCurrentSession method: return transactional Session, if any.  

  14.             try {  

  15.                 return SessionFactoryUtils.doGetSession((SessionFactory) proxy, false);  

  16.             }  

  17.             catch (IllegalStateException ex) {  

  18.                 throw new HibernateException(ex.getMessage());  

  19.             }  

  20.         }  

  21.         else if (method.getName().equals("equals")) {  

  22.             // Only consider equal when proxies are identical.  

  23.             return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);  

  24.         }  

  25.         else if (method.getName().equals("hashCode")) {  

  26.             // Use hashCode of SessionFactory proxy.  

  27.             return new Integer(hashCode());  

  28.         }  

  29.   

  30.         // 这里是需要运行的SessionFactory的目标方法  

  31.         try {  

  32.             return method.invoke(this.target, args);  

  33.         }  

  34.         catch (InvocationTargetException ex) {  

  35.             throw ex.getTargetException();  

  36.         }  

  37.     }  

  38. }  


我们看看getCurrentSession的实现,在SessionFactoryUtils中: 

Java代码  收藏代码

  1. private static Session doGetSession(  

  2.             SessionFactory sessionFactory, Interceptor entityInterceptor,  

  3.             SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)  

  4.             throws HibernateException, IllegalStateException {  

  5.   

  6.         Assert.notNull(sessionFactory, "No SessionFactory specified");  

  7.   

  8.         //这个TransactionSynchronizationManager的Resource是一个ThreadLocal变量,sessionFactory是一个单例,但ThreadLocal是和线程绑定的  

  9.         //这样就实现了Hiberante中常用的通过ThreadLocal的session管理机制  

  10.         SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);  

  11.         if (sessionHolder != null && !sessionHolder.isEmpty()) {  

  12.             // pre-bound Hibernate Session  

  13.             Session session = null;  

  14.             if (TransactionSynchronizationManager.isSynchronizationActive() &&  

  15.                     sessionHolder.doesNotHoldNonDefaultSession()) {  

  16.                 // Spring transaction management is active ->  

  17.                 // register pre-bound Session with it for transactional flushing.  

  18.                 session = sessionHolder.getValidatedSession();  

  19.                 if (session != null && !sessionHolder.isSynchronizedWithTransaction()) {  

  20.                     logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");  

  21.                     TransactionSynchronizationManager.registerSynchronization(  

  22.                             new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));  

  23.                     sessionHolder.setSynchronizedWithTransaction(true);  

  24.                     // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session  

  25.                     // with FlushMode.NEVER, which needs to allow flushing within the transaction.  

  26.                     FlushMode flushMode = session.getFlushMode();  

  27.                     if (flushMode.lessThan(FlushMode.COMMIT) &&  

  28.                             !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {  

  29.                         session.setFlushMode(FlushMode.AUTO);  

  30.                         sessionHolder.setPreviousFlushMode(flushMode);  

  31.                     }  

  32.                 }  

  33.             }  

  34.             else {  

  35.                 // No Spring transaction management active -> try JTA transaction synchronization.  

  36.                 session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);  

  37.             }  

  38.             if (session != null) {  

  39.                 return session;  

  40.             }  

  41.         }  

  42.         //这里直接打开一个Session  

  43.         logger.debug("Opening Hibernate Session");  

  44.         Session session = (entityInterceptor != null ?  

  45.                 sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());  

  46.   

  47.         // Use same Session for further Hibernate actions within the transaction.  

  48.         // Thread object will get removed by synchronization at transaction completion.  

  49.         // 把新打开的Session放到SessionHolder,然后放到ThreadLocal里面去和线程绑定起来,这个ThreadLocal是在 TransactionSynchronizationManager中配置好的,可以根据sessionFactory来索取  

  50.         // 同时根据事务处理的状态来配置session的属性,比如把FlushMode设置为Never,同时把session和事务处理关联起来  

  51.         if (TransactionSynchronizationManager.isSynchronizationActive()) {  

  52.             // We're within a Spring-managed transaction, possibly from JtaTransactionManager.  

  53.             logger.debug("Registering Spring transaction synchronization for new Hibernate Session");  

  54.             SessionHolder holderToUse = sessionHolder;  

  55.             if (holderToUse == null) {  

  56.                 holderToUse = new SessionHolder(session);  

  57.             }  

  58.             else {  

  59.                 holderToUse.addSession(session);  

  60.             }  

  61.             if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {  

  62.                 session.setFlushMode(FlushMode.NEVER);  

  63.             }  

  64.             TransactionSynchronizationManager.registerSynchronization(  

  65.                     new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));  

  66.             holderToUse.setSynchronizedWithTransaction(true);  

  67.             if (holderToUse != sessionHolder) {  

  68.                 TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);  

  69.             }  

  70.         }  

  71.         else {  

  72.             // No Spring transaction management active -> try JTA transaction synchronization.  

  73.             registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);  

  74.         }  

  75.   

  76.         // Check whether we are allowed to return the Session.  

  77.         if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {  

  78.             closeSession(session);  

  79.             throw new IllegalStateException("No Hibernate Session bound to thread, " +  

  80.                 "and configuration does not allow creation of non-transactional one here");  

  81.         }  

  82.   

  83.         return session;  

  84.     }  


这里就是在Spring中为使用Hiberante的SessionFactory以及Session做的准备工作,在这个基础上,用户可以通过使用 HibernateTemplate来使用Hibernate的O/R功能,和以前看到的一样这是一个execute的回调: 

Java代码  收藏代码

  1. public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException {  

  2.     Assert.notNull(action, "Callback object must not be null");  

  3.     //这里得到配置好的Hibernate的Session  

  4.     Session session = getSession();  

  5.     boolean existingTransaction = SessionFactoryUtils.isSessionTransactional(session, getSessionFactory());  

  6.     if (existingTransaction) {  

  7.         logger.debug("Found thread-bound Session for HibernateTemplate");  

  8.     }  

  9.   

  10.     FlushMode previousFlushMode = null;  

  11.     try {  

  12.         previousFlushMode = applyFlushMode(session, existingTransaction);  

  13.         enableFilters(session);  

  14.         Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session));  

  15.         //这里是回调的入口  

  16.         Object result = action.doInHibernate(sessionToExpose);  

  17.         flushIfNecessary(session, existingTransaction);  

  18.         return result;  

  19.     }  

  20.     catch (HibernateException ex) {  

  21.         throw convertHibernateAccessException(ex);  

  22.     }  

  23.     catch (SQLException ex) {  

  24.         throw convertJdbcAccessException(ex);  

  25.     }  

  26.     catch (RuntimeException ex) {  

  27.         // Callback code threw application exception...  

  28.         throw ex;  

  29.     }  

  30.     finally {  

  31.         //如果这个调用的方法在一个事务当中,  

  32.         if (existingTransaction) {  

  33.             logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");  

  34.             disableFilters(session);  

  35.             if (previousFlushMode != null) {  

  36.                 session.setFlushMode(previousFlushMode);  

  37.             }  

  38.         } //否则把Session关闭  

  39.         else {  

  40.             // Never use deferred close for an explicitly new Session.  

  41.             if (isAlwaysUseNewSession()) {  

  42.                 SessionFactoryUtils.closeSession(session);  

  43.             }  

  44.             else {  

  45.                 SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());  

  46.             }  

  47.         }  

  48.     }  

  49. }  


我们看看怎样得到对应的Session的,仍然使用了SessionFactoryUtils的方法doGetSession: 

Java代码  收藏代码

  1. protected Session getSession() {  

  2.     if (isAlwaysUseNewSession()) {  

  3.         return SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor());  

  4.     }  

  5.     else if (!isAllowCreate()) {  

  6.         return SessionFactoryUtils.getSession(getSessionFactory(), false);  

  7.     }  

  8.     else {  

  9.         return SessionFactoryUtils.getSession(  

  10.                 getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());  

  11.     }  

  12. }  


这样我们就可以和其他的Template那样使用Hibernate的基本功能了,使用的时候Spring已经为我们对Session的获取和关闭,事务处理的绑定做好了封装 - 从这个角度看也大大方便了用户的使用。

猜你喜欢

转载自yovi.iteye.com/blog/2270439