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

Spring 专栏收录该内容
13 篇文章 0 订阅
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的获取和关闭,事务处理的绑定做好了封装 - 从这个角度看也大大方便了用户的使用。 
  • 1
    点赞
  • 0
    评论
  • 0
    收藏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:黑客帝国 设计师:我叫白小胖 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值