Quartz是什么

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能:

持久性作业 - 就是保持调度定时的状态;
作业管理 - 对调度作业进行有效的管理;

为什么用Quartz

项目需要对所有的定时需要进行统一的可视化的管理,之前都是用@Scheduled 进行定时任务的操作,维护比较麻烦

实现思路:

利用Quartz实现定时功能,自定义业务表,业务表跟Quartz关联,通过操作业务表来实现对Quartz的控制,也可进行可视化的管理,业务表存有方法与类的名字,通过反射的方法来实现方法的调用。
当然我们分布式的时候是需要Quartz持久化到数据库中的不能存在内存中

具体使用

本项目用的pgDB 整合可能跟mysql 有些许的不同,使用mybatisPlus进行db的操作

1.springboot引入pom

     <!--quartz依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency>

2.springboot 添加 yml 配置

   quartz:#相关属性配置auto-startup: trueoverwrite-existing-jobs: trueproperties:org:quartz:scheduler:instanceName: IsimClusteredSchedulerinstanceId: AUTOjobStore:class: org.quartz.impl.jdbcjobstore.JobStoreTXdriverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegatetablePrefix: qrtz_isClustered: trueclusterCheckinInterval: 50000useProperties: falsethreadPool:class: org.quartz.simpl.SimpleThreadPoolthreadCount: 10threadPriority: 5threadsInheritContextClassLoaderOfInitializingThread: true#数据库方式job-store-type: jdbc#初始化表结构,初始使用值为always,然后将值改为neverjdbc:initialize-schema: never

3.编写 QuartzHandler 工具类
对Quartz统一操作

Quartz API核心接口
Scheduler:(调度器)与scheduler交互的主要API;
Job:(作业)你通过scheduler执行任务,你的任务类需要实现的接口;
JobDetail:(作业实例)定义Job的实例;
Trigger:(触发器)触发Job的执行;
JobBuilder:定义和创建JobDetail实例的接口;
TriggerBuilder:定义和创建Trigger实例的接口;

