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后后期不应随便修改。

 
 

 

 

 

发布了14 篇原创文章 · 获赞 16 · 访问量 3054

猜你喜欢

转载自blog.csdn.net/haohao_ding/article/details/103912707