在工做时遇到一种麻烦的问题,我在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。