public class QuartzHandler {private final static Logger log = LoggerFactory.getLogger(QuartzHandler.class);private final static String JOB_NAME_PREFIX = "JOB_";private final static String TRIGGER_GROUP_NAME = "ISIM_TRIGGER_GROUP";public final static String DEF_JOB_GROUP_NAME = "ISIM_JOB_GROUP";public final static int TASK_STATUS_STOPED = 0;// 定时任务状态-停止public final static int TASK_STATUS_RUNNING = 1;// 定时任务状态-启动/*** 获取CronTrigger* @param scheduler* @param jobId* @return*/public static CronTrigger getCronTrigger(Scheduler scheduler, Integer jobId) {try {return (CronTrigger)scheduler.getTrigger(getTriggerKey(jobId));} catch (SchedulerException e) {log.error("获取CronTrigger异常:", e);throw new QuartzJobException("获取CronTrigger异常:" + e.getMessage());}}/*** 添加定时Job* @param scheduler* @param job*/public static void addJob(Scheduler scheduler, ScheduleJob job) {try {// 构建job信息JobDetail jobDetail = JobBuilder.newJob(SysJob.class).withIdentity(getJobKey(job)).build();// 添加具体Job信息jobDetail.getJobDataMap().put(ScheduleJob.JOB_KEY, job);// Cron表达式调度构建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()).withMisfireHandlingInstructionDoNothing();// 构建triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(job.getId())).withSchedule(scheduleBuilder).build();// 交给scheduler调度scheduler.scheduleJob(jobDetail, trigger);// 暂停JOBif (job.getStatus() == TASK_STATUS_STOPED) {pauseJob(scheduler, job);}} catch (SchedulerException e) {log.error("添加定时Job失败:", e);throw new QuartzJobException("添加定时Job失败: " + e.getMessage());}}/*** 更新定时Job* @param scheduler* @param job*/public static void updateJob(Scheduler scheduler, ScheduleJob job) {try {TriggerKey triggerKey = getTriggerKey(job.getId());// 表达式调度构建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()).withMisfireHandlingInstructionDoNothing();CronTrigger trigger = getCronTrigger(scheduler, job.getId());// Cron表达式调度构建器trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();// 添加具体Job信息trigger.getJobDataMap().put(ScheduleJob.JOB_KEY, job);// 重置jobscheduler.rescheduleJob(triggerKey, trigger);// 暂停Jobif (job.getStatus() == TASK_STATUS_STOPED) {pauseJob(scheduler, job);}} catch (SchedulerException e) {log.error("更新定时Job失败:", e);throw new QuartzJobException("更新定时Job失败:" + e.getMessage());}}/*** 立即执行定时Job* @param scheduler* @param sysJob*/public static void runJob(Scheduler scheduler, ScheduleJob job) {try {JobDataMap dataMap = new JobDataMap();dataMap.put(ScheduleJob.JOB_KEY, job);scheduler.triggerJob(getJobKey(job), dataMap);} catch (SchedulerException e) {log.error("立即执行定时Job失败:", e);throw new QuartzJobException("立即执行定时Job失败:" + e.getMessage());}}/*** 暂停定时Job*/public static void pauseJob(Scheduler scheduler, ScheduleJob job) {try {scheduler.pauseJob(getJobKey(job));} catch (SchedulerException e) {log.error("暂停定时Job失败:", e);throw new QuartzJobException("暂停定时Job失败:" + e.getMessage());}}/*** 恢复定时Job*/public static void resumeJob(Scheduler scheduler, ScheduleJob job) {try {scheduler.resumeJob(getJobKey(job));} catch (SchedulerException e) {log.error("恢复定时Job失败:", e);throw new QuartzJobException("恢复定时Job失败:" + e.getMessage());}}/*** 删除定时Job*/public static void deleteJob(Scheduler scheduler, ScheduleJob job) {try {scheduler.deleteJob(getJobKey(job));} catch (SchedulerException e) {log.error("删除定时Job失败:", e);throw new QuartzJobException("删除定时Job失败:" + e.getMessage());}}/*** 立即执行Job* @param job*/public static void execJob(ScheduleJob job) {Method method = null;try {Object jobClass = SpringContextHolder.getBean(job.getBeanName());String params = job.getParams();if (StringUtils.isNotEmpty(params)) {method = jobClass.getClass().getDeclaredMethod(job.getMethodName(), String.class);} else {method = jobClass.getClass().getDeclaredMethod(job.getMethodName());}ReflectionUtils.makeAccessible(method);// 利用反射执行Jobif (StringUtils.isNotBlank(params)) {method.invoke(jobClass, params);} else {method.invoke(jobClass);}} catch (Exception e) {e.printStackTrace();log.error("立即执行Job失败(" + job.getId() + ")", e);}}/*** 获取Trigger Key* @param jobId* @return*/private static TriggerKey getTriggerKey(Integer jobId) {return TriggerKey.triggerKey(JOB_NAME_PREFIX + jobId, TRIGGER_GROUP_NAME);}/*** 获取Job Key* @param jobId* @return*/private static JobKey getJobKey(ScheduleJob job) {String group = StringUtils.isEmpty(job.getGroupName()) ? DEF_JOB_GROUP_NAME : job.getGroupName();return JobKey.jobKey(JOB_NAME_PREFIX + job.getId(), group);}
}

4.编写 QuartzJobException 的异常类

public class QuartzJobException extends BaseRuntimeException {private static final long serialVersionUID = 1L;public QuartzJobException(String excpMsg) {super(excpMsg);}public QuartzJobException(String code, String excpMsg) {super(code, excpMsg);}
}

