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後後期不應隨便修改。


 
 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章