`

在spring+hibernate中,只读事务是如何被优化的。

阅读更多
解惑:在spring+hibernate中,只读事务是如何被优化的。

       大家都知道,spring+hibernate的环境下,spring对只读事务会有特别的优化,那么spring是如何做到这个优化的呢?     

Without ejb中写到,当事务被标识为只读事务时,某些可以针对只读事务进行优化的资源就可以执行相应的优化措施,比如说hibernate的session在只读事务模式下不会尝试检测和同步持久对象的状态的更新。另外还写到jdbc的connection可以通过调用setReadOnly(true)来切换到只读事务模式上来;但是大多数jdbc driver会忽略掉他。

我们知道spring中所谓的只读事务就是通过设置session的flushmode为never来实现的(http://www.iteye.com/topic/87426 )。那么把flushmode设置为never能给我们带来什么呢?
我们来看一下hibernate中JDBCTransaction中的方法:
java 代码
   1. publicvoid commit()  throws  HibernateException {   
   2.         if  (!begun) {   
   3.            thrownew TransactionException( "Transaction not successfully started" );   
   4.        }   
   5.     
   6.        log.debug( "commit" );   
   7.     
   8.         if  ( !transactionContext.isFlushModeNever() && callback ) {   
   9.            transactionContext.managedFlush();  //if an exception occurs during flush, user must call rollback()   
  10.        }   

  11. //也就是在这里会判断是否需要刷新一级缓存中的持久对象,如果session的flushmode不为//never而且需要回调的话,那么就刷新一级缓存中的持久对象,向数据库发送sql语句   
  12.     
  13.        beforeTransactionCompletion();   
  14.         if  ( callback ) {   
  15.            jdbcContext.beforeTransactionCompletion(  this  );   
  16.        }   
  17.     
  18.         try  {   
  19.            commitAndResetAutoCommit();   
  20. //提交事务,并且把事务的commit方式设置为auto,是不是和spring在事务开始和事务结束//时设置session的flush mode的方式是一样的呀。   
  21.            log.debug( "committed JDBC Connection" );   
  22.            committed =  true ;   
  23.             if  ( callback ) {   
  24.               jdbcContext.afterTransactionCompletion(  true ,  this  );   
  25.            }   
  26.            afterTransactionCompletion( Status.STATUS_COMMITTED );   
  27.        }   
  28.         catch  (SQLException e) {   
  29.            log.error( "JDBC commit failed" , e);   
  30.            commitFailed =  true ;   
  31.             if  ( callback ) {   
  32.               jdbcContext.afterTransactionCompletion(  false ,  this  );   
  33.            }   
  34.            afterTransactionCompletion( Status.STATUS_UNKNOWN );   
  35.            thrownew TransactionException( "JDBC commit failed" , e);   
  36.        }   
  37.         finally  {   
  38.            closeIfRequired();   
  39.        }   
  40.     }   

我们看一下那个managedFlush()方法,这个方法主要就是刷新一级缓存的一个方法:
java 代码
   1. public   void  managedFlush() {   
   2.          if  ( isClosed() ) {   
   3.             log.trace(  "skipping auto-flush due to session closed"  );   
   4.              return ;   
   5.         }   
   6.         log.trace( "automatically flushing session" );   
   7.         flush();   
   8.          //刷新这个session实例的一级缓存。   
   9.          if  ( childSessionsByEntityMode !=  null  ) {   
  10.             Iterator iter = childSessionsByEntityMode.values().iterator();   
  11.              while  ( iter.hasNext() ) {   
  12.                 ( (Session) iter.next() ).flush();   
  13.             }   
  14.         } //刷新该session的子session的一级缓存。   
  15.     }   

我们知道如果 session 的 flushmode 为 never 的时候,以上的方法是不会调用的,这样就可以省去很多 flush 的开销。于是命题就变成了 flush 操作有哪些开销了。
也许你要问 flush 和不 flush 有什么样的区别,在开销上有多大的区别呢。要看明白 hibernate 是怎么做 flush 的,那就必须要知道观察者模式了,实际上 session 是一个被观察者 (subject) ,而真正执行 flush 的是一个观察者 (observer), 我们来看一下下面这个图:
( 这个图是我画在纸上然后用手机拍下来的 )

从这里面我们可以看到 flush 实际上是由 DefaultFlushEventListener 来执行的,而且 sessionimpl 默认的只注册了一个 FlushEventListener 实例(为什么只有一个还要这样做,我估计他是为了扩展的需要,不知道 3.2 中是否就不止一个了呢?),这个 DefaultFlushEventListener 最终执行了 flush 的方法:

java 代码

   1. public   void  onFlush(FlushEvent event)  throws  HibernateException {  
   2.          final  EventSource source = event.getSession();  
   3.          if  ( source.getPersistenceContext().hasNonReadOnlyEntities() ) {  
   4.               
   5.             flushEverythingToExecutions(event);  
   6. //这个方法是flush前的准备工作,它把需要被flush的实体,集合,等等放到需要被flush  
   7. //的一个队列中  
   8.             performExecutions(source);  
   9. //这个方法是最重要的,因为在这里才是真正的执行sql语句,并且负责更新二级缓存(如果你//配置了二级缓存的话)  
  10.             postFlush(source);  
  11. //负责flush后的善后工作,比如说一个对象不再被另外一个对象关联了,那么就把这个对象//从一级缓存重剔除,等等。  
  12.              if  ( source.getFactory().getStatistics().isStatisticsEnabled() ) {  
  13.                 source.getFactory().getStatisticsImplementor().flush();  
  14.             }  
  15.               
  16.         }  
  17.     }  


由此我们看到 hibernate 在执行 flush 操作的时候还是做了不少事情的,它不但要把持久对象刷到数据库,而且还要把其管理的对象也都刷到数据库中,这是一个很大的操作。同时如果你使用了二级缓存, flush 操作也会涉及到它,而且在 flush 时还要判断哪些时插入的,哪些是更新的,哪些是删除的等等, flush 完了还得更新一级缓存等。

其实我只是对 flush 作了最简单的概括和描述,事实上从代码上看来它远比我们想象的要来得复杂的多。

在对 flush 简单得了解了之后,我们再来讨论一下:为什么要把查询设置为只读事务。因为一个本来只是查询的操作,却要在事务提交时多做这么多事情,这显然是不合理的,所以 hibernate 才给 session 的设置了这么一个 flushmode ,那么只要这个 mode 为 never ,就可以免去这些不必要的操作。而 spring 在对 hibernate 的支持时也充分的考虑到了这一点,所以就把只读事务的 session 的 flush mode 设置为了 never 。这样我们事务提交时就不会执行 flush 操作了。

总结:
所以说,我们在使用 spring 时一定要注意把查询的操作定义成只读事务,这个可以给我们带来不必要的开销,比如看如下配置。
< property name = "transactionAttributes" >
           < props >
              < prop key = "do*" > PROPAGATION_REQUIRED prop >
              < prop key = "get*" > PROPAGATION_REQUIRED,readOnly prop >
              < prop key = "load*" > PROPAGATION_REQUIRED,readOnly prop > 
              < prop key = "find*" > PROPAGATION_REQUIRED,readOnly prop >
              < prop key = "list*" > PROPAGATION_REQUIRED,readOnly prop >
           props >
property >

或者事务的传播途径最好能设置为 supports (运行在当前的事务范围内,如果当前没有启动事务,那么就不在事务范围内运行)或者 not supports (不在事务范围内执行,如果当前启动了事务,那么挂起当前事务),也就是说查询操作其实可以不必要真正的开启一个数据库事务,因为开启一个真正的数据库事务又会给我们带来一点点可以忽略不计的开销。下面是一个例子
< property name = "transactionAttributes" >
           < props >
              < prop key = "do*" > PROPAGATION_REQUIRED prop >
              < prop key = "get*" > PROPAGATION_SUPPORTS,readOnly prop >
              < prop key = "load*" > PROPAGATION_SUPPORTS,readOnly prop > 
              < prop key = "find*" > PROPAGATION_SUPPORTS,readOnly prop >
              < prop key = "list*" > PROPAGATION_SUPPORTS,readOnly prop >
           props >
property >
分享到:
评论

相关推荐

    jbpm 整合到现有的spring+hibernate系统

    例如,在上述配置中,对所有以`get`开头的方法都设置了只读事务。 ```xml class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&gt; *"&gt;PROPAGATION_REQUIRED,readOnly,...

    spring3+hibernate4配置声明式事务管理(annotation方式)

    在Java开发中,Spring框架和Hibernate是两个非常重要的组件,它们分别用于依赖注入和对象关系映射。本实例工程展示了如何在Spring 3和Hibernate 4中使用注解进行声明式事务管理,这是一种高效且易于维护的方法。接...

    spring +hibernate+structs2 从入门到精通学习宝典

    - **Spring与Hibernate集成**: 在Spring中配置Hibernate,通常需要设置DataSource,这是Hibernate访问数据库的数据源。通过Spring的`&lt;bean&gt;`配置,可以轻松地将Hibernate的`SessionFactory`与Spring的事务管理相结合...

    spring + hibernate 数据话持久层

    在IT领域,特别是Java企业级应用开发中,Spring框架与Hibernate技术是构建高效、可维护的数据持久层的关键组件。从给定的文件标题、描述、标签以及部分内容中,我们可以提炼出以下关键知识点: ### Spring与...

    Spring+Struts2+Spring3+Hibernate3三大框架整合

    在JavaEE应用程序开发中,Spring、Struts2和Hibernate3是三个非常重要的框架,它们各自负责不同的职责,而将它们整合在一起可以构建出强大的企业级应用。Spring作为核心框架,提供了依赖注入、AOP(面向切面编程)、...

    Spring Hibernate 事务处理 详细说明

    3. **Hibernate事务配置:**在Spring中,需要配置Hibernate SessionFactory,并将其注入到需要进行数据库操作的服务中。同时,通过`PlatformTransactionManager`接口(如HibernateTransactionManager)配置事务管理...

    Struts+Hibernate+Spring的整合方法

    2. **配置dataSource和SessionFactory**:在Spring的配置文件中,你需要配置数据源(dataSource)和SessionFactory,这样可以将Hibernate的配置集成到Spring中,通常可以移除单独的hibernate.cfg.xml文件。...

    spring3.2+strut2+hibernate4

    spring3.2+strut2+hibernate4 注解方式。 spring.xml &lt;beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi=...

    Spring_Hibernate使用TransactionInterceptor声明式事务配置.doc

    在这篇文章中,我们将探讨使用Spring_Hibernate框架实现声明式事务配置的方法,具体来说,就是使用TransactionInterceptor来管理事务。在Spring框架中,事务管理器是核心组件之一,它负责管理事务的生命周期,包括...

    springmvc+jpa(hibernate实现)+spring整合实例

    其中项目里面的配置文件或是代码每行都有注释,网上这种整合实例很少,就奉献出来大家一起学习吧,在整合过程中遇到一个很奇怪的问题,就是spring的事务我配置成@Transactional(readOnly = true)只读事务,但是还...

    网络硬盘(Struts 2+Hibernate+Spring实现)

    在本项目中,Spring管理着所有的bean,包括Struts 2的Action、Hibernate的数据访问对象(DAO)以及业务逻辑组件。它还负责事务管理,确保在并发环境下数据的一致性。 4. 文件管理:在网络硬盘系统中,用户可以创建...

    Spring AOP管理Hibernate事务(TransactionInSpringAOP)

    在Spring框架中,AOP(面向切面编程)被广泛用于事务管理,特别是与ORM(对象关系映射)框架如Hibernate结合使用时。本篇文章将深入探讨如何在Spring AOP中实现Hibernate事务管理,以及这一过程中的关键概念和技术...

    集成spring的hibernate懒加载

    7. **使用`@Transactional(readOnly = true)`**:对于只读操作,可以使用只读事务,这样即使在Session关闭后,仍能安全地访问懒加载属性,因为只读事务不会开启二级缓存。 在实际项目中,选择合适的策略需要综合...

    spring hibernate struts整合开发实例

    它可以定义事务属性,比如`find*`方法被标记为只读事务,其他方法则默认为读写事务。 7. **Struts与Spring集成**:在Struts中,Action类通常由Spring管理,这样可以通过依赖注入(DI)获取Service层的对象。为了...

    spring学习事务源码

    在源码分析中,我们可以看到当一个方法被@Transactional标记后,Spring会创建一个代理,这个代理在目标方法执行前后插入事务管理逻辑。在方法开始前,如果满足事务启动条件,就会调用`PlatformTransactionManager`的...

    spring3.0两种事务管理配置

    在 Spring 配置文件中,我们需要定义事务处理类,不同的数据访问方式,事务处理类不同,如 Hibernate 操作的 HibernateTransactionManager,JDBC 操作的使用 DataSourceTransactionManager。 接下来,我们需要定义...

    spring事务详解

    在不同的数据访问技术(如JDBC、Hibernate、JPA等)中,Spring提供了相应的实现类来适配各自的事务模型。 Spring的AOP(面向切面编程)框架与事务管理紧密结合,使得声明式事务管理成为可能。声明式事务管理允许...

    Spring事务类型祥解

    - `readOnly`: 是否为只读事务,如果设置为`true`,则事务管理器会尽可能地优化只读事务,例如在某些数据库中禁用写锁定。 - `rollbackFor`和`noRollbackFor`: 分别指定发生哪些异常时应回滚和不应回滚事务。 3. ...

    spring_如何管理事务的

    Spring框架为开发者提供了一套强大的事务管理机制,它简化了应用程序中的事务控制逻辑,使得开发人员能够更加专注于业务逻辑的编写,而不是繁琐的事务管理代码。Spring支持两种类型的事务管理方式:编程式事务管理和...

Global site tag (gtag.js) - Google Analytics