Spring + Hibernate + Quartz:动态作业
Spring + Hibernate + Quartz: Dynamic Job
我想使用 Quartz、Spring 和 Hibernate 动态创建作业。用户与 Web 服务交互以创建此 class 的 工作 :
public class StartJobSpring extends QuartzJobBean {
private String jobId;
private String jobType;
@Autowired
private NoaJobInstancesDAO njiDAO;
@Transactional
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getMergedJobDataMap();
// some logic
njiDAO.create(instanceUUID, noaJob.getNoaJob(jobId), jobType);
}
}
NoaJobInstancesDAO 是一个简单的 DAO class,它利用了 Hibernate 的 EntityManager:
@Repository
public class NoaJobInstancesDAOHibImpl implements NoaJobInstancesDAO {
@PersistenceContext
private EntityManager entityManager;
@Override
@Transactional
public NoaJobInstanceJPA create(NoaJobInstanceJPA entity) {
entityManager.persist(entity);
return entity;
}
@Override
public void create(String instance_uuid, NoaJobJPA job, String job_type) {
NoaJobInstanceJPA entity = new NoaJobInstanceJPA(instance_uuid, job,
job_type, "CREATED", null, null, "", "N", "N");
this.create(entity);
}
}
问题是当这个作业触发时,抛出异常:
javax.persistence.TransactionRequiredException: No transactional EntityManager available
我不明白为什么!
我以这种方式在 Manager class
中安排工作
JobDetail job = newJob(StartJobSpring.class).withIdentity(//anId)
.setJobData(//aJobMap).build();
getScheduler().getObject().scheduleJob(job, trigger);
调度程序连接到管理器的位置为
@Autowired
private ApplicationContext applicationContext;
@Bean
SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource, JpaTransactionManager transactionManager) {
SchedulerFactoryBean bean = new SchedulerFactoryBean();
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
bean.setJobFactory(jobFactory);
bean.setTransactionManager(transactionManager);
return bean;
}
class自动装配SpringBeanJobFactory与Autowiring相同。
在我看来,调度程序接线有问题。事实上,我不明白如何检索应用程序上下文。
EDIT1: 应用程序上下文似乎已正确实例化。问题不可能在那里。
EDIT2: 我正在使用单个配置 bean(不是 xml 文件)。这里主要方法:
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("package");
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.OracleDialect");
jpaProperties.put("hibernate.show_sql", "false");
jpaProperties.put("hibernate.hbm2ddl.auto", "update");
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
@Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
@Bean
public NoaJobInstancesDAO noaJobInstancesDAO() {
NoaJobInstancesDAOHibImpl noaJobInstancesDAO = new NoaJobInstancesDAOHibImpl();
return noaJobInstancesDAO;
}
您处于 spring 托管上下文中,您尝试使用 @PersistentContext 访问 EntityManager,这是一个 javax.persistence 注释。尝试使用 @Autowire 自动装配 EntityManagerFactory bean,我假设您在 spring-context.xml 中配置它并使用 entityManagerFactory.createEntityManager() 为您提供 spring 托管实体管理器,它将由 spring 包装并在您定义的事务管理器中
简短的解决方案:让Spring通过工厂创造你的工作。
长解: 这里是长说明。我通过导入 xml 配置文件修改了我的配置文件:
<bean name="complexJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="jobs.StartJob" />
<property name="durability" value="true" />
</bean>
<bean id="cronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="complexJobDetail" />
<property name="cronExpression" value="0/5 * * ? * SAT-SUN" />
</bean>
这样,您就有了一个 spring 生产作业实例的工厂。现在,这是我更新的 java 配置 class
@ImportResource({"spring-quartz-context.xml"})
public class BeanConfig {
//autowired from xml
@Autowired JobDetailFactoryBean jobDetailFactory;
@Autowired CronTriggerFactoryBean cronTriggerFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean(LocalContainerEntityManagerFactoryBean entityManagerFactory) {
SchedulerFactoryBean bean = new SchedulerFactoryBean();
bean.setApplicationContextSchedulerContextKey("applicationContext");
bean.setSchedulerName("MyScheduler");
//used for the wiring
Map<String, Object> schedulerContextAsMap = new HashMap<String, Object>();
schedulerContextAsMap.put("noaJobDAO", noaJobDAO());
schedulerContextAsMap.put("noaJobInstancesDAO", noaJobInstancesDAO());
schedulerContextAsMap.put("esbClient", this.esbClient());
bean.setSchedulerContextAsMap(schedulerContextAsMap);
bean.setQuartzProperties(quartzProperties());
return bean;
}
@Bean
public Properties quartzProperties() {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
Properties properties = null;
try {
propertiesFactoryBean.afterPropertiesSet();
properties = propertiesFactoryBean.getObject();
} catch (IOException e) {
log.warn("Cannot load quartz.properties.");
}
return properties;
}
// other beans (as included in the question)
}
我使用一个 bean 来安排作业。所以首先我在这个 bean 中注入工厂。然后当我想安排工作时,我使用这个片段
JobDetail job = jobDetailFactory.getObject();
Trigger trigger = cronTriggerFactory.getObject();
scheduler.schedule(job, trigger);
作业我也修改了class
@Service
public class StartJob extends QuartzJobBean {
// the DAO
private NoaJobInstancesDAO njiDAO;
public void executeInternal(JobExecutionContext context)
throws JobExecutionException {
init(context.getJobDetail().getJobDataMap(), context.getScheduler()
.getContext());
// some logic here
njiDAO.create(params);
}
private void init(JobDataMap jobContextMap,
SchedulerContext schedulerContext) {
// some initialization using the job data map, not interesting for DAOs
// row that inject the correct DAO
this.njiDAO = (NoaJobInstancesDAO) schedulerContext
.get("noaJobInstancesDAO");
}
}
问题已解决!
我这样做解决了这个问题:
Job中(必须获取接口):
public class SchedulerJob extends QuartzJobBean {
public void executeInternal(JobExecutionContext context)
throws JobExecutionException {
try{
<YOUR_BEAN_DAO_INTERFACE_OBJECT> = ((ApplicationContext) context.getJobDetail().getJobDataMap().get("applicationContext")).get("<YOUR_BEAN_DAO_INTERFACE_ID>");
} catch (Exception e ){
e.printStackTrace();
return;
}
}
}
在应用程序的 .xml 上下文中:
还需要在这个xml中声明为bean:
<!-- Spring Quartz Scheduler job -->
<bean name="schedulerJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="<PATH_OF_YOUR_CLASS_JOB>.SchedulerJob" />
<property name="applicationContextJobDataKey" value="applicationContext" />
</bean>
<!-- Cron Trigger, run every 10 seconds -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="schedulerJob" />
<property name="cronExpression" value="0/10 * * * * ?" />
</bean>
<!-- DI -->
<bean id="scheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="schedulerJob" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
我想使用 Quartz、Spring 和 Hibernate 动态创建作业。用户与 Web 服务交互以创建此 class 的 工作 :
public class StartJobSpring extends QuartzJobBean {
private String jobId;
private String jobType;
@Autowired
private NoaJobInstancesDAO njiDAO;
@Transactional
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
JobKey key = context.getJobDetail().getKey();
JobDataMap dataMap = context.getMergedJobDataMap();
// some logic
njiDAO.create(instanceUUID, noaJob.getNoaJob(jobId), jobType);
}
}
NoaJobInstancesDAO 是一个简单的 DAO class,它利用了 Hibernate 的 EntityManager:
@Repository
public class NoaJobInstancesDAOHibImpl implements NoaJobInstancesDAO {
@PersistenceContext
private EntityManager entityManager;
@Override
@Transactional
public NoaJobInstanceJPA create(NoaJobInstanceJPA entity) {
entityManager.persist(entity);
return entity;
}
@Override
public void create(String instance_uuid, NoaJobJPA job, String job_type) {
NoaJobInstanceJPA entity = new NoaJobInstanceJPA(instance_uuid, job,
job_type, "CREATED", null, null, "", "N", "N");
this.create(entity);
}
}
问题是当这个作业触发时,抛出异常:
javax.persistence.TransactionRequiredException: No transactional EntityManager available
我不明白为什么! 我以这种方式在 Manager class
中安排工作JobDetail job = newJob(StartJobSpring.class).withIdentity(//anId)
.setJobData(//aJobMap).build();
getScheduler().getObject().scheduleJob(job, trigger);
调度程序连接到管理器的位置为
@Autowired
private ApplicationContext applicationContext;
@Bean
SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource, JpaTransactionManager transactionManager) {
SchedulerFactoryBean bean = new SchedulerFactoryBean();
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
bean.setJobFactory(jobFactory);
bean.setTransactionManager(transactionManager);
return bean;
}
class自动装配SpringBeanJobFactory与Autowiring相同。
在我看来,调度程序接线有问题。事实上,我不明白如何检索应用程序上下文。
EDIT1: 应用程序上下文似乎已正确实例化。问题不可能在那里。
EDIT2: 我正在使用单个配置 bean(不是 xml 文件)。这里主要方法:
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("package");
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.OracleDialect");
jpaProperties.put("hibernate.show_sql", "false");
jpaProperties.put("hibernate.hbm2ddl.auto", "update");
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
@Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
@Bean
public NoaJobInstancesDAO noaJobInstancesDAO() {
NoaJobInstancesDAOHibImpl noaJobInstancesDAO = new NoaJobInstancesDAOHibImpl();
return noaJobInstancesDAO;
}
您处于 spring 托管上下文中,您尝试使用 @PersistentContext 访问 EntityManager,这是一个 javax.persistence 注释。尝试使用 @Autowire 自动装配 EntityManagerFactory bean,我假设您在 spring-context.xml 中配置它并使用 entityManagerFactory.createEntityManager() 为您提供 spring 托管实体管理器,它将由 spring 包装并在您定义的事务管理器中
简短的解决方案:让Spring通过工厂创造你的工作。
长解: 这里是长说明。我通过导入 xml 配置文件修改了我的配置文件:
<bean name="complexJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="jobs.StartJob" />
<property name="durability" value="true" />
</bean>
<bean id="cronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="complexJobDetail" />
<property name="cronExpression" value="0/5 * * ? * SAT-SUN" />
</bean>
这样,您就有了一个 spring 生产作业实例的工厂。现在,这是我更新的 java 配置 class
@ImportResource({"spring-quartz-context.xml"})
public class BeanConfig {
//autowired from xml
@Autowired JobDetailFactoryBean jobDetailFactory;
@Autowired CronTriggerFactoryBean cronTriggerFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean(LocalContainerEntityManagerFactoryBean entityManagerFactory) {
SchedulerFactoryBean bean = new SchedulerFactoryBean();
bean.setApplicationContextSchedulerContextKey("applicationContext");
bean.setSchedulerName("MyScheduler");
//used for the wiring
Map<String, Object> schedulerContextAsMap = new HashMap<String, Object>();
schedulerContextAsMap.put("noaJobDAO", noaJobDAO());
schedulerContextAsMap.put("noaJobInstancesDAO", noaJobInstancesDAO());
schedulerContextAsMap.put("esbClient", this.esbClient());
bean.setSchedulerContextAsMap(schedulerContextAsMap);
bean.setQuartzProperties(quartzProperties());
return bean;
}
@Bean
public Properties quartzProperties() {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
Properties properties = null;
try {
propertiesFactoryBean.afterPropertiesSet();
properties = propertiesFactoryBean.getObject();
} catch (IOException e) {
log.warn("Cannot load quartz.properties.");
}
return properties;
}
// other beans (as included in the question)
}
我使用一个 bean 来安排作业。所以首先我在这个 bean 中注入工厂。然后当我想安排工作时,我使用这个片段
JobDetail job = jobDetailFactory.getObject();
Trigger trigger = cronTriggerFactory.getObject();
scheduler.schedule(job, trigger);
作业我也修改了class
@Service
public class StartJob extends QuartzJobBean {
// the DAO
private NoaJobInstancesDAO njiDAO;
public void executeInternal(JobExecutionContext context)
throws JobExecutionException {
init(context.getJobDetail().getJobDataMap(), context.getScheduler()
.getContext());
// some logic here
njiDAO.create(params);
}
private void init(JobDataMap jobContextMap,
SchedulerContext schedulerContext) {
// some initialization using the job data map, not interesting for DAOs
// row that inject the correct DAO
this.njiDAO = (NoaJobInstancesDAO) schedulerContext
.get("noaJobInstancesDAO");
}
}
问题已解决!
我这样做解决了这个问题:
Job中(必须获取接口):
public class SchedulerJob extends QuartzJobBean {
public void executeInternal(JobExecutionContext context)
throws JobExecutionException {
try{
<YOUR_BEAN_DAO_INTERFACE_OBJECT> = ((ApplicationContext) context.getJobDetail().getJobDataMap().get("applicationContext")).get("<YOUR_BEAN_DAO_INTERFACE_ID>");
} catch (Exception e ){
e.printStackTrace();
return;
}
}
}
在应用程序的 .xml 上下文中: 还需要在这个xml中声明为bean:
<!-- Spring Quartz Scheduler job -->
<bean name="schedulerJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="<PATH_OF_YOUR_CLASS_JOB>.SchedulerJob" />
<property name="applicationContextJobDataKey" value="applicationContext" />
</bean>
<!-- Cron Trigger, run every 10 seconds -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="schedulerJob" />
<property name="cronExpression" value="0/10 * * * * ?" />
</bean>
<!-- DI -->
<bean id="scheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="schedulerJob" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>