關於hibernate Transactional註解無效的解決辦法

在工做時遇到一種麻煩的問題,我在spring-quartz服務中調用service的方法,該方法主要是查詢操做,但始終報錯,下面我貼出代碼以及配置:java

首先是定式服務調用service方法 DoGoodsSync:web

    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:ajax

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配置信息: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方法報錯,錯誤信息:sql

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。apache

    @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中封裝處理請求的方法。session

Spring有內置註解支持,於是你能夠簡單地經過@Transactional 註解你的方法或類,並在配置文件中添加<tx:annotation-driven />,把相應方法封裝於一個事務之中。這聽起來好像很簡單app

 

可是,全部這些都是經過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。

相關文章
相關標籤/搜索