實現技術:項目中通過Springboot整合quartz,使用spring-boot-starter-quartz實現可配置定時任務。
場景:定時任務執行需要使用到一些其他的參數,在創建定時任務時將參數設置到jobDataMap中並持久化到`QRTZ_JOB_DETAILS`表中的`JOB_DATA`字段,該字段爲blob類型,前端展示定時任務信息是需要展示到參數數據
這裏不介紹怎麼使用spring-boot-starter-quartz,非常簡單,需要demo,私信一下
踩坑前:
- 通過jobDetail = scheduler.getJobDetail(jobKey),得到jobDetail
- 通過jobDataMap = jobDetail.getJobDataMap(),得到jobDataMap
- 從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後後期不應隨便修改。