Springboot 整合 quartz,使用spring-boot-starter-quartz获取jobDataMap数据遇到的坑

实现技术:项目中通过Springboot整合quartz,使用spring-boot-starter-quartz实现可配置定时任务。

场景:定时任务执行需要使用到一些其他的参数,在创建定时任务时将参数设置到jobDataMap中并持久化到`QRTZ_JOB_DETAILS`表中的`JOB_DATA`字段,该字段为blob类型,前端展示定时任务信息是需要展示到参数数据

 

这里不介绍怎么使用spring-boot-starter-quartz,非常简单,需要demo,私信一下

 

踩坑前:

  1. 通过jobDetail = scheduler.getJobDetail(jobKey),得到jobDetail
  2. 通过jobDataMap = jobDetail.getJobDataMap(),得到jobDataMap
  3. 从jobDataMap获取参数

刚开始并没有出现什么问题,几天后通过jobDetail = scheduler.getJobDetail(jobKey),得到jobDetail为null,之前的定时任务都获取不到参数,但是新增加定时任务可以获取到参数。

通过各种尝试从其他对象中去获取jobDataMap,最终都得不到数据,于是考虑是否数据库中的job_data字段被置空,查询数据库发现并没有,然后通过手动执行一个定时任务发现能正常执行。

下面是执行定时任务代码

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        .
        .
        .
        .
        .
        .
    }

说明通过JobExecutionContext 对象去获取jobDetail,再获取jobDataMap是一直都能得到参数的,然后就考虑怎么能在查询的时候获取到每一个定时任务的JobExecutionContext对象 ,最后还是失败了。

最后没有办法就考虑用最笨的方法解析数据库中jobDataMap存的blob类型数据,最后问题得到了解决。

目前还不知道是什么原因造成,但是这里提供一种解决方法:

按照上面的思路,既然获取不到jobDataMap ,我们就直接从数据库里面查询出来,注意blob类型用byte[] 接收,然后从byte[]解析出需要的参数

代码实现:

查询结果添加JOB_DATA


	<select id="list" resultType="com.gbcom.datax_service.quartz.model.vo.JobAndTrigger" parameterType="String">
		SELECT
		job_details.`JOB_NAME`,
		job_details.`DESCRIPTION`,
		job_details.`JOB_GROUP`,
		job_details.`JOB_CLASS_NAME`,
		job_details.`JOB_DATA`,
		cron_triggers.`CRON_EXPRESSION`,
		cron_triggers.`TIME_ZONE_ID`,
		qrtz_triggers.`TRIGGER_NAME`,
		qrtz_triggers.`TRIGGER_GROUP`,
		qrtz_triggers.`TRIGGER_STATE`
		FROM
		`QRTZ_JOB_DETAILS` job_details
		LEFT JOIN `QRTZ_CRON_TRIGGERS` cron_triggers ON job_details.`JOB_NAME` = cron_triggers.`TRIGGER_NAME`
		AND job_details.`JOB_GROUP` = cron_triggers.`TRIGGER_GROUP`
		LEFT JOIN `QRTZ_TRIGGERS` qrtz_triggers ON qrtz_triggers.`TRIGGER_NAME` = job_details.`JOB_NAME`
		AND qrtz_triggers.`TRIGGER_GROUP` = job_details.`JOB_GROUP`
		<where>
			<if test="@org.apache.commons.lang.StringUtils@isNotBlank(description)">
				job_details.`DESCRIPTION` like CONCAT('%', #{description}, '%')
			</if>
		</where>
	</select>

将得到结果逐一去解析jobData字段

        List<JobAndTrigger> list = jobMapper.list(description);
        for (JobAndTrigger jobAndTrigger : list) {
            try {
                ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(jobAndTrigger.getJobData()));
                JobDataMap jobDataMap = (JobDataMap) objectInputStream.readObject();
                int jobType = (int) jobDataMap.get("jobType");
                String jobCode = (String) jobDataMap.get("jobCode");
                String jobOrBatchJobName = (String) jobDataMap.get("jobOrBatchJobName");
                jobAndTrigger.setJobType(jobType);
                jobAndTrigger.setJobCode(jobCode);
                jobAndTrigger.setJobOrBatchJobName(jobOrBatchJobName);
                jobAndTrigger.setJobData(null);
            } catch (IOException e) {
                log.error("读取JobDetail中Map的属性发生异常,error", e);
            } catch (ClassNotFoundException e) {
                log.error("读取JobDetail中Map的属性发生异常,error", e);
            }
        }

 

 

上面代码

1. List<JobAndTrigger> list = jobMapper.list(description)

通过描述查询所有定时任务

