程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Spring源代碼解析(八):Spring驅動Hibernate的實現

Spring源代碼解析(八):Spring驅動Hibernate的實現

編輯:關於JAVA

O/R工具出現之後,簡化了許多復雜的信息持久化的開發。Spring應用開發者可以通過 Spring提供的O/R方案更方便的使用各種持久化工具,比如Hibernate;下面我們就 Spring+Hibernate中的Spring實現做一個簡單的剖析。

Spring對Hinberanate的配置是通過LocalSessionFactoryBean來完成的,這是一個工 廠Bean的實現,在基類AbstractSessionFactoryBean中:

Java代碼

/**
   * 這是FactoryBean需要實現的接口方法,直接取得當前的sessionFactory的 值
   */
   public Object getObject() {
     return this.sessionFactory;
   }

這個值在afterPropertySet中定義:

Java代碼

public void afterPropertiesSet() throws Exception {
     //這個buildSessionFactory是通過配置信息得到SessionFactory的地方
     SessionFactory rawSf = buildSessionFactory();
     //這裡使用了Proxy方法插入對getCurrentSession的攔截,得到和事務相關 的session
     this.sessionFactory = wrapSessionFactoryIfNecessary(rawSf);
   }

我們先看看SessionFactory是怎樣創建的,這個方法很長,包含了創建Hibernate的 SessionFactory的詳盡步驟:

Java代碼

