分享

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

 阿K哥 2010-02-25
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.     // Create Configuration instance.   

  4.     Configuration config = newConfiguration();   

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

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

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

  8.         configTimeDataSourceHolder.set(this.dataSource);   

  9.     }   

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

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

  12.         configTimeTransactionManagerHolder.set(this.jtaTransactionManager);   

  13.     }   

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

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

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

  17.         configTimeLobHandlerHolder.set(this.lobHandler);   

  18.     }   

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

  20.     try {   

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

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

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

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

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

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

  27.         if (!isExposeTransactionAwareSessionFactory()) {   

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

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

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

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

  32.             config.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS,   

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

  34.         }   

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

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

  37.             config.setInterceptor(this.entityInterceptor);   

  38.         }   

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

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

  41.             config.setNamingStrategy(this.namingStrategy);   

  42.         }   

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

  44.             // Register specified Hibernate type definitions.   

  45.             Mappings mappings = config.createMappings();   

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

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

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

  49.             }   

  50.         }   

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

  52.             // Register specified Hibernate FilterDefinitions.   

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

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

  55.             }   

  56.         }   

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

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

  59.                 // Load Hibernate configuration from given location.   

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

  61.             }   

  62.         }   

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

  64.             // Add given Hibernate properties to Configuration.   

  65.             config.addProperties(this.hibernateProperties);   

  66.         }   

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

  68.             boolean actuallyTransactionAware =   

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

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

  71.             config.setProperty(Environment.CONNECTION_PROVIDER,   

  72.                     actuallyTransactionAware ?   

  73.                     TransactionAwareDataSourceConnectionProvider.class.getName() :   

  74.                     LocalDataSourceConnectionProvider.class.getName());   

  75.         }   

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

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

  78.             config.setProperty(   

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

  80.         }   

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

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

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

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

  85.             }   

  86.         }   

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

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

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

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

  91.             }   

  92.         }   

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

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

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

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

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

  98.             }   

  99.         }   

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

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

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

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

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

  105.                     throw new IllegalArgumentException(   

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

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

  108.                 }   

  109.                 config.addDirectory(file);   

  110.             }   

  111.         }   

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

  113.             // Register cache strategies for mapped entities.   

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

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

  116.                 String[] strategyAndRegion =   

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

  118.                 if (strategyAndRegion.length > 1) {   

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

  120.                 }   

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

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

  123.                 }   

  124.             }   

  125.         }   

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

  127.             // Register cache strategies for mapped collections.   

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

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

  130.                 String[] strategyAndRegion =   

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

  132.                 if (strategyAndRegion.length > 1) {   

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

  134.                 }   

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

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

  137.                 }   

  138.             }   

  139.         }   

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

  141.             // Register specified Hibernate event listeners.   

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

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

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

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

  146.                 Object listenerObject = entry.getValue();   

  147.                 if (listenerObject instanceof Collection) {   

  148.                     Collection listeners = (Collection) listenerObject;   

  149.                     EventListeners listenerRegistry = config.getEventListeners();   

  150.                     Object[] listenerArray =   

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

  152.                     listenerArray = listeners.toArray(listenerArray);   

  153.                     config.setListeners(listenerType, listenerArray);   

  154.                 }   

  155.                 else {   

  156.                     config.setListener(listenerType, listenerObject);   

  157.                 }   

  158.             }   

  159.         }   

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

  161.         postProcessConfiguration(config);   

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

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

  164.         this.configuration = config;   

  165.         sf = newSessionFactory(config);   

  166.     }   

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

  168.     finally {   

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

  170.             // Reset DataSource holder.   

  171.             configTimeDataSourceHolder.set(null);   

  172.         }   

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

  174.             // Reset TransactionManager holder.   

  175.             configTimeTransactionManagerHolder.set(null);   

  176.         }   

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

  178.             // Reset LobHandler holder.   

  179.             configTimeLobHandlerHolder.set(null);   

  180.         }   

  181.     }   

  182.     // Execute schema update if requested.   

  183.     if (this.schemaUpdate) {   

  184.         updateDatabaseSchema();   

  185.     }   

  186.     return sf;   

  187. }  