5.编写 业务类

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName(schema = "admin", value = "schedule_job")
public class ScheduleJob extends BaseEntity<ScheduleJob> {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Integer id;private String taskName;// 任务名称private String groupName;// Group名称@NotBlank(message = "Bean名称不能为空!")private String beanName;// SpringBean名称@NotBlank(message = "方法名称不能为空!")private String methodName;// 方法名称private String params;// 参数@NotBlank(message = "cron表达式不能为空!")private String cronExpression;// cron表达式private Integer status;// 任务状态private String remark;// 备注private Integer tenantId;// 租户Idpublic final static String BEAN_NAME = "bean_name";public final static String METHOD_NAME = "method_name";public final static String STATUS = "status";public static final String JOB_KEY = "JOB_KEY";// Job调度key@Overrideprotected Serializable pkVal() {return this.id;}}

6.编写controller

@RestController
@RequestMapping("/api/schedule")
@Api(tags = "Schedule")
public class ScheduleRestController extends BaseController {@Autowiredprivate ScheduleJobBiz scheduleJobBiz;/*** 定时任务-分页查询* @param scheduleJobDto* @return*/@ApiOperation("定时任务分页查询")@PostMapping("/page")public Result getJobPage(@RequestBody ScheduleJob scheduleJob) {return scheduleJobBiz.getJobPage(scheduleJob);}/*** 验证定时任务* @param ScheduleJobDto* @return*/@ApiOperation("验证定时任务")@PostMapping("/has")public Result hasJob(@RequestBody ScheduleJob scheduleJob) {if (StringUtils.isEmpty(scheduleJob.getBeanName())) {return Result.error("Bean名称不能为空");}boolean hasJob = scheduleJobBiz.hasJob(scheduleJob.getBeanName(), scheduleJob.getMethodName());return Result.success(hasJob);}/*** 定时任务-增加* @param scheduleJob* @return*/@ApiOperation("增加定时任务")@PostMapping("/add")public Result addJob(@RequestBody ScheduleJob scheduleJob) {scheduleJob.setTenantId(this.getTenantId());scheduleJob.setCreateBy(this.getUserId());Result ret;try {ValidatorUtils.validate(scheduleJob);ret = scheduleJobBiz.addJob(scheduleJob);} catch (InvalidDataException e) {ret = Result.error(e.getMessage());} catch (Exception e) {e.printStackTrace();log.error("Add job error: ", e);ret = Result.error("增加定时任务出错,请重试");}return ret;}/*** 定时任务-修改* @param scheduleJob* @return*/@ApiOperation("修改定时任务")@PostMapping("/upd")public Result updJob(@RequestBody ScheduleJob scheduleJob) {scheduleJob.setUpdateBy(this.getUserId());Result ret = null;try {ValidatorUtils.validate(scheduleJob);ret = scheduleJobBiz.updateJob(scheduleJob);} catch (InvalidDataException e) {ret = Result.error(e.getMessage());} catch (Exception e) {e.printStackTrace();log.error("Upd job error: ", e);ret = Result.error("修改定时任务出错,请重试");}return ret;}/*** 定时任务-删除* @param taskId* @return*/@ApiOperation("删除定时任务")@PostMapping("/del")public Result delJob(@RequestParam String id) {if (StringUtils.isEmpty(id)) {return Result.error("删除定时任务错,传入参数有误");}boolean result = scheduleJobBiz.deleteJob(id);return result ? Result.success("删除定时任务成功") : Result.error("删除定时任务出错,请重试");}/*** 定时任务-立即执行* @param id* @return*/@ApiOperation("立即执行定时任务")@PostMapping("/run")public Result runJob(@RequestParam String id) {if (StringUtils.isEmpty(id)) {return Result.error("执行定时任务错,传入参数有误");}scheduleJobBiz.runJob(id);return Result.success("执行定时任务成功");}/*** 定时任务-立即执行* @param id* @return*/@ApiOperation("立即执行定时任务")@PostMapping("/execute")public Result execJob(@RequestParam String id) {if (StringUtils.isEmpty(id)) {return Result.error("立即执行定时任务错,传入参数有误");}scheduleJobBiz.execJob(id);return Result.success("立即执行定时任务成功");}/*** 定时任务-暂停* @param id* @return*/@ApiOperation("暂停定时任务")@PostMapping("/pause")public Result pauseJob(@RequestParam String id) {if (StringUtils.isEmpty(id)) {return Result.error("暂停定时任务错,传入参数有误");}scheduleJobBiz.pauseJob(id, this.getUserId());return Result.success("暂停定时任务成功");}/*** 定时任务-恢复* @param id* @return*/@ApiOperation("恢复定时任务")@PostMapping("/resume")public Result resumeJob(@RequestParam String id) {if (StringUtils.isEmpty(id)) {return Result.error("恢复定时任务错,传入参数有误");}scheduleJobBiz.resumeJob(id, this.getUserId());return Result.success("恢复定时任务成功");}/*** 定时任务日志-分页查询* @param scheduleJobDto* @return*/@ApiOperation("定时任务日志分页查询")@PostMapping("/log/query")public Result queryJobLog(@RequestBody ScheduleJob scheduleJob) {return scheduleJobBiz.queryJobLogPage(scheduleJob);}
}

7.编写 biz

@Component
public class ScheduleJobBiz {@Autowiredprivate Scheduler scheduler;@Autowiredprivate ScheduleJobService scheduleJobService;@Autowiredprivate ScheduleJobLogService scheduleJobLogService;/*** 项目启动时,初始化定时器*/@PostConstructpublic void init() {List<ScheduleJob> jobList = scheduleJobService.list();for (ScheduleJob job : jobList) {CronTrigger cronTrigger = QuartzHandler.getCronTrigger(scheduler, job.getId());if (cronTrigger == null) {QuartzHandler.addJob(scheduler, job);} else {QuartzHandler.updateJob(scheduler, job);}}}/*** 定时任务分页查询* @param scheduleJob* @return*/public Result getJobPage(ScheduleJob scheduleJob) {QueryWrapper<ScheduleJob> queryWrapper = new QueryWrapper<>();if (StringUtils.isNotEmpty(scheduleJob.getBeanName())) {queryWrapper.like(ScheduleJob.BEAN_NAME, "%" + scheduleJob.getBeanName() + "%");}if (StringUtils.isNotEmpty(scheduleJob.getMethodName())) {queryWrapper.like(ScheduleJob.METHOD_NAME, "%" + scheduleJob.getMethodName() + "%");}if (null != scheduleJob.getStatus()) {queryWrapper.eq(ScheduleJob.STATUS, scheduleJob.getStatus());}Page<ScheduleJob> page = new Page<>(scheduleJob.getPage(), scheduleJob.getSize());IPage<ScheduleJob> result = scheduleJobService.page(page, queryWrapper);return Result.success(PageModel.getPageModel(result));}/*** 验证任务是否存在* @param beanName* @param methodName* @return*/public boolean hasJob(String beanName, String methodName) {return null != scheduleJobService.getScheduleJob(beanName, methodName);}/*** 增加定时任务* @param scheduleJob*/public Result addJob(ScheduleJob scheduleJob) {if (this.hasJob(scheduleJob.getBeanName(), scheduleJob.getMethodName())) {return Result.error("该任务已存在,请勿重复添加!");}if (!CronExpression.isValidExpression(scheduleJob.getCronExpression())) {return Result.error("Cron表达式不正确!");}if (StringUtils.isEmpty(scheduleJob.getGroupName())) {scheduleJob.setGroupName(QuartzHandler.DEF_JOB_GROUP_NAME);}if (StringUtils.isEmpty(scheduleJob.getParams())) {scheduleJob.setParams("{\"tenantId\":\"" + scheduleJob.getTenantId() + "\"}");}scheduleJob.setStatus(QuartzHandler.TASK_STATUS_RUNNING);scheduleJob.setCreateTime(new Date());scheduleJobService.addJob(scheduleJob);return Result.success("增加定时任务成功!");}/*** 任务修改* @param job*/public Result updateJob(ScheduleJob job) {if (null == job.getId() || StringUtils.isEmpty(job.getBeanName(), job.getMethodName(), job.getCronExpression())) {return Result.error("修改定时任务出错,传入数据有误!");}if (!CronExpression.isValidExpression(job.getCronExpression())) {return Result.error("Cron表达式不正确!");}scheduleJobService.updateJob(job);return Result.success("修改定时任务成功!");}/*** 任务删除* @param jobId* @return*/public boolean deleteJob(String jobId) {List<Integer> jobIds = StringUtils.getSplitInteger(jobId, ",");return scheduleJobService.deleteJob(jobIds);}/*** 停止定时任务* @param jobId* @param userId*/public void pauseJob(String jobId, Integer userId) {List<Integer> jobIds = StringUtils.getSplitInteger(jobId, ",");scheduleJobService.pauseJob(jobIds, userId);}/*** 恢复定时任务* @param jobId* @param userId*/public void resumeJob(String jobId, Integer userId) {List<Integer> jobIds = StringUtils.getSplitInteger(jobId, ",");scheduleJobService.resumeJob(jobIds, userId);}/*** 立即执行定时任务* @param jobId*/public void runJob(String jobId) {List<Integer> jobIds = StringUtils.getSplitInteger(jobId, ",");List<ScheduleJob> jobList = scheduleJobService.listByIds(jobIds);for (ScheduleJob scheduleJob : jobList) {QuartzHandler.runJob(scheduler, scheduleJob);}}/*** 立即执行定时任务* @param jobId*/@Asyncpublic void execJob(String jobId) {List<Integer> jobIds = StringUtils.getSplitInteger(jobId, ",");List<ScheduleJob> jobList = scheduleJobService.listByIds(jobIds);for (ScheduleJob scheduleJob : jobList) {QuartzHandler.execJob(scheduleJob);}}/*** 定时任务日志分页查询* @param scheduleJob* @return*/public Result queryJobLogPage(ScheduleJob scheduleJob) {QueryWrapper<ScheduleJobLog> queryWrapper = new QueryWrapper<>();if (StringUtils.isNotEmpty(scheduleJob.getId())) {queryWrapper.eq(ScheduleJobLog.JOB_ID, scheduleJob.getId());}if (StringUtils.isNotEmpty(scheduleJob.getBeanName())) {queryWrapper.like(ScheduleJobLog.BEAN_NAME, "%" + scheduleJob.getBeanName() + "%");}if (StringUtils.isNotEmpty(scheduleJob.getMethodName())) {queryWrapper.like(ScheduleJobLog.METHOD_NAME, "%" + scheduleJob.getMethodName() + "%");}if (null != scheduleJob.getStatus()) {queryWrapper.eq(ScheduleJobLog.STATUS, scheduleJob.getStatus());}Page<ScheduleJobLog> page = new Page<>(scheduleJob.getPage(), scheduleJob.getSize());IPage<ScheduleJobLog> result = scheduleJobLogService.page(page, queryWrapper);return Result.success(PageModel.getPageModel(result));}}

8.编写 service

@Service
public class ScheduleJobService extends ServiceImpl<ScheduleJobMapper, ScheduleJob> {@Autowiredprivate Scheduler scheduler;/*** 查询定时任务* @param beanName* @param methodName* @return*/public ScheduleJob getScheduleJob(String beanName, String methodName) {QueryWrapper<ScheduleJob> queryWrapper = new QueryWrapper<>();queryWrapper.eq(ScheduleJob.BEAN_NAME, beanName).eq(ScheduleJob.METHOD_NAME, methodName);return this.getOne(queryWrapper);}/*** 增加Job* @param scheduleJob*/@Transactional(rollbackFor = Exception.class, value = "transactionManager")public void addJob(ScheduleJob scheduleJob) {this.save(scheduleJob);QuartzHandler.addJob(scheduler, scheduleJob);}/*** 修改Job* @param scheduleJob*/@Transactional(rollbackFor = Exception.class, value = "transactionManager")public void updateJob(ScheduleJob scheduleJob) {ScheduleJob job = this.getById(scheduleJob.getId());if (null != job) {QuartzHandler.updateJob(scheduler, scheduleJob);scheduleJob.setUpdateTime(new Date());scheduleJob.updateById();}}/*** 删除Job* @param jobIds* @return*/public boolean deleteJob(List<Integer> jobIds) {List<ScheduleJob> jobList = this.listByIds(jobIds);jobList.forEach(job -> QuartzHandler.deleteJob(scheduler, job));return this.removeByIds(jobIds);}/*** 暂停运行* @param jobIds* @param userId*/@Transactional(rollbackFor = Exception.class, value = "transactionManager")public void pauseJob(List<Integer> jobIds, Integer userId) {List<ScheduleJob> jobList = this.listByIds(jobIds);for (ScheduleJob job : jobList) {QuartzHandler.pauseJob(scheduler, job);this.updScheduleJob(job.getId(), QuartzHandler.TASK_STATUS_STOPED, userId);}}/*** 恢复运行* @param jobIds* @param userId*/@Transactional(rollbackFor = Exception.class, value = "transactionManager")public void resumeJob(List<Integer> jobIds, Integer userId) {List<ScheduleJob> jobList = this.listByIds(jobIds);for (ScheduleJob job : jobList) {QuartzHandler.resumeJob(scheduler, job);this.updScheduleJob(job.getId(), QuartzHandler.TASK_STATUS_RUNNING, userId);}}/*** 更新定时任务数据库状态* @param jobId* @param status* @param userId* @return*/private ScheduleJob updScheduleJob(Integer jobId, int status, Integer userId) {ScheduleJob scheduleJob = this.getById(jobId);if (null != scheduleJob) {// 更新定时任务状态ScheduleJob upd = new ScheduleJob();upd.setId(scheduleJob.getId());upd.setStatus(status);upd.setUpdateBy(userId);upd.setUpdateTime(new Date());this.updateById(upd);}return scheduleJob;}}

9.反射调用类


@Slf4j
@Component("testTask")
public class TestTask {public void test1(String params) throws InterruptedException {log.info("TestTask test1 run start:" + params);Thread.sleep(3000);log.info("TestTask test1 run end.");}public void test2(String params) throws InterruptedException {log.info("TestTask test2 run start:" + params);Thread.sleep(6000);log.info("TestTask test2 run end.");}}

10.编写后开始测试
第一次启动项目将yml中修改一下 改成always

