Ce guide détaille l'intégration de Spring Boot avec le planificateur de tâches Quartz et une base de données MySQL, en fournissant une interface web pour la gestion des tâches.
Configuration de Spring Boot et Quartz
Commencez par créer un projet Spring Boot et ajoutez les dépendances nécessaires dans votre fichier pom.xml.
Dépendances Maven
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qz</groupId>
<artifactId>quartz02</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>quartz02</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<mysql.version>5.1.44</mysql.version>
</properties>
<dependencies>
<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.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</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>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
<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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<dependencies>
<!-- Utilisation d'une version compatible du pilote MySQL pour mybatis-generator -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
<configuration>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<!-- Inclure les fichiers XML des mappers pour mybatis-generator -->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<!-- Inclure les fichiers de propriétés et YAML -->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>*.properties</include>
<include>*.xml</include>
<include>*.yml</include>
</includes>
</resource>
</resources>
</build>
</project>
Configuration du Pool de Connexions Druid
Pour utilisre Druid comme pool de connexions avec Quartz, créez une classe DruidConnectionProvider qui implémente org.quartz.utils.ConnectionProvider.
DruidConnectionProvider.java
package com.qz.quartz02.utils;
import com.alibaba.druid.pool.DruidDataSource;
import org.quartz.SchedulerException;
import org.quartz.utils.ConnectionProvider;
import java.sql.Connection;
import java.sql.SQLException;
/*
Configuration JDBC pour Quartz :
org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties: false
org.quartz.jobStore.dataSource: qzDS
org.quartz.dataSource.qzDS.connectionProvider.class: com.zking.q03.quartz.DruidConnectionProvider
org.quartz.dataSource.qzDS.driver: com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8
org.quartz.dataSource.qzDS.user: root
org.quartz.dataSource.qzDS.password: root
org.quartz.dataSource.qzDS.maxConnections: 30
org.quartz.dataSource.qzDS.validationQuery: select 0
*/
/**
* Extension pour le pool de connexions Druid avec Quartz.
*/
public class DruidConnectionProvider implements ConnectionProvider {
/* Configuration du pool de connexions, mappée aux propriétés de quartz.properties */
public String driver;
public String URL;
public String user;
public String password;
public int maxConnection;
public String validationQuery;
private boolean validateOnCheckout;
private int idleConnectionValidationSeconds;
public String maxCachedStatementsPerConnection;
public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;
public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;
private DruidDataSource datasource;
@Override
public Connection getConnection() throws SQLException {
if (datasource == null) {
throw new SQLException("Le pool de connexions n'est pas initialisé.");
}
return datasource.getConnection();
}
@Override
public void shutdown() throws SQLException {
if (datasource != null) {
datasource.close();
}
}
public void initialize() throws SQLException {
if (this.URL == null) {
throw new SQLException("Impossible de créer le pool de connexions : l'URL de la base de données ne peut pas être nulle.");
}
if (this.driver == null) {
throw new SQLException("Impossible de créer le pilote du pool : le nom de la classe du pilote ne peut pas être nul !");
}
if (this.maxConnection < 0) {
throw new SQLException("Impossible de créer les connexions maximales du pool : le nombre maximum de connexions doit être supérieur à zéro !");
}
datasource = new DruidDataSource();
try {
datasource.setDriverClassName(this.driver);
} catch (Exception e) {
throw new SchedulerException("Problème lors de la définition du nom de la classe du pilote du pool : " + e.getMessage(), e);
}
datasource.setUrl(this.URL);
datasource.setUsername(this.user);
datasource.setPassword(this.password);
datasource.setMaxActive(this.maxConnection);
datasource.setMinIdle(1);
datasource.setMaxWait(0); // Temps d'attente maximal en ms pour obtenir une connexion
datasource.setPoolPreparedStatements(true); // Activer les PreparedStatements en cache
datasource.setMaxPoolPreparedStatementPerConnectionSize(DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION);
if (this.validationQuery != null) {
datasource.setValidationQuery(this.validationQuery);
// Configurer la validation selon le paramètre
if(this.validateOnCheckout) {
datasource.setTestOnBorrow(true);
} else {
datasource.setTestOnReturn(true);
}
datasource.setValidationQueryTimeout(this.idleConnectionValidationSeconds);
}
// Démarrage du pool (peut être géré par Spring aussi)
try {
datasource.init();
} catch (SQLException e) {
throw new SQLException("Erreur lors de l'initialisation du pool Druid : " + e.getMessage(), e);
}
}
/* Getters et Setters pour l'injection par Spring */
public String getDriver() { return driver; }
public void setDriver(String driver) { this.driver = driver; }
public String getURL() { return URL; }
public void setURL(String URL) { this.URL = URL; }
public String getUser() { return user; }
public void setUser(String user) { this.user = user; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public int getMaxConnection() { return maxConnection; }
public void setMaxConnection(int maxConnection) { this.maxConnection = maxConnection; }
public String getValidationQuery() { return validationQuery; }
public void setValidationQuery(String validationQuery) { this.validationQuery = validationQuery; }
public boolean isValidateOnCheckout() { return validateOnCheckout; }
public void setValidateOnCheckout(boolean validateOnCheckout) { this.validateOnCheckout = validateOnCheckout; }
public int getIdleConnectionValidationSeconds() { return idleConnectionValidationSeconds; }
public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) { this.idleConnectionValidationSeconds = idleConnectionValidationSeconds; }
public DruidDataSource getDatasource() { return datasource; }
public void setDatasource(DruidDataSource datasource) { this.datasource = datasource; }
}
Intégration de Spring dans les Jobs Quartz
Pour que vos tâches Quartz puissent utiliser les beans Spring, créez une fabrique de jobs personnalisée.
MyJobFactory.java
package com.qz.quartz02.utils;
import lombok.extern.slf4j.Slf4j;
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;
/**
* Fabrique de jobs personnalisée pour permettre l'injection de dépendances Spring dans les Jobs Quartz.
*/
@Component
@Slf4j
public class MyJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory beanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// Crée une instance du job en utilisant la fabrique de beans Spring
Object jobInstance = super.createJobInstance(bundle);
// Injecte les dépendances Spring dans l'instance du job
beanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
Fichier de Configuration Quartz
Créez un fichier src/main/resources/quartz.properties pour configurer Quartz, en spécifiant notamment l'utilisation du pool Druid et le delegate JDBC.
quartz.properties
#============================================================================
# Propriétés principales du planificateur
#============================================================================
org.quartz.scheduler.instanceName: MonSchedulerQuartz
org.quartz.scheduler.instanceId: AUTO
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
#============================================================================
# Configuration du JobStore (persistance en base de données)
#============================================================================
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: false # Mettez à true pour le mode cluster
#============================================================================
# Configuration de la source de données (DataSource)
#============================================================================
# Utilisation de notre fournisseur de connexion Druid personnalisé
org.quartz.dataSource.qzDS.connectionProvider.class: com.qz.quartz02.utils.DruidConnectionProvider
org.quartz.dataSource.qzDS.driver: com.mysql.jdbc.Driver
org.quartz.dataSource.qzDS.URL: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8
org.quartz.dataSource.qzDS.user: root
org.quartz.dataSource.qzDS.password: root
# Configuration spécifique à Druid via les setters de DruidConnectionProvider
org.quartz.dataSource.qzDS.maxConnection: 10 # Correspond à maxActive dans Druid
# org.quartz.dataSource.qzDS.validationQuery: SELECT 1 # Exemple de requête de validation
Configuration Spring pour Quartz
Créez une classe de configuration Spring pour initialiser Quartz avec votre fabrique de jobs et vos propriétés personnalisées.
QuartzConfiguration.java
package com.qz.quartz02.config;
import com.qz.quartz02.utils.MyJobFactory;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
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;
@Bean
public SchedulerFactoryBean schedulerFactory() {
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
try {
// Charger les propriétés personnalisées de Quartz
factoryBean.setQuartzProperties(quartzProperties());
// Utiliser notre fabrique de jobs personnalisée
factoryBean.setJobFactory(myJobFactory);
// Important : S'assurer que le démarrage est géré par Spring
factoryBean.setStartupDelay(1); // Optionnel: délai avant démarrage du scheduler
} catch (IOException e) {
throw new RuntimeException("Erreur lors du chargement des propriétés Quartz", e);
}
return factoryBean;
}
@Bean
public Properties quartzProperties() throws IOException {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
}
// Bean pour obtenir le Scheduler principal
@Bean(name = "scheduler")
public Scheduler scheduler() {
return schedulerFactory().getScheduler();
}
}
Génération de Code MyBatis
Utilisez MyBatis Generator pour générer le code d'accès aux données à partir de vos tables de base de données.
generatorConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<generatorConfiguration>
<!-- Fichier de propriétés pour la connexion JDBC -->
<properties resource="jdbc.properties"/>
<!-- Chemin vers le JAR du pilote JDBC -->
<classPathEntry location="CHEMIN_VERS_VOTRE_MYSQL_CONNECTOR"/> <!-- Remplacez par le chemin réel -->
<context id="quartzContext">
<commentGenerator>
<property name="suppressAllComments" value="true"/>
<property name="suppressDate" value="true"/>
</commentGenerator>
<!-- Configuration de la connexion JDBC -->
<jdbcConnection driverClass="${jdbc.driver}"
connectionURL="${jdbc.url}"
userId="${jdbc.username}"
password="${jdbc.password}"/>
<!-- Configuration du type Java -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- Génération des Modèles (POJOs) -->
<javaModelGenerator targetPackage="com.qz.quartz02.model" targetProject="src/main/java">
<property name="enableSubPackages" value="false"/>
<property name="constructorBased" value="true"/>
<property name="trimStrings" value="false"/>
<property name="immutable" value="false"/>
</javaModelGenerator>
<!-- Génération des fichiers de mapping SQL (XML) -->
<sqlMapGenerator targetPackage="com.qz.quartz02.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- Génération des interfaces Mapper -->
<javaClientGenerator targetPackage="com.qz.quartz02.mapper" targetProject="src/main/java" type="XMLMAPPER">
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<!-- Tables à mapper -->
<table schema="" tableName="t_schedule_trigger" domainObjectName="ScheduleTrigger"
enableCountByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" enableUpdateByExample="false">
<property name="useActualColumnNames" value="true" />
</table>
<table schema="" tableName="t_schedule_trigger_param" domainObjectName="ScheduleTriggerParam"
enableCountByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" enableUpdateByExample="false">
<property name="useActualColumnNames" value="true" />
</table>
</context>
</generatorConfiguration>
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root
# Ces propriétés peuvent être utilisées par d'autres pools de connexions si nécessaire
# jdbc.initialSize=10
# jdbc.maxTotal=100
# jdbc.maxIdle=50
# jdbc.minIdle=10
# jdbc.maxWaitMillis=-1
Exécutez le plugin MyBatis Generator (par exemple, via Maven : mvn mybatis-generator:generate) pour créer les modèles, les mappers et les fichiers XML correspondants.
Implémentation des Jobs
Créez vos classes de jobs, en vous asurant qu'elles implémentent l'interface org.quartz.Job.
MyJob.java (Job simple)
package com.qz.quartz02.job;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
@Slf4j
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.err.println("Exécution de MyJob simple - Heure : " + new Date().toLocaleString());
}
}
MyJob1.java (Job avec JobDataMap)
package com.qz.quartz02.job;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
@Slf4j
public class MyJob1 implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
System.out.println(new Date().toLocaleString() + " --> MyJob1: Nombre de paramètres reçus : " + dataMap.size());
}
}
MyJob2.java (Job avec paramètres spécifiques)
package com.qz.quartz02.job;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
@Slf4j
public class MyJob2 implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String name = (String) dataMap.get("name");
Integer score = (Integer) dataMap.get("score");
System.out.println(new Date().toLocaleString() + " --> MyJob2: Nom=" + name + ", Score=" + score);
}
}
Couche Service et Contrôleur
Créez une couche service pour interagir avec la base de données et le planificateur Quartz, et un contrôleur pour gérer les requêtes web.
ScheduleTriggerService.java
package com.qz.quartz02.service;
import com.qz.quartz02.model.ScheduleTrigger; // Utilisez le modèle généré par MyBatis
import java.util.List;
public interface ScheduleTriggerService {
int deleteByPrimaryKey(Integer id);
int insert(ScheduleTrigger record);
int insertSelective(ScheduleTrigger record);
ScheduleTrigger selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(ScheduleTrigger record);
int updateByPrimaryKey(ScheduleTrigger record);
/**
* Récupère la liste de tous les déclencheurs de tâches planifiées.
* @return Liste des déclencheurs.
*/
List<ScheduleTrigger> queryScheduleTriggerList();
}
ScheduleTriggerServiceImpl.java
package com.qz.quartz02.service.impl;
import com.qz.quartz02.mapper.ScheduleTriggerMapper;
import com.qz.quartz02.mapper.ScheduleTriggerParamMapper;
import com.qz.quartz02.model.ScheduleTrigger;
import com.qz.quartz02.model.ScheduleTriggerParam;
import com.qz.quartz02.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 ScheduleTriggerMapper scheduleTriggerMapper;
@Autowired
private ScheduleTriggerParamMapper scheduleTriggerParamMapper;
@Autowired
private Scheduler scheduler; // Injectez le Scheduler Spring
/**
* Rafraîchit le planificateur Quartz périodiquement en fonction des données de la base de données.
*/
@Scheduled(cron = "0/15 * * * * ?") // Vérifie toutes les 15 secondes
public void refreshScheduler() {
try {
List<ScheduleTrigger> triggersFromDb = scheduleTriggerMapper.queryScheduleTriggerList();
if (triggersFromDb != null) {
for (ScheduleTrigger triggerInfo : triggersFromDb) {
String jobClassName = triggerInfo.getJob_name();
String jobGroup = triggerInfo.getJob_group();
String cronExpression = triggerInfo.getCron();
String status = triggerInfo.getStatus(); // '1' pour actif, '0' pour inactif
JobKey jobKey = JobKey.jobKey(jobClassName, jobGroup);
TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, jobGroup);
CronTrigger existingTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (status.equals("1")) { // Tâche active
if (existingTrigger == null) {
// Créer et planifier une nouvelle tâche
System.out.println("Création et planification du job: " + jobClassName);
JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(jobClassName))
.withIdentity(jobKey)
.build();
// Ajouter les paramètres au JobDataMap
List<ScheduleTriggerParam> params = scheduleTriggerParamMapper.queryScheduleParamLst(triggerInfo.getId());
if (params != null && !params.isEmpty()) {
JobDataMap jobDataMap = jobDetail.getJobDataMap();
for (ScheduleTriggerParam param : params) {
jobDataMap.put(param.getName(), param.getValue());
}
}
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
Trigger newTrigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(scheduleBuilder)
.build();
scheduler.scheduleJob(jobDetail, newTrigger);
} else {
// Mettre à jour le trigger existant si l'expression cron a changé
String existingCron = existingTrigger.getCronExpression();
if (!cronExpression.equals(existingCron)) {
System.out.println("Mise à jour de l'expression cron pour le job: " + jobClassName);
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
CronTrigger updatedTrigger = existingTrigger.getTriggerBuilder()
.withIdentity(triggerKey)
.withSchedule(scheduleBuilder)
.build();
scheduler.rescheduleJob(triggerKey, updatedTrigger);
}
}
} else { // Tâche inactive (statut '0')
if (existingTrigger != null) {
// Supprimer la tâche si elle existe
System.out.println("Suppression du job inactif: " + jobClassName);
scheduler.deleteJob(jobKey);
}
}
}
}
} catch (ClassNotFoundException e) {
System.err.println("Erreur : Classe du job non trouvée. Vérifiez le nom complet de la classe. " + e.getMessage());
// Gérer l'erreur : log, lancer une exception, etc.
} catch (SchedulerException e) {
System.err.println("Erreur Scheduler Quartz: " + e.getMessage());
// Gérer l'erreur : log, lancer une exception, etc.
} catch (Exception e) {
System.err.println("Erreur inattendue lors de la synchronisation des tâches: " + e.getMessage());
e.printStackTrace();
}
}
// Implémentation des méthodes CRUD de ScheduleTriggerMapper
@Override
public int deleteByPrimaryKey(Integer id) { return scheduleTriggerMapper.deleteByPrimaryKey(id); }
@Override
public int insert(ScheduleTrigger record) { return scheduleTriggerMapper.insert(record); }
@Override
public int insertSelective(ScheduleTrigger record) { return scheduleTriggerMapper.insertSelective(record); }
@Override
public ScheduleTrigger selectByPrimaryKey(Integer id) { return scheduleTriggerMapper.selectByPrimaryKey(id); }
@Override
public int updateByPrimaryKeySelective(ScheduleTrigger record) { return scheduleTriggerMapper.updateByPrimaryKeySelective(record); }
@Override
public int updateByPrimaryKey(ScheduleTrigger record) { return scheduleTriggerMapper.updateByPrimaryKey(record); }
@Override
public List<ScheduleTrigger> queryScheduleTriggerList() { return scheduleTriggerMapper.queryScheduleTriggerList(); }
}
QuartzController.java
package com.qz.quartz02.controller;
import com.qz.quartz02.model.ScheduleTrigger;
import com.qz.quartz02.service.ScheduleTriggerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
@RequestMapping("/quartz")
public class QuartzController {
@Autowired
private ScheduleTriggerService scheduleTriggerService;
/**
* Affiche la liste des tâches planifiées.
*/
@GetMapping("/list")
public ModelAndView listQuartzJobs() {
ModelAndView mv = new ModelAndView("list"); // Nom du template Thymeleaf
List<ScheduleTrigger> jobList = scheduleTriggerService.queryScheduleTriggerList();
mv.addObject("quartzList", jobList);
return mv;
}
/**
* Met à jour le statut (activation/désactivation) d'une tâche.
*/
@GetMapping("/editStatus")
public String editJobStatus(ScheduleTrigger scheduleTrigger) {
scheduleTriggerService.updateByPrimaryKeySelective(scheduleTrigger);
// La logique de rafraîchissement sera déclenchée par @Scheduled
return "redirect:/quartz/list";
}
/**
* Affiche le formulaire d'édition d'une tâche.
*/
@GetMapping("/edit/{id}")
public ModelAndView editJobForm(@PathVariable Integer id) {
ModelAndView mv = new ModelAndView("edit"); // Nom du template Thymeleaf
ScheduleTrigger scheduleTrigger = scheduleTriggerService.selectByPrimaryKey(id);
mv.addObject("schedule", scheduleTrigger);
return mv;
}
/**
* Sauvegarde les modifications d'une tâche.
*/
@PostMapping("/save")
public String saveJob(ScheduleTrigger scheduleTrigger) {
scheduleTriggerService.updateByPrimaryKeySelective(scheduleTrigger);
// La logique de rafraîchissement sera déclenchée par @Scheduled
return "redirect:/quartz/list";
}
}
Interfaces Utilisateur (Thymeleaf)
Créez les fichiers HTML pour l'affichage et la gestion des tâches.
src/main/resources/templates/list.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Gestion des Tâches Quartz</title>
<style>
body { font-family: sans-serif; }
table { border-collapse: collapse; width: 80%; margin: 20px auto; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.actions a { margin-right: 10px; text-decoration: none; }
.status-active { color: green; }
.status-inactive { color: red; }
</style>
</head>
<body>
<h1 style="text-align: center;">Gestion des Tâches Planifiées</h1>
<table>
<tr>
<th>ID</th>
<th>Expression Cron</th>
<th>Statut</th>
<th>Classe du Job</th>
<th>Groupe</th>
<th>Actions</th>
</tr>
<tr th:each="job : ${quartzList}">
<td th:text="${job.id}"></td>
<td th:text="${job.cron}"></td>
<td>
<span th:if="${job.status == '1'}" class="status-active">Actif</span>
<span th:if="${job.status == '0'}" class="status-inactive">Inactif</span>
</td>
<td th:text="${job.job_name}"></td>
<td th:text="${job.job_group}"></td>
<td class="actions">
<a th:if="${job.status == '0'}" th:href="@{/quartz/editStatus(id=${job.id},status='1')}">Activer</a>
<a th:if="${job.status == '1'}" th:href="@{/quartz/editStatus(id=${job.id},status='0')}">Désactiver</a>
<a th:href="@{'/quartz/edit/' + ${job.id}}">Modifier</a>
</td>
</tr>
</table>
</body>
</html>
src/main/resources/templates/edit.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Modifier la Tâche Planifiée</title>
<style>
body { font-family: sans-serif; padding: 20px; }
form { width: 400px; margin: 20px auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px;}
label { display: block; margin-bottom: 5px; }
input[type=text], input[type=submit] {
width: calc(100% - 12px);
padding: 8px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 3px;
}
input[type=submit] {
background-color: #4CAF50;
color: white;
cursor: pointer;
}
input[type=submit]:hover { background-color: #45a049; }
</style>
</head>
<body>
<h1>Modifier la Tâche Planifiée</h1>
<form th:action="@{/quartz/save}" method="post">
<input type="hidden" name="id" th:value="${schedule.id}" />
<label for="cron">Expression Cron</label>
<input type="text" id="cron" name="cron" th:value="${schedule.cron}" required /><br>
<label for="job_name">Classe du Job (Complet)</label>
<input type="text" id="job_name" name="job_name" th:value="${schedule.job_name}" required /><br>
<label for="job_group">Groupe</label>
<input type="text" id="job_group" name="job_group" th:value="${schedule.job_group}" required /><br>
<input type="submit" value="Sauvegarder les modifications" />
</form>
</body>
</html>
Après configuration, les tâches planifiées seront chargées depuis la base de données et pourront être gérées via l'interface web.