多此一舉-記spring3 hibernate4整合時遇到問題的處理辦法 web.xml 中的listener、 filter、servlet 加載順序及其詳解

  最近在來到一個新公司,使用新的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裏並無配置事務,因此事務徹底不起做用.

不起做用的緣由,在另外一篇轉載的文章裏能夠看到。

web.xml 中的listener、 filter、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就能夠了,這不但能加快啓動速度,並且最主要的就是不會  多此一舉。

相關文章
相關標籤/搜索