      #数据库方式job-store-type: jdbc#初始化表结构,初始使用值为always,然后将值改为neverjdbc:initialize-schema: always

启动成功会在db 看到

掉用创建方法等待1min会看到

成功

ps:cron表达式不会写的可以参考这个 https://cron.qqe2.com/

quartz 整合 postgresql 附带例子相关推荐

  1. Win2000 DDK 附带例子概览(图解)

    首先安装Win2000 DDK: 全选: 完成后的目录结构:src下是附带例子: 例子包括以下类别:audio, debugging, general, ime, input, kernel, mdk ...

  2. 在springBoot与quartz 整合中 @Transaction 失效

    问题1::springBoot在与quartz 整合时,使用@Transaction 注解时事务失效 解决方案:创建一个类使用@component被spring管理 ,使用@Transaction标识 ...

  3. 对PostgreSQL SPI例子的学习

    [作者:技术者高健@博客园  mail: luckyjackgao@gmail.com ] http://www.postgresql.org/docs/9.1/static/spi-examples ...

  4. springboot和quartz整合实现动态定时任务(持久化单节点)

    Quartz是一个完全由java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制,它支持定时任务持久化到数据库,从而避免了重启服务器时任务丢失,支持分布式多节点,大大的 ...

  5. SpringBoot + SpringBatch + Quartz整合定时批量任务

