zoukankan      html  css  js  c++  java
  • 关于Could not obtain transaction-synchronized Session for current thread 这个异常。

    Could not obtain transaction-synchronized Session for current thread 这个异常之前非常让我头大。对于网上的各种说法都试了一下反正都不行。现在终于使这个异常消失了,但是现在(2017-7-8)还搞不清真正的原因。

    这是异常的一部分。

    org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
        at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134)
        at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:988)
        at com.zcd.ssh.dao.impl.BaseDao.getSession(BaseDao.java:14)
        at com.zcd.ssh.dao.impl.DepartmentDaoImpl.save(DepartmentDaoImpl.java:16)
        at com.zcd.ssh.test.SSHTest.testCRUD(SSHTest.java:42)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

    现在先把我猜测的原因写下来,之后如果有新发现再来这里更新这篇文章。

    一、当我整合 Spring 和 Hibernate 的时候先配置好 IOC 容器的 applicationContext.xml 文件中的部分内容,创建实体类 Employee 类和Department类, Dao层的EmployeeDao类和DepartmentDao类等等,进行JUnit测试。

    ==========================================================================

    applicationContext.xml 文件的部分内容。连接数据库需要的 db.properties 文件就不贴出来。

    <!-- 扫描包 -->
        <context:component-scan base-package="com.zcd.ssh"></context:component-scan>
        
        <!-- 导入资源文件: db.properties文件 -->
        <context:property-placeholder location="classpath:db.properties"/>
        
        <!-- 配置数据源,使用c3p0数据源 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="user" value="${user}"></property>
            <property name="password" value="${password}"></property>
            <property name="driverClass" value="${driverClass}"></property>
            <property name="jdbcUrl" value="${jdbcUrl}"></property>
            <property name="maxPoolSize" value="${maxPoolSize}"></property>
            <property name="minPoolSize" value="${minPoolSize}"></property>
            <property name="initialPoolSize" value="${initialPoolSize}"></property>
            <property name="acquireIncrement" value="${acquireIncrement}"></property>
        </bean>
        
        <!-- 配置SessionFactory 使用LocalSessionFactoryBean-->
        <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource"></property>
            <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
            <property name="mappingLocations" value="classpath:com/zcd/ssh/beans/*.hbm.xml"></property>
        </bean>

    ==========================================================================

    BaseDao类

    /**
     * 其他Dao类通用的方法。
     * @author ZCD
     */
    @Repository
    public class BaseDao
    {
        @Autowired
        private SessionFactory sessionFactory;
        
        /**
         * 获取当前线程的Session。
         * @return 返回当前线程的Session
         */
        public Session getSession()
        {
            return sessionFactory.getCurrentSession();
        }
    }

    ==========================================================================

    DepartmentDaoImpl类。

    @Repository
    public class DepartmentDaoImpl extends BaseDao implements DepartmentDao
    {
        @Override
        public boolean save(Department department)
        {
            getSession().save(department);
            
            return true;//暂时返回true。
        }
    
        @Override
        public boolean delete(Integer id)
        {
            String hql = "DELETE FROM Department d WHERE d.id = ?";
            
            getSession().createQuery(hql).setInteger(0, id);
            
            return true; //暂时返回true。
        }
    
        @Override
        public Department getDepartment(Integer id)
        {
            String hql = "FROM Department d WHERE d.id = ?";
            
            Department department = (Department)getSession().createQuery(hql).setInteger(0, id);
            
            return department;
        }
    
        @Override
        public ArrayList<Department> getDepartments()
        {
            String hql = "FROM Department d";
            
            ArrayList<Department> departments = (ArrayList<Department>)getSession().createQuery(hql).list();
            
            return departments;
        }
    }

    ==========================================================================

    @Repository
    public class EmployeeDaoImpl extends BaseDao implements EmployeeDao
    {
    
        @Override
        public boolean save(Employee employee)
        {
            getSession().save(employee);
            
            return true; //暂时返回true。
        }
    
        @Override
        public boolean delete(Integer id)
        {
            String hql = "DELETE FROM Employee e WHERE e.id = id";
            
            getSession().createQuery(hql).setInteger(0, id).executeUpdate();
            
            return true; //暂时返回true。
        }
    
        @Override
        public Employee getEmployee(Integer id)
        {
            String hql = "FROM Employee e WHERE e.id = ?";
            
            Employee employee = (Employee) getSession().createQuery(hql).setInteger(0, id).uniqueResult();
            
            return employee;
        }
    
        @Override
        public ArrayList<Employee> getEmployees()
        {
            String hql = "FROM Employee e";
            
            ArrayList<Employee> employees = (ArrayList<Employee>) getSession().createQuery(hql).list();
            
            return employees;
        }
    }

    ==========================================================================

    进行单元测试时出现了异常。

    public class SSHTest
    {
        private ApplicationContext ac;
        private DataSource dataSource;private DepartmentDao departmentDao;
        
        
        {
            ac = new ClassPathXmlApplicationContext("applicationContext.xml");
            
            dataSource = ac.getBean(DataSource.class);
            
            departmentDao = ac.getBean(DepartmentDao.class);
        }
        
        /*
         * 测试是否能正常连接数据库,并且自动生成数据表。
         */
        @Test
        public void testConnection() throws SQLException
        {
            System.out.println(dataSource.getConnection().getClass().getName());
        }
        
        /*
         * 测试DepartmentDao类的增删改查功能。
    * 当我测试一下保存一个部门对象时,就抛出了上述异常。
    */ @Test public void testCRUD() { departmentDao.save(new Department(102, "财务部")); } }

    ==========================================================================

    二、当我完善了我的 applicationContext.xml文件,并且创建Service层等等后,在进行上述单元测试时就能够正常执行操作。

    在applicationContext.xml中添加了一下内容。

    <!-- 使用Spring的声明式事务 -->
        
        <!-- 1、配置事务管理器 -->
        <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory"></property>
        </bean>
        
        <!-- 2、配置事务通知 -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="get*" read-only="true"/>
                <tx:method name="*"/>
            </tx:attributes>
        </tx:advice>
        
        <!-- 3、配置事务切点,并且将事务切点和事务通知关联起来。 -->
        <aop:config>
            <aop:pointcut expression="execution(* com.zcd.ssh.service.*.*(..))" id="txPointcut"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
        </aop:config>

    ==========================================================================

    Service层的 OperateDepartmentService 类其实和Dao层的 DepartmentDao 类基本没区别。为了使这个项目的结构完整才添加了。

    @Service
    public class OperateDepartmentService
    {
        @Autowired
        private DepartmentDao departmentDao;
        
        /**
         * 保存部门对象。保存新建的部门或修改的部门对象。
         * @param department 被保存的部门对象。
         * @return 保存成功返回true,否则返回false。
         */
        public boolean save(Department department)
        {
            boolean success = departmentDao.save(department);
            
            return success;
        }
        
        /**
         * 根据部门id删除部门对象。
         * @param id 部门id.
         * @return 删除成功返回true,否则返回false。
         */
        public boolean delete(Integer id)
        {
            boolean success = departmentDao.delete(id);
            
            return success;
        }
        
        /**
         * 根据部门id查询部门对象。
         * @param id 部门id。
         * @return 返回一个部门对象。
         */
        public Department getDepartment(Integer id)
        {
            Department department = departmentDao.getDepartment(id);
            
            return department;
        }
        
        /**
         * 查询所有部门对象。
         * @return 返回所有部门对象。
         */
        public ArrayList<Department> getDepartments()
        {
            ArrayList<Department> departments = departmentDao.getDepartments();
            
            return departments;
        }
    }

    ==========================================================================

    再次进行单元测试就能正常进行操作了。

    @Test
        public void testCRUD()
        {
            operateDepartmentService.save(new Department(101, "销售部"));
        }

     ==========================================================================

                                        新发现

     ==========================================================================

     三、在 Spring 整合 Hibernate 后进行单元测试什么都没有出现问题。但是加入SpringMVC后感觉自己写的代码都没有错误。

     ==========================================================================

    下面是SpringMVC的配置文件。

        <!-- 扫描包 -->
        <context:component-scan base-package="com.zcd.ssh" use-default-filters="true">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
        </context:component-scan>
        
        <!-- 配置视图解析器 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/views/"></property>
            <property name="suffix" value=".jsp"></property>
        </bean>
        
        <mvc:default-servlet-handler/>
        
        <mvc:annotation-driven></mvc:annotation-driven>

     ==========================================================================

    web.xml 文件。

    <!-- 配置contextConfigLocation -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </context-param>
    
        <!-- Bootstraps the root web application context before servlet initialization -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        
        <!-- 配置DispatcherServlet -->
        <servlet>
            <servlet-name>springDispatcherServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        
        <servlet-mapping>
            <servlet-name>springDispatcherServlet</servlet-name>
            <!-- 拦截所有请求 -->
            <url-pattern>/</url-pattern>
        </servlet-mapping>
        
        <filter>
            <filter-name>HiddenHttpMethodFilter</filter-name>
            <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>HiddenHttpMethodFilter</filter-name>
            <!-- 拦截所有请求 -->
            <url-pattern>/*</url-pattern>
        </filter-mapping>

     ==========================================================================

    jsp页面的请求。

    <a href="employees">所有员工</a>

     ==========================================================================

    @Controller
    public class EmployeeController
    {
        @Autowired
        private OperateEmployeeService employeeService;
        
        @RequestMapping(value="/employees", method=RequestMethod.GET)
        public String list(Map<String, Object> map)
        {
            ArrayList<Employee> employees = employeeService.getEmployees();
            map.put("employees", employees);
            
            return "employees";
        }
    }

    我以上的代码感觉都没有问题。但却总是抛出异常。真正的原因就是加入SpringMVC后,我又配置了一个SpringMVC的IOC容器文件:springmvc.xml 。此时有两个IOC容器的配置文件。一个是SpringMVC的,一个是Spring的。这两个文件都对bean进行了自动扫描。

    springmvc.xml 文件扫描的代码如下。这里是只扫描@Controller 注解的类。

        <!-- 扫描包 -->
        <context:component-scan base-package="com.zcd.ssh">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
        </context:component-scan>

    applicationContext.xml 文件的扫描的代码如下。这是除了@Controller 注解的类都扫描。看似很完美。刚好所有的bean都扫描完了,每个bean都只会创建一个实例。

        <!-- 扫描包 -->
        <context:component-scan base-package="com.zcd.ssh">
            <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
        </context:component-scan>

    但是真的只会创建一个实例吗?在每个bean的无参构造器都打印一句话,然后运行程序。发现,除了 @Controller 注解的类,其他的类都创建了两个实例。也就是说我们在Spring IOC容器里声明的SessionFactory这个bean也创建了两个实例。问题可能就是因为创建了两个SessionFactory的实例,所以不能获取当前线程的Session。

    此时只要在springmvc.xml扫描<context:component-scan>节点加上use-default-filters="false" 这个属性。就可以了。因为不加这个它还是会使用默认的过滤器。还是会扫描到其他的Spring IOC容器中的bean。具体修改如下

        <!-- 扫描包 --><!-- 加上 use=default-filters="false" 这个属性就可以了-->
        <context:component-scan base-package="com.zcd.ssh" use-default-filters="false">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
        </context:component-scan>

    ====================================================================

    2017-09-25

    四、总结

           观察了一下,会出现上述异常原因:

      一、因为没有通过Service层来访问数据库,更准确的说没有使用声明式事务。因为Hibernate 使用Spring的声明式事务,事务的切点是在Service层里的所有类的所有方法。所以如果直接通过Dao层操作数据库就相当于没有使用事务,如果事务切点配置错误也有可能出现异常。还有,如果一个业务层(Service层)中的某一个类已经使用@Service注解,但是事务切点没有作用在这个类的方法上,那么也就相当于没有使用事务,也会出现上述异常。

    以上猜测不知道正不正确,反正异常消失了。^o^

  • 相关阅读:
    Jersey的异常处理
    REST响应处理
    jersey REST的接口简述
    Firebird 同一字段的多行合并为一行
    Firebird/InterBase内置函数使用说明
    发布FireBird数据库所需要DLL文件
    unidac连接FireBird数据库
    打开与关闭Linux防火墙
    Linux FTP 命令
    Linux 命令修改系统时间
  • 原文地址:https://www.cnblogs.com/GooPolaris/p/7138216.html
Copyright © 2011-2022 走看看