在google上找了很久没有合适的解决办法,通常的建议是把hibernate的lazy改成false或者直接写sql。为了调度而启用lazy显然是不划算的。自己写sql,那就弃用了hibernate,两种方式都让人难以接收。最后看到一提示,spring可以使用OpenSessionInViewFilter和hibernateInterceptor来扩展hibernate的session,避免了我们在web请求的过程中出现no session的问题。借鉴hibernateInterceptor的做法,扩展QuartzJobBean就可以实现在非web环境下使用Quartz而不会出现no session的问题。
TransactionalQuartzTask的源码
public abstract class TransactionalQuartzTask extends QuartzJobBean {
private static final Logger log = Logger
.getLogger(TransactionalQuartzTask.class);
// spring injected reference
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Most of this method is copied from the HibernateInterceptor.
*/
protected final void executeInternal(JobExecutionContext ctx)
throws JobExecutionException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
boolean existingTransaction = SessionFactoryUtils
.isSessionTransactional(session, getSessionFactory());
if (existingTransaction) {
log.info("Found thread-bound Session for TransactionalQuartzTask");
} else {
TransactionSynchronizationManager.bindResource(getSessionFactory(),
new SessionHolder(session));
}
try {
executeTransactional(ctx);
} catch (HibernateException ex) {
ex.printStackTrace();
throw ex;
} finally {
if (existingTransaction) {
log.debug("Not closing pre-bound Hibernate Session after TransactionalQuartzTask");
} else {
TransactionSynchronizationManager
.unbindResource(getSessionFactory());
SessionFactoryUtils
.releaseSession(session, getSessionFactory());
}
}
}
/**
* Implementing classes, implement this method.
*/
protected abstract void executeTransactional(JobExecutionContext ctx)
throws JobExecutionException;
}
具体的job类,继承了TransactionalQuartzTask。此处需要注意的是成员变量channelRssUtil不能通过标注@Autowired来装配,否则会报错。
public class AutoBuildChannelRssTerminal extends TransactionalQuartzTask {
private ChannelRssUtil channelRssUtil;
public ChannelRssUtil getChannelRssUtil() {
return channelRssUtil;
}
public void setChannelRssUtil(ChannelRssUtil channelRssUtil) {
this.channelRssUtil = channelRssUtil;
}
private Logger log = Logger.getLogger(getClass());
public void executeTransactional(JobExecutionContext ctx)
throws JobExecutionException {
channelRssUtil.buildChannelRss();
}
}
在spring中的配置
<bean id="buildChannelRssJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>cn.com.people.tv.pvms.system.AutoBuildChannelRssTerminal
</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="sessionFactory" value-ref="sessionFactory"></entry>
<entry key="channelRssUtil" value-ref="channelRssUtil"></entry>
</map>
</property>
</bean>
<bean id="simpleAutoBuildChannelRssTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail">
<ref bean="buildChannelRssJob" />
</property>
<property name="repeatInterval">
<value>300000</value>
</property>
</bean>