    点击关注公众号,实用技术文章及时了解 来源:blog.csdn.net/zxd1435513775/article/ details/99677223 一.引言 最近一周,被借调到其他部门,赶一个紧急 ...

  6. Spring+SpringMVC+mybatis+Quartz整合

    Quartz与SpringMVC的整合 简介 Quartz是一个完全由java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制.Quartz允许开发人员根据时间间隔来调 ...

  7. springboot+Quartz整合!!!简单实用

    一.什么是Quartz 在Quartz官网上是这么写的 Quartz官网 1.Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制 ...

  8. Postgresql LATERAL例子

    lateral 子查询可以支持横向连接外面的表,在FROM 或者JOIN子句的子查询里面可以关联查询LATERAL前面的FROM子句或者JOIN子句,通过例子看如何使用: #创建测试表并初始化数据 C ...

  9. Quartz整合Spring

    操作步骤 第一步,引入jar包 第二步,创建Job类 第三步,将Job类配置到Spring容器 第四步,将Job类配置到JobDetail 第五步,配置调度触发器 第六步,配置调度工厂 注意 一个Jo ...

  10. MySQL视图附带例子详解(小白都能懂哦)

    1.视图的概念 2.视图的应用场景 3.视图的特点 4.创建视图的格式 5.视图的修改 6.视图的删除 7.视图的查看 8.视图的更新 9.视图和表格的对比 10.视图的优缺点 1.视图的概念 视图是 ...

最新文章