protected SessionFactory buildSessionFactory() throws Exception {
     SessionFactory sf = null;
     // Create Configuration instance.
     Configuration config = newConfiguration();
     //這裡配置數據源,事務管理器,LobHander到Holder中,這個Holder是一個 ThreadLocal變量,這樣這些資源就和線程綁定了
     if (this.dataSource != null) {
       // Make given DataSource available for SessionFactory configuration.
       configTimeDataSourceHolder.set(this.dataSource);
     }
     if (this.jtaTransactionManager != null) {
       // Make Spring-provided JTA TransactionManager available.
       configTimeTransactionManagerHolder.set (this.jtaTransactionManager);
     }
     if (this.lobHandler != null) {
       // Make given LobHandler available for SessionFactory configuration.
       // Do early because because mapping resource might refer to custom types.
       configTimeLobHandlerHolder.set(this.lobHandler);
     }
     //這裡是使用Hibernate的各個屬性的配置,這裡使用了Configuration類來 抽象這些數據
     try {
       // Set connection release mode "on_close" as default.
       // This was the case for Hibernate 3.0; Hibernate 3.1 changed
       // it to "auto" (i.e. "after_statement" or "after_transaction").
       // However, for Spring's resource management (in particular for
       // HibernateTransactionManager), "on_close" is the better default.
       config.setProperty(Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.ON_CLOSE.toString());
       if (!isExposeTransactionAwareSessionFactory()) {
         // Not exposing a SessionFactory proxy with transaction- aware
         // getCurrentSession() method -> set Hibernate 3.1 CurrentSessionContext
         // implementation instead, providing the Spring-managed Session that way.
         // Can be overridden by a custom value for corresponding Hibernate property.
         config.setProperty (Environment.CURRENT_SESSION_CONTEXT_CLASS,
             "org.springframework.orm.hibernate3.SpringSessionContext");
       }
       if (this.entityInterceptor != null) {
         // Set given entity interceptor at SessionFactory level.
         config.setInterceptor(this.entityInterceptor);
       }
       if (this.namingStrategy != null) {
         // Pass given naming strategy to Hibernate Configuration.
         config.setNamingStrategy(this.namingStrategy);
       }
       if (this.typeDefinitions != null) {
         // Register specified Hibernate type definitions.
         Mappings mappings = config.createMappings();
         for (int i = 0; i < this.typeDefinitions.length; i++) {
           TypeDefinitionBean typeDef = this.typeDefinitions [i];
           mappings.addTypeDef(typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters());
         }
       }
       if (this.filterDefinitions != null) {
         // Register specified Hibernate FilterDefinitions.
         for (int i = 0; i < this.filterDefinitions.length; i++) {
           config.addFilterDefinition(this.filterDefinitions [i]);
         }
       }
       if (this.configLocations != null) {
         for (int i = 0; i < this.configLocations.length; i++) {
           // Load Hibernate configuration from given location.
           config.configure(this.configLocations[i].getURL());
         }
       }
       if (this.hibernateProperties != null) {
         // Add given Hibernate properties to Configuration.
         config.addProperties(this.hibernateProperties);
       }
       if (this.dataSource != null) {
         boolean actuallyTransactionAware =
             (this.useTransactionAwareDataSource || this.dataSource instanceof TransactionAwareDataSourceProxy);
         // Set Spring-provided DataSource as Hibernate ConnectionProvider.
         config.setProperty(Environment.CONNECTION_PROVIDER,
             actuallyTransactionAware ?
             TransactionAwareDataSourceConnectionProvider.class.getName() :
             LocalDataSourceConnectionProvider.class.getName ());
       }
       if (this.jtaTransactionManager != null) {
         // Set Spring-provided JTA TransactionManager as Hibernate property.
         config.setProperty(
             Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName());
       }
       if (this.mappingLocations != null) {
         // Register given Hibernate mapping definitions, contained in resource files.
         for (int i = 0; i < this.mappingLocations.length; i++) {
           config.addInputStream(this.mappingLocations [i].getInputStream());
         }
       }
       if (this.cacheableMappingLocations != null) {
         // Register given cacheable Hibernate mapping definitions, read from the file system.
         for (int i = 0; i < this.cacheableMappingLocations.length; i++) {
           config.addCacheableFile(this.cacheableMappingLocations [i].getFile());
         }
       }
       if (this.mappingJarLocations != null) {
         // Register given Hibernate mapping definitions, contained in jar files.
         for (int i = 0; i < this.mappingJarLocations.length; i++) {
           Resource resource = this.mappingJarLocations[i];
           config.addJar(resource.getFile());
         }
       }
       if (this.mappingDirectoryLocations != null) {
         // Register all Hibernate mapping definitions in the given directories.
         for (int i = 0; i < this.mappingDirectoryLocations.length; i++) {
           File file = this.mappingDirectoryLocations[i].getFile ();
           if (!file.isDirectory()) {
             throw new IllegalArgumentException(
                 "Mapping directory location [" + this.mappingDirectoryLocations[i] +
                 "] does not denote a directory");
           }
           config.addDirectory(file);
         }
       }
       if (this.entityCacheStrategies != null) {
         // Register cache strategies for mapped entities.
         for (Enumeration classNames = this.entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) {
           String className = (String) classNames.nextElement ();
           String[] strategyAndRegion =
               StringUtils.commaDelimitedListToStringArray (this.entityCacheStrategies.getProperty(className));
           if (strategyAndRegion.length > 1) {
             config.setCacheConcurrencyStrategy(className, strategyAndRegion[0], strategyAndRegion[1]);
           }
           else if (strategyAndRegion.length > 0) {
             config.setCacheConcurrencyStrategy(className, strategyAndRegion[0]);
           }
         }
       }
       if (this.collectionCacheStrategies != null) {
         // Register cache strategies for mapped collections.
         for (Enumeration collRoles = this.collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) {
           String collRole = (String) collRoles.nextElement();
           String[] strategyAndRegion =
               StringUtils.commaDelimitedListToStringArray (this.collectionCacheStrategies.getProperty(collRole));
           if (strategyAndRegion.length > 1) {
             config.setCollectionCacheConcurrencyStrategy (collRole, strategyAndRegion[0], strategyAndRegion[1]);
           }
           else if (strategyAndRegion.length > 0) {
             config.setCollectionCacheConcurrencyStrategy (collRole, strategyAndRegion[0]);
           }
         }
       }
       if (this.eventListeners != null) {
         // Register specified Hibernate event listeners.
         for (Iterator it = this.eventListeners.entrySet().iterator (); it.hasNext();) {
           Map.Entry entry = (Map.Entry) it.next();
           Assert.isTrue(entry.getKey() instanceof String, "Event listener key needs to be of type String");
           String listenerType = (String) entry.getKey();
           Object listenerObject = entry.getValue();
           if (listenerObject instanceof Collection) {
             Collection listeners = (Collection) listenerObject;
             EventListeners listenerRegistry = config.getEventListeners();
             Object[] listenerArray =
                 (Object[]) Array.newInstance (listenerRegistry.getListenerClassFor(listenerType), listeners.size());
             listenerArray = listeners.toArray (listenerArray);
             config.setListeners(listenerType, listenerArray);
           }
           else {
             config.setListener(listenerType, listenerObject);
           }
         }
       }
       // Perform custom post-processing in subclasses.
       postProcessConfiguration(config);
       // 這裡是根據Configuration配置創建SessionFactory的地方
       logger.info("Building new Hibernate SessionFactory");
       this.configuration = config;
       sf = newSessionFactory(config);
     }
     //最後把和線程綁定的資源清空
     finally {
       if (this.dataSource != null) {
         // Reset DataSource holder.
         configTimeDataSourceHolder.set(null);
       }
       if (this.jtaTransactionManager != null) {
         // Reset TransactionManager holder.
         configTimeTransactionManagerHolder.set(null);
       }
       if (this.lobHandler != null) {
         // Reset LobHandler holder.
         configTimeLobHandlerHolder.set(null);
       }
     }
     // Execute schema update if requested.
     if (this.schemaUpdate) {
       updateDatabaseSchema();
     }
     return sf;
   }

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

