实现技术:项目中通过Springboot整合quartz,使用spring-boot-starter-quartz实现可配置定时任务。
场景:定时任务执行需要使用到一些其他的参数,在创建定时任务时将参数设置到jobDataMap中并持久化到`QRTZ_JOB_DETAILS`表中的`JOB_DATA`字段,该字段为blob类型,前端展示定时任务信息是需要展示到参数数据
这里不介绍怎么使用spring-boot-starter-quartz,非常简单,需要demo,私信一下
踩坑前:
刚开始并没有出现什么问题,几天后通过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
将得到结果逐一去解析jobData字段
List 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
通过描述查询所有定时任务
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后后期不应随便修改。