2. objectInputStream = new ObjectInputStream(new ByteArrayInputStream(jobAndTrigger.getJobData()))

将jobAndTrigger.getJobData()得到的byte[]转换为对象输入流

3. JobDataMap jobDataMap = (JobDataMap) objectInputStream.readObject()

读取对象,这里注意原对象为JobDataMap 

4. 从jobDataMap 获取之前放入的参数

 

补充一点:

quartz是将JobDataMap对象序列化保存到数据库中所以上面的代码只是做了个反序列而已,下面提供一个序列化和反序列化的demo吧!

import java.io.ByteArrayInputStream;   
import java.io.ByteArrayOutputStream;   
import java.io.IOException;   
import java.io.ObjectInputStream;   
import java.io.ObjectOutputStream;   
 
  
public class ObjectAndByte {   
  
    /**  
     * 对象转数组  
     * @param obj  
     * @return  
     */  
    public byte[] toByteArray (Object obj) {      
        byte[] bytes = null;      
        ByteArrayOutputStream bos = new ByteArrayOutputStream();      
        try {        
            ObjectOutputStream oos = new ObjectOutputStream(bos);         
            oos.writeObject(obj);        
            oos.flush();         
            bytes = bos.toByteArray ();      
            oos.close();         
            bos.close();        
        } catch (IOException ex) {        
            ex.printStackTrace();   
        }      
        return bytes;    
    }   
       
    /**  
     * 数组转对象  
     * @param bytes  
     * @return  
     */  
    public Object toObject (byte[] bytes) {      
        Object obj = null;      
        try {        
            ByteArrayInputStream bis = new ByteArrayInputStream (bytes);        
            ObjectInputStream ois = new ObjectInputStream (bis);        
            obj = ois.readObject();      
            ois.close();   
            bis.close();   
        } catch (IOException ex) {        
            ex.printStackTrace();   
        } catch (ClassNotFoundException ex) {        
            ex.printStackTrace();   
        }      
        return obj;    
    }   
       
    
}

一般我们将序列化后的byte[]持久化到数据库时,数据库使用blob类型,上面的代码可作为工具类,方便写入/读取序列化数据

 

现发现之前出现该问题的原因:

原因是:在配置spring.quartz.scheduler-name的时候中途修改了配置,在数据库中查询发现定时任务的SCHED_NAME与项目中配置的不匹配。

解决办法:将配置文件中的spring.quartz.scheduler-name该回与数据库相同,所以配置好spring.quartz.scheduler-name后后期不应随便修改。

 
 

 

 

 

Spring Boot整合Quartz实现cron定时任务的步骤如下: 1.在pom.xml添加依赖 ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> ``` 2.创建Job类 ``` public class MyJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 这里编写具体的定时任务 } } ``` 3.创建JobDetail和Trigger ``` @Configuration public class QuartzConfig { @Bean public JobDetail myJobDetail() { return JobBuilder.newJob(MyJob.class).withIdentity("myJob").storeDurably().build(); } @Bean public Trigger myTrigger() { return TriggerBuilder.newTrigger().forJob(myJobDetail()) .withIdentity("myTrigger").withSchedule(CronScheduleBuilder.cronSchedule("0 0 0 * * ?")).build(); } } ``` 4.启动定时任务 ``` @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Autowired private Scheduler scheduler; @Autowired private Trigger myTrigger; @Autowired private JobDetail myJobDetail; @PostConstruct public void start() throws SchedulerException { scheduler.scheduleJob(myJobDetail, myTrigger); } } ``` 这里的注解`@PostConstruct`表示在Bean初始化之后执行该方法。 5.编写具体的定时任务逻辑 ``` public class MyJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("定时任务执行了!"); } } ``` 完整的代码示例: ``` // MyJob.java public class MyJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("定时任务执行了!"); } } // QuartzConfig.java @Configuration public class QuartzConfig { @Bean public JobDetail myJobDetail() { return JobBuilder.newJob(MyJob.class).withIdentity("myJob").storeDurably().build(); } @Bean public Trigger myTrigger() { return TriggerBuilder.newTrigger().forJob(myJobDetail()) .withIdentity("myTrigger").withSchedule(CronScheduleBuilder.cronSchedule("0 0 0 * * ?")).build(); } } // Application.java @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Autowired private Scheduler scheduler; @Autowired private Trigger myTrigger; @Autowired private JobDetail myJobDetail; @PostConstruct public void start() throws SchedulerException { scheduler.scheduleJob(myJobDetail, myTrigger); } } ``` 这里的定时任务是每天凌晨0点执行一次,并打印一句话。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haohao_ding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值