在工做時遇到一種麻煩的問題,我在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。