而直接调用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.     private final SessionFactory target;   

  3.     public TransactionAwareInvocationHandler(SessionFactory target) {   

  4.         this.target = target;   

  5.     }   

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

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

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

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

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

  11.             try {   

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

  13.             }   

  14.             catch (IllegalStateException ex) {   

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

  16.             }   

  17.         }   

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

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

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

  21.         }   

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

  23.             // Use hashCode of SessionFactory proxy.   

  24.             return new Integer(hashCode());   

  25.         }   

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

  27.         try {   

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

  29.         }   

  30.         catch (InvocationTargetException ex) {   

  31.             throw ex.getTargetException();   

  32.         }   

  33.     }   

  34. }  


我们看看getCurrentSession的实现,在SessionFactoryUtils中:
Java代码
  1. private static Session doGetSession(   

  2.             SessionFactory sessionFactory, Interceptor entityInterceptor,   

  3.             SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)   

  4.             throws HibernateException, IllegalStateException {   

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

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

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

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

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

  10.             // pre-bound Hibernate Session   

  11.             Session session = null;   

  12.             if (TransactionSynchronizationManager.isSynchronizationActive() &&   

  13.                     sessionHolder.doesNotHoldNonDefaultSession()) {   

  14.                 // Spring transaction management is active ->   

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

  16.                 session = sessionHolder.getValidatedSession();   

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

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

  19.                     TransactionSynchronizationManager.registerSynchronization(   

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

  21.                     sessionHolder.setSynchronizedWithTransaction(true);   

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

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

  24.                     FlushMode flushMode = session.getFlushMode();   

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

  26.                             !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {   

  27.                         session.setFlushMode(FlushMode.AUTO);   

  28.                         sessionHolder.setPreviousFlushMode(flushMode);   

  29.                     }   

  30.                 }   

  31.             }   

  32.             else {   

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

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

  35.             }   

  36.             if (session != null) {   

  37.                 return session;   

  38.             }   

  39.         }   

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

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

  42.         Session session = (entityInterceptor != null ?   

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

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

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

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

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

  48.         if (TransactionSynchronizationManager.isSynchronizationActive()) {   

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

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

  51.             SessionHolder holderToUse = sessionHolder;   

  52.             if (holderToUse == null) {   

  53.                 holderToUse = new SessionHolder(session);   

  54.             }   

  55.             else {   

  56.                 holderToUse.addSession(session);   

  57.             }   

  58.             if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {   

  59.                 session.setFlushMode(FlushMode.NEVER);   

  60.             }   

  61.             TransactionSynchronizationManager.registerSynchronization(   

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

  63.             holderToUse.setSynchronizedWithTransaction(true);   

  64.             if (holderToUse != sessionHolder) {   

  65.                 TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);   

  66.             }   

  67.         }   

  68.         else {   

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

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

  71.         }   

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

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

  74.             closeSession(session);   

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

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

  77.         }   

  78.         return session;   

  79.     }  


这里就是在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.     FlushMode previousFlushMode = null;   

  10.     try {   

  11.         previousFlushMode = applyFlushMode(session, existingTransaction);   

  12.         enableFilters(session);   

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

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

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

  16.         flushIfNecessary(session, existingTransaction);   

  17.         return result;   

  18.     }   

  19.     catch (HibernateException ex) {   

  20.         throw convertHibernateAccessException(ex);   

  21.     }   

  22.     catch (SQLException ex) {   

  23.         throw convertJdbcAccessException(ex);   

  24.     }   

  25.     catch (RuntimeException ex) {   

  26.         // Callback code threw application exception...   

  27.         throw ex;   

  28.     }   

  29.     finally {   

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

  31.         if (existingTransaction) {   

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

  33.             disableFilters(session);   

  34.             if (previousFlushMode != null) {   

  35.                 session.setFlushMode(previousFlushMode);   

  36.             }   

  37.         } //否则把Session关闭   

  38.         else {   

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

  40.             if (isAlwaysUseNewSession()) {   

  41.                 SessionFactoryUtils.closeSession(session);   

  42.             }   

  43.             else {   

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

  45.             }   

  46.         }   

  47.     }   

  48. }  


我们看看怎样得到对应的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的获取和关闭,事务处理的绑定做好了封装 - 从这个角度看也大大方便了用户的使用。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多