最近在来到一个新公司,使用新的spring3,hibernate4框架,在使用注解事务老是不起做用。html
首先看配置文件,而后再讲解。mysql
首先是springmvc-servlet.xml,这个配置文件是servlet的加载文件,web
引用这个文件的位置是在web.xml里spring
<!-- 定义 springmvcServlet --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- 默认/WEB-INF/[servlet名字]-servlet.xml加载上下文, 若是配置了contextConfigLocation参数, 将使用classpath:/springmvc-servlet.xml加载上下文 --> <param-name>contextConfigLocation</param-name> <param-value>classpath:/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- 拦截匹配的请求,这里全部请求采用名字为mvc-dispatcher的DispatcherServlet处理 --> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
<!-- 启动自动扫描该包下全部的Bean(例如@Controller) 这块很重要,会影响到事务--> <context:component-scan base-package="com.bpz.demo" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter> </context:component-scan> <!-- 定义视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/jsp/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean>
而后是applicationcontext.xmlsql
<!-- 启动自动扫描该包下全部的Bean 注意这块,也很是重要 --> <context:component-scan base-package="com.bpz.demo" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"></context:include-filter> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"></context:include-filter> </context:component-scan> <!-- Hibernate4 --> <!-- 加载资源文件 其中包含变量信息,必须在Spring配置文件的最前面加载,即第一个加载--> <context:property-placeholder location="classpath:mysql.properties" /> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="packagesToScan"> <list> <!-- 能够加多个包 --> <value>com.bpz.demo.entity</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> </props> </property> </bean> <!-- 数据库映射 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.user}" /> <property name="password" value="${jdbc.pass}" /> </bean> <!-- 基于注释的事务,当注释中发现@Transactional时,使用id为“transactionManager”的事务管理器 --> <!-- 若是没有设置transaction-manager的值,则spring以缺省默认的事务管理器来处理事务,默认事务管理器为第一个加载的事务管理器 --> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- 配置Hibernate事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>
引用位置是在web.xml里的数据库
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/applicationcontext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
以上是一个正确的配置方式,事务起做用彻底没有问题。如今说说为何不少时候配置的事务未起做用呢。express
1.若是事务未起做用还未报错或异常,那应该是使用了sessionFactory.openSession(),在hibernate4里,若是使用spring来管理事务,须要使用session
Session session = sessionFactory.getCurrentSession();来获取session。mvc
2.若是使用了Session session = sessionFactory.getCurrentSession();来获取session有可能会报一个异常:app
No Session found for current thread
为何会出现这种状况呢,这和上面的配置就有关系了。通常状况下不少人为了省事,在servlet配置文件里直接
<context:component-scan base-package="com.bpz.demo"> </context:component-scan>
这样配置,对所有代码扫描,可是在servlet里并无配置事务,因此事务彻底不起做用.
不起做用的缘由,在另外一篇转载的文章里能够看到。
servlet是最后加载的,在最后使用
context:component-scan
扫描的时候,也会发如今有@Service,@Reposity等注解并进行实例化,因为在servlet配置文件里未配置事务,因此形成了事务不起做用,
看下面的源代码:
public Session currentSession() throws HibernateException { Object value = TransactionSynchronizationManager.getResource(this.sessionFactory); if(value instanceof Session) { return (Session)value; } else if(value instanceof SessionHolder) { SessionHolder session2 = (SessionHolder)value; Session session1 = session2.getSession(); if(TransactionSynchronizationManager.isSynchronizationActive() && !session2.isSynchronizedWithTransaction()) { TransactionSynchronizationManager.registerSynchronization(new SpringSessionSynchronization(session2, this.sessionFactory)); session2.setSynchronizedWithTransaction(true); FlushMode flushMode = session1.getFlushMode(); if(flushMode.equals(FlushMode.MANUAL) && !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { session1.setFlushMode(FlushMode.AUTO); session2.setPreviousFlushMode(flushMode); } } return session1; } else if(this.jtaSessionContext != null) { Session session = this.jtaSessionContext.currentSession(); if(TransactionSynchronizationManager.isSynchronizationActive()) { TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session)); } return session; } else { throw new HibernateException("No Session found for current thread"); } }
1:@Transactional声明的方法执行时,Spring的TransactionManager会自动Open Session,自动开启事务,而且将此Sesion绑定到SpringSessionContext(其实是TransactionSynchronizationManager的ThreadLocal的Map)中..而@Transactional的声明只有
tx:annotation-driven配置才会处理这个声明
2:SessionFactory.getCurrentSession()方法执行时,调用SpringSessionContext.currentSession()从TransactionSynchronizationManager的上下文中查找 当前的Session
3:找到后返回当前的Session,找不到,则返回HibernateException("No Session found for current thread")
对上面源代码的理解。
so:处理事务不起做用的办法就是servlet里作好本身的事,扫描@controller就能够了,这不但能加快启动速度,并且最主要的就是不会 多此一举。