1. 程式人生 > >Quartz:高階,資料庫儲存

Quartz:高階,資料庫儲存

Pom-xml

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-freemarker</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-quartz</artifactId>
		</dependency>


		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz-jobs</artifactId>
			<version>2.2.1</version>
		</dependency>

		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.5.1</version>
		</dependency>


		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

Quartz排程所需要的依賴 quartz-jobs+c3p0,其他的可在建立SpringBoot專案時勾選jdbc、mysql、quartz、mybatis等

 

 application.properties

spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/cn?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

專案所需要連線的資料庫,cn是MySQL的庫 。com.mysql.cj.jdbc.Driver,新版連線

scheduletrigger 

-- ----------------------------
-- Table structure for `scheduletrigger`
-- ----------------------------
DROP TABLE IF EXISTS `scheduletrigger`;
CREATE TABLE `scheduletrigger` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `cron` varchar(255) DEFAULT NULL,
  `status` varchar(255) DEFAULT NULL,
  `jobName` varchar(255) DEFAULT NULL,
  `jobGroup` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of scheduletrigger
-- ----------------------------
INSERT INTO `scheduletrigger` VALUES ('1', '*/3 * * * * ?', '1', 'com.hc.quartzboot.quartz.MyJob', 'hc');
INSERT INTO `scheduletrigger` VALUES ('2', '*/10 * * * * ?', '1', 'com.hc.quartzboot.quartz.MyJobs', 'hc');

MySQL中的任務表 ,cron是表示式,jobname是任務的路徑,com.hc.quartzboot.quartz.MyJobs

quartz提供的表官方Quartz--MySQL版的表共11張 表

 quartz.properties


org.quartz.scheduler.instanceName: DefaultQuartzScheduler

org.quartz.scheduler.instanceId = AUTO

org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate


org.quartz.jobStore.useProperties:true

org.quartz.jobStore.tablePrefix:qrtz_
org.quartz.jobStore.dataSource:qzDS
org.quartz.jobStore.isClustered = true


org.quartz.dataSource.qzDS.driver:com.mysql.cj.jdbc.Driver
org.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/cn?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
org.quartz.dataSource.qzDS.user:root
org.quartz.dataSource.qzDS.password:root
org.quartz.dataSource.qzDS.maxConnections:10

 ScheduleTrigger

package com.hc.quartzboot.model;

public class ScheduleTrigger {
    private Integer id;

    private String cron;

    private String status;

    private String jobName;

    private String jobGroup;

    public ScheduleTrigger(Integer id, String cron, String status, String jobName, String jobGroup) {
        this.id = id;
        this.cron = cron;
        this.status = status;
        this.jobName = jobName;
        this.jobGroup = jobGroup;
    }

    public ScheduleTrigger() {
        super();
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getCron() {
        return cron;
    }

    public void setCron(String cron) {
        this.cron = cron;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getJobName() {
        return jobName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName;
    }

    public String getJobGroup() {
        return jobGroup;
    }

    public void setJobGroup(String jobGroup) {
        this.jobGroup = jobGroup;
    }
}

 

ScheduleTriggerMapper
package com.hc.quartzboot.mapper;

import com.hc.quartzboot.model.ScheduleTrigger;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public interface ScheduleTriggerMapper {

    @Select(" select * from ScheduleTrigger")
    List<ScheduleTrigger> queryAll();
}

MyJob  

package com.hc.quartzboot.quartz;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("任務執行中..." + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
    }
}

MyJobFactory  

package com.hc.quartzboot.quartz;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;

@Component
public class MyJobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //通過quartz框架中的建立作業的方法建立一個作業物件
        Object jobInstance = super.createJobInstance(bundle);
        capableBeanFactory.autowireBean(jobInstance); //這一步解決不能spring注入bean的問題
        return jobInstance;
    }
}

 MyJobs 

package com.hc.quartzboot.quartz;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class MyJobs implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("任務222執行中..." + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new Date()));
    }
}

QuartzConfiguration  

package com.hc.quartzboot.quartz;

import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import java.io.IOException;
import java.util.Properties;

@Configuration
public class QuartzConfiguration {

    @Autowired
    private MyJobFactory myJobFactory;  //自定義的factory

