`
osacar
  • 浏览: 211542 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

由OpenSessionInViewFilter报错引出的spring3.0事务配置问题及解决办法

阅读更多
开发环境

IDE: eclipse3.4
FrameWork: spring3.0 + spring mvc 3.0 + hibernate 3.2
Server: Tomcat 6.0

使用 OpenSessionInViewFilter的原因

引用Hibernate 允许对关联对象、属性进行延迟加载,但是必须保证延迟加载的操作限于同一个 Hibernate Session 范围之内进行。如果 Service 层返回一个启用了延迟加载功能的领域对象给 Web 层,当 Web 层访问到那些需要延迟加载的数据时,由于加载领域对象的 Hibernate Session 已经关闭,这些导致延迟加载数据的访问异常。而Spring为我们提供的OpenSessionInViewFilter过滤器为我们很好的解决了这个问题。OpenSessionInViewFilter的主要功能是使每个请求过程绑定一个 Hibernate Session,即使最初的事务已经完成了,也可以在 Web 层进行延迟加载的操作。OpenSessionInViewFilter 过滤器将 Hibernate Session 绑定到请求线程中,它将自动被 Spring 的事务管理器探测到。


在Service层执行插入,删除及数据更新操作时会包以下错误
引用org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition


报错原因:
看过OpenSessionInViewFilter以后就知道,OpenSessionInViewFilter的getSession方法中会对session的flushMode设定一个默认为NEVER的值

解决办法有两个:
1:继承OpenSessionInViewFilter,并覆盖getSession,设施flushMode 为AUTO
2:通过spring的事务管理Hibernate的sessionFactory,对不同的数据操作作事务的隔离及限制

因为反正都要用到spring作事务管理,所以我在项目中采用了第二种方法

spring2.5以后就支持annotation配置了,到了3.0配置更加简化方便了,但对于新手来说,如果不懂具体的spring运作原理,很容易犯错误,并且在报错之后很难发现错误原因。

在此特将spring的配置方法写出来,并经过实例测试,是可以通过并正常运行的。希望对在这方面遇到问题的朋友有帮助,spring的基于annotation请不明白的朋友在网上查看相关文档。

主要配置文件有两个

app-config.xml 配置数据源,hibernate,事务等
mvc-config.xml 配置spring mvc的 controller,viewResolver,messageConvert等

本文主要涉及 app-config.xml的配置

首先通过以下配置,自动扫描com包内作了annotation注解的类,并实例化(至于里面为什么会有个exclude-filter请看文章的后面)

  <context:component-scan base-package="com"> 
       <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

hibernate及数据源的配置

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
   <property name="dataSource" ref="dataSource"/>
   <property name="packagesToScan" value="com"/>

          
  
   <property name="hibernateProperties">
<props>

   
   
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>    
<!--  <prop key="hibernate.dialect">org.hibernate.dialect.sql</prop>-->
<!-- <prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop> -->

<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>


<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>

<!-- 
<prop key="hibernate.search.default.directory_provider">org.hibernate.search.store.FSDirectoryProvider</prop>
<prop key="hibernate.search.default.indexBase">/WEB-INF/indexes</prop>-->
</props>
    </property>
</bean>

配置transactionManager管理事务

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" autowire="byName"/>

接下来是 自动化事务

<tx:annotation-driven/>
如果前面配置的事务id不是"transactionManager",比如id="txManager",则应该如下配置

<tx:annotation-driven transaction-manager="txManager"/>

这里需要注意的问题,看过spring的参考手册的朋友都会这样配置

<context:component-scan base-package="com">
因为省事,一句话可以自动把com包低下的所有带annotation注解的类都实例化并配好了,但如果这样简单的配置会导致刚才spring的事务配置失效
原因:
实例化@Controller类时,Spring会自动把关联的@Service(此@Service已做了@Transaction事务注解)类实例化,此时事务并未生效,导致@Transaction注解无效,事务未被注册

因此需要把@Controller和其它的@Service,@Components,@Reposity等分开实例化,在事务生效后,并且其它组件都实例化完成后,@Controller最后实例化,app-config.xml和mvc-config.xml的配置分别如下

app-config.xml

   <!-- 扫描com及子包,自动实例化带@注释的实例,这里排除@Controller,所有controller的实例化在 mvc-config中完成 -->
   <context:component-scan base-package="com"> 
       <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

mvc-config.xml(注意里面的注释)

<!-- 扫描com及子包,自动实例化带@controller注释的实例,
      由于实例化controller时会对controller关联的Service类一同实例化,所以这里需要排除@Service
-->
<context:component-scan base-package="com">
   <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
   <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>


此时在带@Service注解的类中添加@Transactional标签就会其作用了,例如

@Service("articleService")
@Transactional
public class ArticleService extends GenericService<Article, Long> {
      
        @Autowired
private ArticleDao articleDao;

        @Transactional(readOnly=true)
        public List<Article> getArticles(){
          /*...*/
        }
       
        @Transactional(propagation=Propagation.REQUIRED)
        public void saveArticle(long id){
            /*..*/
        }



更多事务注解的方式请参考spring3.0 reference

这这样需要对每个Service的方法都要做类似的注解,多了会很麻烦,也难管理维护。

下面通过spring的aop对事务进行自动化管理,配置如下

<!-- 配置aop 切入点 和事务访问策略 -->  
<aop:config>
  <aop:pointcut id="serviceOperation" expression="execution(* com.*.service..*Service.*(..))"/>
      <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>
</aop:config>


<tx:advice id="txAdvice" >
<tx:attributes>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="create*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice> 

注意
1这部分代码要放在<tx:annotation-driven/>的上面,否则tx:advice不起作用
2<tx:advice id="txAdvice" > 如果前面配置的事务id不是"transactionManager",比如id="txManager",则应该修改为 <tx:advice id="txAdvice" transaction-manager="txManager" >


app-config.xml的整个配置如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
  
    
   
   <!-- 扫描com及子包,自动实例化带@注释的实例,这里排除@Controller,所有controller的实例化在 mvc-config中完成 -->
   <context:component-scan base-package="com"> 
       <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
   
         
  
   <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>/WEB-INF/prop/database.properties</value>
            </list>
        </property>
    </bean>

    <!-- JNDI DataSource for J2EE environments  user c3p0-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
  <property name="driverClass"><value>${jdbc.driverClass}</value></property>
  <property name="jdbcUrl"><value>${jdbc.jdbcUrl}</value></property>
  <property name="user"><value>${jdbc.user}</value></property>
  <property name="password"><value>${jdbc.password}</value></property>
  <property name="minPoolSize"><value>${jdbc.minPoolSize}</value></property>
  <property name="maxPoolSize"><value>${jdbc.maxPoolSize}</value></property>
  <property name="maxIdleTime"><value>${jdbc.maxIdleTime}</value></property>
</bean>
   
   
     <!--  hibernate.hbm2ddl.auto参数说明
validate           加载hibernate时,验证创建数据库表结构
create             每次加载hibernate,重新创建数据库表结构,这就是导致数据库表数据丢失的原因。
create-drop        加载hibernate时创建,退出是删除表结构
  update             加载hibernate自动更新数据库结构
  none               不更新数据库结构
    -->   
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
   <property name="dataSource" ref="dataSource"/>
   <property name="packagesToScan" value="com"/>

          
  
   <property name="hibernateProperties">
<props>

   
   
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>    
<!--  <prop key="hibernate.dialect">org.hibernate.dialect.sql</prop>-->
<!-- <prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop> -->

<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>


<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>

<!-- 
<prop key="hibernate.search.default.directory_provider">org.hibernate.search.store.FSDirectoryProvider</prop>
<prop key="hibernate.search.default.indexBase">/WEB-INF/indexes</prop>-->
</props>
    </property>
</bean>   
   
    <!-- 配置hibernate事务 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" autowire="byName"/>

  

   
   
<!-- 配置aop 切入点 和事务访问策略 -->  
<aop:config>
  <aop:pointcut id="serviceOperation" expression="execution(* com.*.service..*Service.*(..))"/>
      <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>
</aop:config>


<tx:advice id="txAdvice" >
<tx:attributes>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="create*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>   
   
    <!-- tx:annotation 自动配置事务,注意这个标签必需放在tx:advice下面,否则不起作用 -->
    <tx:annotation-driven/>
   
       
<!-- Application Message Bundle -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/WEB-INF/messages/messages" />
<property name="cacheSeconds" value="0" />
</bean>

<!-- Configures Spring MVC
<import resource="mvc-config.xml" />
-->


</beans>





分享到:
评论

相关推荐

    struts2.1+spring3.0+hibernate3.3整合

    ### Struts2.1 + Spring3.0 + Hibernate3.3 整合实践 #### 一、项目背景与概述 在Java Web开发中,Struts2、Spring与Hibernate是三个非常重要的框架,它们分别用于处理MVC架构中的表示层、业务逻辑层与数据访问层...

    关于OpenSessionInViewFilter的学习

    OpenSessionInViewFilter是Spring框架中一个非常重要的组件,主要用于解决数据访问层(DAO)与视图层(View)之间的事务管理问题。在Web应用中,由于HTTP请求的无状态性,一次请求通常由多个Servlet过滤器、控制器和...

    OpenSessionInViewFilter

    OpenSessionInViewFilter个人学习总结

    spring框架中常用的配置

    Spring框架还提供了一系列的过滤器来处理Web应用中的请求,例如OpenSessionInViewFilter用于解决Hibernate懒加载问题。 - **OpenSessionInViewFilter**:此过滤器在请求处理开始时打开一个Hibernate会话,在请求...

    马士兵Spring课堂笔记(超级详细版).pdf

    我们可以通过配置XML文件和注解来实现整合,并使用opensessionInviewfilter来解决会话问题。 Spring JDBC面向接口编程 本节讲解了如何使用Spring JDBC来实现面向接口编程。我们可以通过配置XML文件和注解来实现DAO...

    spring面试题大全

    4. 解决OpenSessionInView问题,可以使用OpenSessionInViewFilter或OpenSessionInViewInterceptor,确保在一次HTTP请求中保持Hibernate Session的开放状态,以解决懒加载异常。 Spring的事务管理分为编程式和声明式...

    Spring配置总结

    通过在web.xml中配置`OpenSessionInViewFilter`,可以实现持久化层的事务与视图渲染的无缝结合,提高应用性能。 ##### 2. **Struts-config.xml与Spring集成** 当Spring与Struts框架集成时,可以通过`...

    Spring提供的CharacterEncoding和OpenSessionInView功能

    在处理Web应用时,Spring提供了一些关键特性,如`CharacterEncodingFilter`和`OpenSessionInViewFilter`,它们对于解决特定问题至关重要。 首先,让我们深入了解一下`CharacterEncodingFilter`。在Web应用中,字符...

    spring面试题集

    当遇到OpenSessionInView问题时,可以使用OpenSessionInViewFilter或OpenSessionInViewInterceptor来解决。 Spring的事务管理提供了强大且灵活的机制,主要包括编程式和声明式两种方式: 1. 编程式事务管理:通过...

    spring管理struts和hibernate

    - 在`web.xml`中配置`OpenSessionInViewFilter`过滤器,自动管理事务。 #### 三、总结 通过以上步骤,我们不仅可以让Spring有效地管理Struts,实现更灵活的业务逻辑控制,还可以利用Spring的强大功能管理...

    spring2.5学习PPT 传智博客

    - 利用CharacterEncodingFilter和OpenSessionInViewFilter解决编码问题。 通过以上内容的学习,开发者可以深入理解Spring 2.5的核心概念,掌握其在实际开发中的应用,从而提高工作效率和代码质量。

    Spring hibernate opensessioninview

    该模式的核心在于通过Spring提供的`OpenSessionInViewFilter`过滤器,在视图渲染过程中保持Hibernate Session的打开状态,从而简化了事务管理,并避免了一些常见的懒加载异常。 #### 一、OpenSessionInViewFilter...

    Struts,Spring与hibernate集成

    - 修改Struts配置:将Action的类型改为DelegatingActionProxy,这是Spring Struts集成的关键,使得Action实例由Spring管理。 - 配置web.xml:设置ContextLoaderListener监听器,加载Spring的bean工厂,并在web应用...

    Struts2+Spring+hibernate中对action的单元测试环境搭建[总结].pdf

    解决这个问题的方法有两种:一种是在 web.xml 中使用 Spring 提供的 OpenSessionInViewFilter,另一种是在 application.xml 中配置 OpenSessionInViewFilter。前者通用,后者只能用于 SpringMVC 结构中。 最后,...

    SSH2.0配置日记

    涉及到了多个知识点,包括 Spring 配置、Hibernate 配置、Struts2 配置、ConnectionPool 配置、数据库配置、事务管理、OpenSessionInViewFilter 配置、web.xml 配置、依赖注入、Struts2 和 Spring 整合、Hibernate ...

    面试知识点总结--spring面试题大全.pdf

    本文将深入探讨Spring面试中常见的知识点,包括Spring的优点、DI(Dependency Injection)的实现方式、IOC(Inversion of Control)的理解、Spring对ORM框架如Hibernate的支持,以及Spring的事务管理。 首先,...

    Struts+Hibernate+Spring的基本流程

    同时,需要在Spring的配置文件(如applicationContext.xml)中定义Action及DAO实例,并指定相应的依赖注入。 3. Hibernate的配置文件(hibernate.cfg.xml)中,配置数据库连接、实体类映射等信息。Spring的配置文件...

Global site tag (gtag.js) - Google Analytics