关于hibernate Transactional注解无效的解决办法

在工作时遇到一种麻烦的问题,我在spring-quartz服务中调用service的方法,该方法主要是查询操作,但始终报错,下面我贴出代码以及配置:

首先是定式服务调用service方法 DoGoodsSync:

    public void doGoodsSync() {
        List<Map<String, Object>> goodsList = remoteDataSourceDao.getGoodsList();
        for (Map item : goodsList) {
            saveItem(item);
        }
    }


    public void saveItem(Map item){
        if (goodsSyncDao.specExist(Long.valueOf(item.get("spec_id").toString()))) {
            goodsSyncDao.saveSpec(Long.valueOf(item.get("goods_id").toString()),Long.valueOf(item.get("spec_id").toString()),
                    (String) item.get("goods_name"), PHPSerializeUtil.unserializeValue((String) item.get("spec_name")),
                    PHPSerializeUtil.unserializeValue((String) item.get("spec_goods_spec")),(String) item.get("spec_goods_serial"));
        }
    }

Dao层  GoodsSyncDao:

public boolean specExist(Long goods_spec_id) {
        return existsSQLQuery("select 1 from hcj_goods_spec where spec_id = ?",goods_spec_id);
    }
    public void saveSpec(Long goods_id,Long spec_id,String goods_name,String spec_name,String spec_goods_spec,String spec_goods_serial) {
        String insert = "insert into hcj_goods_spec values (seq_hcj_goods_spec.nextval,?,?,?,?,?,?)";
        createSQLQuery(insert,goods_id,spec_id,goods_name,spec_name,spec_goods_spec,spec_goods_serial).executeUpdate();
    }

spring配置信息:

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
       default-lazy-init="true">
    <bean id="applicationContext" class="org.apache.axis2.extensions.spring.receivers.ApplicationContextHolder"
          lazy-init="false"></bean>
    <bean class="com.huicj.jxc.util.ApplicationInstance" lazy-init="false"></bean>
    <!--扫描组件-->
    <context:component-scan base-package="com.huicj"/>
    <!--读取配置文件-->
    <bean id="configBean" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:dataSource.properties</value>
            </list>
        </property>
    </bean>

    <!-- 该 BeanPostProcessor 将自动起作用,对标注 @Autowired 的 Bean 进行自动注入 -->
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

    <!--数据源-->
    <!--<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">-->
    <!--<property name="driverClassName" value="${connection.driver_class}"/>-->
    <!--<property name="url" value="${connection.url}"/>-->
    <!--<property name="username" value="${connection.username}"/>-->
    <!--<property name="password" value="${connection.password}"/>-->
    <!--</bean>-->
    <bean id="dataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource">
        <property name="driver" value="${connection.driver_class}"/>
        <property name="driverUrl" value="${connection.url}"/>
        <property name="user" value="${connection.username}"/>
        <property name="password" value="${connection.password}"/>
        <!-- 最大连接数(默认20个) -->
        <property name="maximumConnectionCount" value="50"/>
        <!-- 最小连接数(默认5个) -->
        <property name="minimumConnectionCount" value="5"/>
        <!-- alias 属性可以覆盖默认的别名 -->
        <property name="alias" value="autumn"/>
        <!-- trace为true,记录数据库每一步操作 -->
        <property name="trace" value="true"/>
        <!-- proxool自动侦察各个连接状态的时间间隔(毫秒),侦察到空闲的连接就马上回 收,超时的销毁 (默认30秒) -->
        <!--<property name="houseKeepingSleepTime"> <value>90000</value> </property> -->
        <!-- 最少保持的空闲连接数 (默认5个) -->
        <property name="prototypeCount" value="5"/>
        <!--verbose:If this is true then we start logging a lot of stuff everytime we serve a connection and everytime the house keeper and prototyper run. Be prepared for a lot of debug! -->
        <property name="verbose" value="true"/>
    </bean>

    <!-- Hibernate配置 -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <!--<prop key="hibernate.connection.isolation">${hibernate.isolation}</prop>-->
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                <prop key="hibernate.max_fetch_depth">${hibernate.max_fetch_depth}</prop>
                <prop key="hibernate.connection.release_mode">after_transaction</prop>
                <!--<prop key="hibernate.hbm2ddl.auto"></prop>-->
            </props>
        </property>
        <!--注入系统实体-->
        <property name="packagesToScan">
            <list>
                <value>com.huicj.jxc</value>
            </list>
        </property>

    </bean>

    <!--事务管理器-->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
        <property name="nestedTransactionAllowed" value="true"/>
    </bean>

    <!--将该事务管理器绑定到@Transaction-->
    <tx:annotation-driven transaction-manager="transactionManager"/>


    <!--交易系统正式库数据源-->
    <bean id="dataSource1" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${connection.driver_class_r}"/>
        <property name="url" value="${connection.url_r}"/>
        <property name="username" value="${connection.username_r}"/>
        <property name="password" value="${connection.password_r}"/>
    </bean>

    <!--jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource">
            <ref local="dataSource1"/>
        </property>
    </bean>
    <!--事务管理器-->
   <!-- <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref local="dataSource1"/>
        </property>
    </bean>-->

    <!--<tx:advice id="txadvice1" transaction-manager="transactionManager1">
        <tx:attributes>
            <tx:method name="get*" propagation="NOT_SUPPORTED"/>
            <tx:method name="bgcgdSync*" propagation="REQUIRED" rollback-for="Throwable"/>
        </tx:attributes>
    </tx:advice>-->