  1. ADAS摄像头20个技术挑战
  2. bzoj 3028 生成函数
  3. 基于Vue和axios的音乐播放器——悦听音乐效果展示及代码分享
  4. 【CodeForces - 789D】Weird journey(思维,图的性质,tricks,有坑)
  5. 选择座位html,影厅座位预览效果(css3)_html/css_WEB-ITnose
  6. 闭包(实例化)【面试】
  7. [EasyHexo 专栏] #1 - Markdown 编辑器推荐与语法简介
  8. Win10电脑安装打印机驱动运转打印机的方法
  9. Ubuntu18.04-国产周立功Can 分析仪驱动实现-python版本
  10. Amesim学习——气体混合室仿真
  11. Office 2010 文件验证
  12. php敏感代码屏蔽,PHP敏感词汇屏蔽或替换
  13. 四川省中小学计算机台球标准,《四川省中小学教育技术装备标准》.xls
  14. 怎么测试ftp服务器上传文件,ftp服务器文件上传测试
  15. 基础C语言知识串串香10☞数组字符串结构体联合体枚举
  16. oracle odbc drivers,Actual ODBC drivers 介绍
  17. k8s部署-48-k8s中如何选择使用哪个api,开发一个k8s的容器管理平台的思路是什么?
  18. MySQL 中你应该使用什么数据类型表示时间?
  19. 从“光棍节”到“双节棍”:今年的天猫双11大不一样
  20. 山水之道第二境——精灵的世界之城

热门文章

  1. Tokio教程之深入异步
  2. 【张朝阳的物理课笔记】9. 瑞丽金斯公式的推导(下),普朗克修正,黑体辐射公式
  3. Android手机便携式wifi的使用及无线数据传输(主要针对XP系统)
  4. 华为手机热点无法连接_为什么电脑就是连不上华为手机的热点
  5. 聊天中批判性思维的应用
  6. 图像各向异性平滑滤波
  7. wps计算机里wps云盘图标,我的电脑中的wps网盘图标怎么设置删除
  8. 华为网络设备与基础配置
  9. 网课答案搜题API接口使用
  10. 百度富文本编辑器配置使用