由OpenSessionInViewFilter报错引出的spring3.0事务配置问题及解决办法
开发环境
IDE: eclipse3.4
FrameWork: spring3.0 + spring mvc 3.0 + hibernate 3.2
Server: Tomcat 6.0
使用 OpenSessionInViewFilter的原因
<context:component-scan base-package="com"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
hibernate及数据源的配置
<bean id="sessionFactory" 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>
<bean id="transactionManager" autowire="byName"/>
<tx:annotation-driven/>
<tx:annotation-driven transaction-manager="txManager"/>
<context:component-scan base-package="com">
<!-- 扫描com及子包,自动实例化带@注释的实例,这里排除@Controller,所有controller的实例化在 mvc-config中完成 --> <context:component-scan base-package="com"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
<!-- 扫描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("articleService")@Transactionalpublic class ArticleService extends GenericService<Article, Long> { @Autowiredprivate ArticleDao articleDao; @Transactional(readOnly=true) public List<Article> getArticles(){ /*...*/ } @Transactional(propagation=Propagation.REQUIRED) public void saveArticle(long id){ /*..*/ }}<!-- 配置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>
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://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" 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" 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" 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" value="/WEB-INF/messages/messages" /><property name="cacheSeconds" value="0" /></bean><!-- Configures Spring MVC <import resource="mvc-config.xml" />--></beans><?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>spring-security-base</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/security.xml /WEB-INF/spring/app-config.xml </param-value> </context-param> <!-- spring security 过滤器链 --> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Reads request input using UTF-8 encoding --><filter><filter-name>characterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><init-param><param-name>forceEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>characterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping> <!-- for restful style 注意要放在 characterEncodingFilter 否则可能引起不必要的乱码--> <filter> <filter-name>httpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class></filter><filter-mapping> <filter-name>httpMethodFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping> <!--OpenSessionInViewFilter 作用,防止hibernate lazy-load 引起的不在同一session的报错 --> <filter> <filter-name>hibernateFilter</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>hibernateFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Enables clean URLs with JSP views e.g. /welcome instead of /app/welcome 注意: 过滤器执行顺序是按web.xml中的各过滤器的位置顺序执行的 将其它过滤器放在此过滤器的前面,否则由于url重写,会导致其它过滤器过滤的url失效--><filter><filter-name>UrlRewriteFilter</filter-name><filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class><!-- <init-param> <param-name>logLevel</param-name> <param-value>DEBUG</param-value> </init-param> --> </filter><filter-mapping><filter-name>UrlRewriteFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping> <!-- Handles all requests into the application --><servlet><servlet-name>springMVCBase</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring/mvc-config.xml</param-value></init-param><load-on-startup>2</load-on-startup></servlet><servlet-mapping><servlet-name>springMVCBase</servlet-name><url-pattern>/app/*</url-pattern></servlet-mapping> </web-app>
<bean id="sessionFactory" 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>
<bean id="transactionManager" autowire="byName"/>
<tx:annotation-driven/>
<tx:annotation-driven transaction-manager="txManager"/>
<context:component-scan base-package="com">
<!-- 扫描com及子包,自动实例化带@注释的实例,这里排除@Controller,所有controller的实例化在 mvc-config中完成 --> <context:component-scan base-package="com"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
<!-- 扫描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("articleService")@Transactionalpublic class ArticleService extends GenericService<Article, Long> { @Autowiredprivate ArticleDao articleDao; @Transactional(readOnly=true) public List<Article> getArticles(){ /*...*/ } @Transactional(propagation=Propagation.REQUIRED) public void saveArticle(long id){ /*..*/ }}<!-- 配置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>
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://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" 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" 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" 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" value="/WEB-INF/messages/messages" /><property name="cacheSeconds" value="0" /></bean><!-- Configures Spring MVC <import resource="mvc-config.xml" />--></beans>