    //獲取工廠bean(獲取任務排程器工廠)
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        try {
            schedulerFactoryBean.setQuartzProperties(quartzProperties());
            schedulerFactoryBean.setJobFactory(myJobFactory);
            return schedulerFactoryBean;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //指定quartz.properties
    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    //建立schedule(獲取任務排程器)
    @Bean(name = "scheduler")
    public Scheduler scheduler() {
        return schedulerFactoryBean().getScheduler();
    }
}

 ScheduleTriggerServiceImpl 

package com.hc.quartzboot.service.impl;

import com.hc.quartzboot.mapper.ScheduleTriggerMapper;
import com.hc.quartzboot.model.ScheduleTrigger;
import com.hc.quartzboot.service.ScheduleTriggerService;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.List;


@Service
public class ScheduleTriggerServiceImpl implements ScheduleTriggerService {

    @Autowired
    private Scheduler scheduler;

    @Autowired
    private ScheduleTriggerMapper scheduleTriggerMapper;


    @Override
    //@Scheduled(cron = "0 0 23:00 * * ?")  //每天晚上11點呼叫這個方法來更新quartz中的任務
    @Scheduled(cron = "*/10 * * * * ?")  //每隔30秒執行一次更新quartz中的任務,調小一點好測試
    public void refreshTrigger() {
        try {
            System.out.println("ScheduleTriggerService.refreshTrigger");


            //查詢出資料庫中所有的定時任務
            List<ScheduleTrigger> jobList = scheduleTriggerMapper.queryAll();

            if (jobList != null) {
                for (ScheduleTrigger scheduleJob : jobList) {

                    String status = scheduleJob.getStatus(); //該任務觸發器目前的狀態
                    TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
                    CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
                    //說明本條任務還沒有新增到quartz中
                    if (null == trigger) {
                        if (status.equals("0")) { //如果是禁用,則不用建立觸發器
                            continue;
                        }

                        JobDetail jobDetail = null;
                        try {
                            //建立JobDetail(資料庫中job_name存的任務全路徑,這裡就可以動態的把任務注入到JobDetail中)
                            jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(scheduleJob.getJobName()))
                                    .withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).build();

                            ////TO//DO
                            // 可以新增一些額外的引數到任務的上下文中
                            //獲取當前執行的任務id
//                            Integer id=scheduleJob.getId();
//                            //通過任務id獲取引數表中的引數資訊
//                            List<ScheduleTriggerParam> scheduleTriggerParams=scheduleTriggerParamMapper.findParamByJobId(id);
//                            JobDataMap jobDataMap=jobDetail.getJobDataMap();
//                            for (ScheduleTriggerParam param : scheduleTriggerParams) {
//                                jobDataMap.put(param.getName(),param.getValue());
//                            }

                            //表示式排程構建器
                            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob
                                    .getCron());
                            //按新的cronExpression表示式構建一個新的trigger
                            trigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).withSchedule(scheduleBuilder).build();
                            //把trigger和jobDetail注入到排程器
                            scheduler.scheduleJob(jobDetail, trigger);
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    } else {  //說明查出來的這條任務,已經設定到quartz中了
                        // Trigger已存在,先判斷是否需要刪除,如果不需要,再判定是否時間有變化
                        if (status.equals("0")) { //如果是禁用,從quartz中刪除這條任務
                            JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
                            scheduler.deleteJob(jobKey);
                            continue;
                        }
                        String searchCron = scheduleJob.getCron(); //獲取資料庫的
                        String currentCron = trigger.getCronExpression();
                        if (!searchCron.equals(currentCron)) {  //說明該任務有變化,需要更新quartz中的對應的記錄
                            //表示式排程構建器
                            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron);

                            //按新的cronExpression表示式重新構建trigger
                            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                                    .withSchedule(scheduleBuilder).build();

                            //按新的trigger重新設定job執行
                            scheduler.rescheduleJob(triggerKey, trigger);
                        }
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("定時任務每日重新整理觸發器任務異常,在ScheduleTriggerServiceImpl的方法refreshTrigger中,異常資訊:"+e);
            throw new RuntimeException(e);
        }
    }

}

 

QuartzbootApplication

 

package com.hc.quartzboot;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableScheduling
@EnableTransactionManagement
@MapperScan("com.hc.quartzboot.mapper")
public class QuartzbootApplication {

	public static void main(String[] args) {
		SpringApplication.run(QuartzbootApplication.class, args);
	}
}

啟動即可