Java代碼

protected SessionFactory newSessionFactory(Configuration config) throws HibernateException {
     return config.buildSessionFactory();
   }

所以我們這裡看到LocalSessionFactory大致起到的一個讀取資源配置然後生成 SessionFactory的作用;當然這裡在得到 SessionFactory之後,還需要對session的事務 管理作一些處理 - 使用了一個Proxy模式對getCurrentSession方法進行了攔截;

Java代碼

//這裡先根據當前的SessionFactory的類型得到Proxy,然後插入Spring定義好 的getCurrentSession攔截器
   protected SessionFactory getTransactionAwareSessionFactoryProxy (SessionFactory target) {
     Class sfInterface = SessionFactory.class;
     if (target instanceof SessionFactoryImplementor) {
       sfInterface = SessionFactoryImplementor.class;
     }
     return (SessionFactory) Proxy.newProxyInstance (sfInterface.getClassLoader(),
         new Class[] {sfInterface}, new TransactionAwareInvocationHandler(target));
   }

攔截器的實現如下:

Java代碼

private static class TransactionAwareInvocationHandler implements InvocationHandler {
     private final SessionFactory target;
     public TransactionAwareInvocationHandler(SessionFactory target) {
       this.target = target;
     }
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       // Invocation on SessionFactory/SessionFactoryImplementor interface coming in...
       // 這裡對getCurrentSession方法進行攔截,得到一個和當前事務綁定 的session交給用戶
       if (method.getName().equals("getCurrentSession")) {
         // Handle getCurrentSession method: return transactional Session, if any.
         try {
           return SessionFactoryUtils.doGetSession ((SessionFactory) proxy, false);
         }
         catch (IllegalStateException ex) {
           throw new HibernateException(ex.getMessage());
         }
       }
       else if (method.getName().equals("equals")) {
         // Only consider equal when proxies are identical.
         return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
       }
       else if (method.getName().equals("hashCode")) {
         // Use hashCode of SessionFactory proxy.
         return new Integer(hashCode());
       }
       // 這裡是需要運行的SessionFactory的目標方法
       try {
         return method.invoke(this.target, args);
       }
       catch (InvocationTargetException ex) {
         throw ex.getTargetException();
       }
     }
   }

我們看看getCurrentSession的實現,在SessionFactoryUtils中:

Java代碼

