zoukankan      html  css  js  c++  java
  • 画蛇添足-记spring3 hibernate4整合时遇到问题的处理办法

      最近在来到一个新公司,使用新的spring3,hibernate4框架,在使用注解事务总是不起作用。

    首先看配置文件,然后再讲解。

    首先是springmvc-servlet.xml,这个配置文件是servlet的加载文件,

    引用这个文件的位置是在web.xml里

        <!-- 定义 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.xml

    <!-- 启动自动扫描该包下所有的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>

    以上是一个正确的配置方式,事务起作用完全没有问题。现在说说为什么很多时候配置的事务未起作用呢。

    1.如果事务未起作用还未报错或异常,那应该是使用了sessionFactory.openSession(),在hibernate4里,如果使用spring来管理事务,需要使用

    Session session = sessionFactory.getCurrentSession();来获取session。

    2.如果使用了Session session = sessionFactory.getCurrentSession();来获取session有可能会报一个异常:

    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就可以了,这不但能加快启动速度,而且最主要的就是不会  画蛇添足。

    
    
    
  • 相关阅读:
    pycharm的各种设置,配置
    python中文件路径的问题
    Pycharm使用的一些问题!!!
    networkx如何将图写到邻接矩阵里?
    networkX如何读取存储图的二进制.dat文件
    再次理解线性回归与梯度下降
    Python DataFrame 如何删除原来的索引,重新建立索引
    NetworkX初相识
    haproxy + keepalived + mycat 高可用与负载均衡集群配置 centos7
    otter+canal
  • 原文地址:https://www.cnblogs.com/xusir/p/4156575.html
Copyright © 2011-2022 走看看