</beans>

就在定时服务启动时,specExist方法报错,错误信息:

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
    at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:700)
    at net.ruixin.orm.hibernate.SimpleHibernateTemplate.getSession(SimpleHibernateTemplate.java:43)
    at net.ruixin.orm.hibernate.SimpleHibernateTemplate.createSQLQuery(SimpleHibernateTemplate.java:341)
    at net.ruixin.orm.hibernate.SimpleHibernateTemplate.existsSQLQuery(SimpleHibernateTemplate.java:367)
    at com.huicj.jxc.dao.goods.GoodsSyncDao.specExist(GoodsSyncDao.java:14)
    at com.huicj.jxc.service.taskdeal.DoGoodsSync.doGoodsSync(DoGoodsSync.java:25)
    at com.huicj.jxc.quartz.GoodsSyncJob.executeInternal(GoodsSyncJob.java:17)
    at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:113)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)

一月 05, 2015 11:17:00 上午 org.quartz.core.ErrorLogger schedulerError
严重: Job (DEFAULT.goodsSyncJob threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exception: org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here]
    at org.quartz.core.JobRunShell.run(JobRunShell.java:234)
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)
Caused by: org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
    at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
    at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:700)
    at net.ruixin.orm.hibernate.SimpleHibernateTemplate.getSession(SimpleHibernateTemplate.java:43)
    at net.ruixin.orm.hibernate.SimpleHibernateTemplate.createSQLQuery(SimpleHibernateTemplate.java:341)
    at net.ruixin.orm.hibernate.SimpleHibernateTemplate.existsSQLQuery(SimpleHibernateTemplate.java:367)
    at com.huicj.jxc.dao.goods.GoodsSyncDao.specExist(GoodsSyncDao.java:14)
    at com.huicj.jxc.service.taskdeal.DoGoodsSync.doGoodsSync(DoGoodsSync.java:25)
    at com.huicj.jxc.quartz.GoodsSyncJob.executeInternal(GoodsSyncJob.java:17)
    at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:113)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
    ... 1 more

这就是一个简单的查询,以前写查询是也没有遇到过这种情况,后来翻阅资料,发现以前的代码在web.xml中已经配置了HibernateOpenSessionView

<filter>
        <filter-name>hibernateOpenSessionInViewFilter</filter-name>
        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
        <init-param>
            <param-name>sessionFactoryBeanName</param-name>
            <param-value>sessionFactory</param-value>
        </init-param>
        <init-param>
            <param-name>singleSession</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter

<filter-mapping>
        <filter-name>hibernateOpenSessionInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

这个filter是保持在整个请求中,始终保持hibernate session的open状态,所以查询方法如果没有@Transactional注解会自动加上@Transactional(readOnly = true)的注解,而定时器是不经过filter的,只能乖乖在方法上加上@Transactional。

    @Transactional
    public void saveItem(Map item){
        if (goodsSyncDao.specExist(Long.valueOf(item.get("spec_id").toString()))) {
            goodsSyncDao.saveSpec(Long.valueOf(item.get("goods_id").toString()),Long.valueOf(item.get("spec_id").toString()),
                    (String) item.get("goods_name"), PHPSerializeUtil.unserializeValue((String) item.get("spec_name")),
                    PHPSerializeUtil.unserializeValue((String) item.get("spec_goods_spec")),(String) item.get("spec_goods_serial"));
        }
    }

问题来了,加上注解以后依然报错,代码跟踪,发现时getCurrentSession()报错,还是没有事物debug后发现Transactional注解根本就是无效的,没办法了继续debug,发现spring对@Transactional的切面是通过代理对象封装并环绕增强来实现的。以下是摘自iteye博文的内容:

 

在网络应用中,我们几乎总是需要严密控制我们spring应用中的数据库事务的启动和结束。为做到这一点,我们或多或少都已经通过AOP来做这些事情。但一般都是在XXService、或XXController中封装处理请求的方法。

Spring有内置注解支持,因而你可以简单地通过@Transactional 注解你的方法或类,并在配置文件中添加<tx:annotation-driven />,把相应方法封装于一个事务之中。这听起来好像很简单

 

但是,所有这些都是通过Spring 的代理对象封装并环绕增强原来的被注解@Transactional 的类来实现的,但这种做法只有当事务方法是public的、并且是被代理类外部调用的情况下才会正常工作(可以参看Spring 事务处理模型图就明白,否则代理对象自己调用自己就会绕过对它的环绕事务增强,其他切面增强也是一样)。这就很不爽了,意味着你不能在XXService或XXController内部串联处理一些具各自独立的事务,例如在XXController调用handleRequestInternal。解决的办法是使用全功能完整成熟的AspectJ织入。AspectJ织入方式同样支持@Transactional (其他自定义注解也行^_^),同时能被织入到所有方法中(不只是public),并且在内不外部都可以。

AspectJ有三种方式织入事务代码

a.编译时(CTW). 拥有所有需要的源代码

b.运行时(LTW).

c.字节码织入(BTW).没有织入目标的源代码(如只有jar)

这里我们使用CTW的方式

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop">  
  
    <aop:spring-configured>  
  
    <bean id="annotationTransactionAspect" factory-method="aspectOf"  class="org.springframework.transaction.aspectj.AnnotationTransactionAspect">  
        <property name="transactionManager" ref="transactionManager"></property>  
    </bean>  
  
    <!-- the rest of your application here -->  
</beans>

编译后把你的war发布到任何web容器中他就能工作了,所有注解了@Transactional  的方法(各种可见度)都能正常的处理事务,如果是类级@Transactional  注解,该类的就所有public方法都有事务。而且被注解类的内外都能调用,这样,你完全可以撇开spring那麻烦的代理了,还补充一句,如果你使用了 DWR做为你的ajax后台的话,服务层如果是JDK代理的话,将无法工作。只能使用Cglib方式的代理。还有很多情况,Spring 代理模式和其他一些框架配合工作的时候会有问题,全部使用AspectJ,撇开Spring 代理模式,你会觉得真的很free。



你可能感兴趣的:(关于hibernate Transactional注解无效的解决办法)