private static Session doGetSession(
       SessionFactory sessionFactory, Interceptor entityInterceptor,
       SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
       throws HibernateException, IllegalStateException {
     Assert.notNull(sessionFactory, "No SessionFactory specified");
     //這個TransactionSynchronizationManager的Resource是一個ThreadLocal 變量,sessionFactory是一個單例,但ThreadLocal是和線程綁定的
     //這樣就實現了Hiberante中常用的通過ThreadLocal的session管理機制
     SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
     if (sessionHolder != null && !sessionHolder.isEmpty()) {
       // pre-bound Hibernate Session
       Session session = null;
       if (TransactionSynchronizationManager.isSynchronizationActive() &&
           sessionHolder.doesNotHoldNonDefaultSession()) {
         // Spring transaction management is active ->
         // register pre-bound Session with it for transactional flushing.
         session = sessionHolder.getValidatedSession();
         if (session != null && ! sessionHolder.isSynchronizedWithTransaction()) {
           logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");
           TransactionSynchronizationManager.registerSynchronization(
               new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));
           sessionHolder.setSynchronizedWithTransaction(true);
           // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
           // with FlushMode.NEVER, which needs to allow flushing within the transaction.
           FlushMode flushMode = session.getFlushMode();
           if (flushMode.lessThan(FlushMode.COMMIT) &&
               ! TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
             session.setFlushMode(FlushMode.AUTO);
             sessionHolder.setPreviousFlushMode(flushMode);
           }
         }
       }
       else {
         // No Spring transaction management active -> try JTA transaction synchronization.
         session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);
       }
       if (session != null) {
         return session;
       }
     }
     //這裡直接打開一個Session
     logger.debug("Opening Hibernate Session");
     Session session = (entityInterceptor != null ?
         sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());
     // Use same Session for further Hibernate actions within the transaction.
     // Thread object will get removed by synchronization at transaction completion.
     // 把新打開的Session放到SessionHolder,然後放到ThreadLocal裡面去和線 程綁定起來,這個ThreadLocal是在 TransactionSynchronizationManager中配置好的, 可以根據sessionFactory來索取
     // 同時根據事務處理的狀態來配置session的屬性,比如把FlushMode設置為 Never,同時把session和事務處理關聯起來
     if (TransactionSynchronizationManager.isSynchronizationActive()) {
       // We're within a Spring-managed transaction, possibly from JtaTransactionManager.
       logger.debug("Registering Spring transaction synchronization for new Hibernate Session");
       SessionHolder holderToUse = sessionHolder;
       if (holderToUse == null) {
         holderToUse = new SessionHolder(session);
       }
       else {
         holderToUse.addSession(session);
       }
       if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
         session.setFlushMode(FlushMode.NEVER);
       }
       TransactionSynchronizationManager.registerSynchronization(
           new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));
       holderToUse.setSynchronizedWithTransaction(true);
       if (holderToUse != sessionHolder) {
         TransactionSynchronizationManager.bindResource (sessionFactory, holderToUse);
       }
     }
     else {
       // No Spring transaction management active -> try JTA transaction synchronization.
       registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);
     }
     // Check whether we are allowed to return the Session.
     if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {
       closeSession(session);
       throw new IllegalStateException("No Hibernate Session bound to thread, " +
         "and configuration does not allow creation of non-transactional one here");
     }
     return session;
   }

這裡就是在Spring中為使用Hiberante的SessionFactory以及Session做的准備工作, 在這個基礎上,用戶可以通過使用 HibernateTemplate來使用Hibernate的O/R功能,和以 前看到的一樣這是一個execute的回調:

Java代碼

public Object execute(HibernateCallback action, boolean exposeNativeSession) throws DataAccessException {
     Assert.notNull(action, "Callback object must not be null");
     //這裡得到配置好的Hibernate的Session
     Session session = getSession();
     boolean existingTransaction = SessionFactoryUtils.isSessionTransactional(session, getSessionFactory());
     if (existingTransaction) {
       logger.debug("Found thread-bound Session for HibernateTemplate");
     }
     FlushMode previousFlushMode = null;
     try {
       previousFlushMode = applyFlushMode(session, existingTransaction);
       enableFilters(session);
       Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session));
       //這裡是回調的入口
       Object result = action.doInHibernate(sessionToExpose);
       flushIfNecessary(session, existingTransaction);
       return result;
     }
     catch (HibernateException ex) {
       throw convertHibernateAccessException(ex);
     }
     catch (SQLException ex) {
       throw convertJdbcAccessException(ex);
     }
     catch (RuntimeException ex) {
       // Callback code threw application exception...
       throw ex;
     }
     finally {
       //如果這個調用的方法在一個事務當中,
       if (existingTransaction) {
         logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");
         disableFilters(session);
         if (previousFlushMode != null) {
           session.setFlushMode(previousFlushMode);
         }
       } //否則把Session關閉
       else {
         // Never use deferred close for an explicitly new Session.
         if (isAlwaysUseNewSession()) {
           SessionFactoryUtils.closeSession(session);
         }
         else {
           SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
         }
       }
     }
   }

我們看看怎樣得到對應的Session的,仍然使用了SessionFactoryUtils的方法 doGetSession:

Java代碼

protected Session getSession() {
     if (isAlwaysUseNewSession()) {
       return SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor());
     }
     else if (!isAllowCreate()) {
       return SessionFactoryUtils.getSession(getSessionFactory(), false);
     }
     else {
       return SessionFactoryUtils.getSession(
           getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
     }
   }

這樣我們就可以和其他的Template那樣使用Hibernate的基本功能了,使用的時候 Spring已經為我們對Session的獲取和關閉,事務處理的綁定做好了封裝 - 從這個角度看 也大大方便了用戶的使用。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved