当前位置: 首页 > news >正文

基于quartz实现定时任务管理系统

基于quartz实现定时任务管理系统

背景

说起定时任务框架,首先想到的是Quartz。这是定时任务的老牌框架了,它的优缺点都很明显。借助PowerJob 的readme文档的内容简单带过一下这部分。

在这里插入图片描述

除了上面提到,还有elastic-job-litequartzui也是相当火热的,可以自行了解一下。

那么都这么多后起之秀,个个吊打quartz,为什么我还选择用quartz来做定时任务?技术是服务于业务的,肯定是有需求呗。公司有个统一管理平台,现在需要开发一个定时任务管理平台,可以动态去管理配置定时任务、查看运行日志。

为什么不考虑其它的框架?由于需求要定制化ui界面和定时任务执行结果接入统一的通知中心等等需求,上面大多数框架都是通过简单配置开箱即用,定制化需要对源码有一定熟悉程度,而quartz我在大学就用了很多次了,非常熟悉,改造相对容易。

需求分析

  1. 定时任务的增删改查
  2. 定时任务执行日志查看

详细设计

开发环境

jdk 1.8

spring boot 2.7.7

quartz 单机版

Mysql 8.0

1. 定时任务的实现

quartz的任务实际是通过内置Java类实现job接口或者继承QuartzJobBean实现executeInternal来实现定时任务的添加。所以我们在管理页面上所做的增删改查操作都不可能是真正意义上地去修改了任务类,而是修改了映射类。举个例子来说,比如Power Job的管理界面

在这里插入图片描述

我们修改的这些任务名称、定时信息的持久化操作都不会是操作了真正的任务类,而是修改了一个绑定这个任务类的映射类。如下图中的类就是任务类

在这里插入图片描述

而图片中的这些就是属性就是映射类的属性。

你可以先是觉得任务类和映射类之间是一对一的关系,映射类记录了定时任务的执行频率(cron)、名称、任务类完整类名等其它的信息,然后在执行过程中通过完整类名反射获得任务类,任务类再根据这些信息去执行。

但如果每个定时任务,我们都要去实现Job接口创建一个类,相同的那些代码比如获取trigger、scheduler等等,都要出现在每个类中,每次添加一个定时任务都要多一个专门的定时任务类。每次开发时都要关注业务和定时任务类之间的关系,多了之后是有点烦。

我推荐的做法是,创建一个具备http请求功能的任务类,将业务操作开发成一个接口,在配置映射类时,将http链接配置上去,这样一到时间 ,就会请求到对应的业务接口。这样使得后续的定时任务功能开发更专注于业务开发,方便使用。尤其是团队开发中,对一些不熟悉quartz的朋友格外友好。

编码实现

引入依赖

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.31</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.11</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

映射实体类

@TableName
@Data
public class ScheduleJob {@TableIdprivate Long id;@TableFieldprivate String jobName;@TableFieldprivate String cronExpr;    // cron表达式@TableFieldprivate String jobStatus;   // 任务状态@TableFieldprivate String url;         // 业务接口
}

必不可少的属性大概就是上面这几个,后续需要再添加

service层与mapper层我偷个懒,直接用mybatisplus的api

ScheduleJobService

@Service
public class ScheduleJobService extends ServiceImpl<ScheduleJobMapper, ScheduleJob> {
}

ScheduleJobMapper

@Mapper
public interface ScheduleJobMapper extends BaseMapper<ScheduleJob> {
}

有了这些就可以先做个添加定时任务的接口测试一下流程是否通畅

ScheduleJobController

@RestController
public class ScheduleJobController {@Resourceprivate ScheduleJobService scheduleJobService;@PostMapping("/scheduleJob")public ResultBean createScheduleJob(@RequestBody ScheduleJob scheduleJob) {scheduleJobService.save(scheduleJob);return ResultBean.SUCCESS();}}

这个ResultBean是我封装的一个返回对象

@Data
@AllArgsConstructor
public class ResultBean {private String code;private String msg;private Object data;public static ResultBean SUCCESS(Object... data) {String code = "200";String msg = "操作成功!";return new ResultBean(code, msg, data);}
}

简单测试了一下,能写入库

在这里插入图片描述

目前为止的操作和定时任务还没有一分钱关系,接下来我们来接入定时任务。

创建任务类,这个任务类要支持发送http请求,所以取名为RestRequestJob,不过我们还不知道能不能真的按着指定的时间的执行,先不写太复杂。

@Slf4j
public class RestRequestJob extends QuartzJobBean {@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {log.info("hello quartz");}
}

映射实体类有了,任务类也有了,就差任务类怎么按照映射实体类的信息去执行定时任务了

@Service
public class ScheduleJobManageService {@Autowiredprivate ScheduleJobService scheduleJobService;@Autowiredprivate Scheduler scheduler;public void createScheduleJob(ScheduleJob scheduleJob) {JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName() + scheduleJob.getId());JobDetail jobDetail = JobBuilder.newJob(RestRequestJob.class).withIdentity(jobKey).build();// 表达式调度构建器CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpr());// 按新的cronExpression表达式构建一个新的triggerCronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(TriggerKey.triggerKey(scheduleJob.getId().toString())).withSchedule(cronScheduleBuilder).build();jobDetail.getJobDataMap().put("TASK_PROPERTIES", scheduleJob);try {scheduler.scheduleJob(jobDetail, trigger);if (scheduleJob.getJobStatus().equals("0")) {scheduler.pauseJob(jobKey);}} catch (SchedulerException e) {throw new RuntimeException(e);}}}

jobKey是定时任务的唯一标识

jobDetail是任务类

trigger是执行时间

jobDetail.getJobDataMap().put("TASK_PROPERTIES", scheduleJob);是把目前的映射类信息保存到缓存中,可提供给其它地方用。

scheduler.scheduleJob是开始定时任务的线程

scheduler.pauseJob如果发现状态是暂停的话,则暂停定时任务

回到ScheduleJobController类的createScheduleJob方法,调用scheduleJobManageService类中的创建定时任务方法

    @PostMapping("/scheduleJob")public ResultBean createScheduleJob(@RequestBody ScheduleJob scheduleJob) {scheduleJobService.save(scheduleJob);scheduleJobManageService.createScheduleJob(scheduleJob);return ResultBean.SUCCESS();}

到此再来测试一下,添加定时任务时,能不能启动任务类

在这里插入图片描述

定时任务启动成功了,但是目前除了停止应用,我们完全没办法能让它停下来。得写个暂停方法

@RestController
public class ScheduleJobManagerController {@Resourceprivate ScheduleJobManageService scheduleJobManageService;@GetMapping("/scheduleJobManage/changeStatus/{id}")public ResultBean changeStatus(@PathVariable("id") Long id) {try {scheduleJobManageService.changeStatus(id);return ResultBean.SUCCESS();} catch (SchedulerException e) {throw new RuntimeException(e);}}
}

在ScheduleJobManageService类中添加changeStatus方法

    @Transactionalpublic void changeStatus(Long id) throws SchedulerException {ScheduleJob scheduleJob = scheduleJobService.getById(id);String status = scheduleJob.getJobStatus().equals("1") ? "0" : "1";scheduleJob.setJobStatus(status);scheduleJobService.saveOrUpdate(scheduleJob);JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName() + scheduleJob.getId());switch (status) {case "1":log.info("已启动任务"+scheduleJob.getId());scheduler.resumeJob(jobKey);break;case "0":log.info("已暂停任务"+scheduleJob.getId());scheduler.pauseJob(jobKey);break;}}

测试一下,暂停启动都能成功

在这里插入图片描述

那我们这些定时任务遭遇了服务器重启之后都没法再启动了吗?那肯定不是,我们在服务启动过后再加载回来这些定时任务不就ok了嘛。在ScheduleJobManageService类中加多一个init方法

    @PostConstructpublic void init() throws SchedulerException{scheduler.clear();List<ScheduleJob> jobs = scheduleJobService.list();jobs.parallelStream().forEach(this::createScheduleJob);}

一启动之后,那堆没暂停的定时任务一直在跑

在这里插入图片描述

现在该实现http请求了,回到RestRequestJob类的executeInternal

    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
//        log.info("hello quartz");ScheduleJob scheduleJob = new ScheduleJob();BeanUtils.copyProperties(context.getMergedJobDataMap().get("TASK_PROPERTIES"), scheduleJob);log.info("calling...{}",scheduleJob.getUrl());String s = HttpUtil.get(scheduleJob.getUrl());log.info(s);}

context.getMergedJobDataMap().get("TASK_PROPERTIES")这个就是刚才存进去的scheduleJob对象,这样就可以将url取出来,去访问了。

我做了一个很简单的接口用于测试是否url可以访问通的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UEO95tVV-1678082698242)(image-20230303154257116.png)]

调用成功

目前还缺定时任务修改、移除,执行日志记录

定时任务修改

ScheduleJobController

    @PutMapping("/scheduleJob")public ResultBean updateScheduleJob(@RequestBody ScheduleJob scheduleJob) {try {scheduleJobManageService.updateScheduleJob(scheduleJob);return ResultBean.SUCCESS();} catch (SchedulerException e) {throw new RuntimeException(e);}}

ScheduleJobManageService

    @Transactionalpublic void updateScheduleJob(ScheduleJob scheduleJob) throws SchedulerException {ScheduleJob oldInfo = scheduleJobService.getById(scheduleJob.getId());scheduleJobService.saveOrUpdate(scheduleJob);if (!oldInfo.getCronExpr().equals(scheduleJob.getCronExpr()) || !oldInfo.getUrl().equals(scheduleJob.getUrl())) {JobKey jobKey = JobKey.jobKey(scheduleJob.getId().toString());if (scheduler.checkExists(jobKey)){// 防止创建时存在数据问题 先移除,然后在执行创建操作scheduler.deleteJob(jobKey);}createScheduleJob(scheduleJob);}log.info("更新成功!");}

删除定时任务

ScheduleJobController

    @DeleteMapping("/scheduleJob/{id}")public ResultBean deleteScheduleJob(@PathVariable Long id) {try {scheduleJobManageService.deleteScheduleJob(id);return ResultBean.SUCCESS();} catch (SchedulerException e) {throw new RuntimeException(e);}}

ScheduleJobManageService

    @Transactionalpublic void deleteScheduleJob(Long id) throws SchedulerException {boolean b = scheduleJobService.removeById(id);if (b) {scheduler.deleteJob(JobKey.jobKey(id.toString()));}}

2. 日志写入

实体类

@Data
@TableName
public class ScheduleJobLog {@TableId(type = IdType.AUTO)private Long id;@TableFieldprivate Long schdJobId;@TableFieldprivate Date startTime;@TableFieldprivate Date endTime;@TableFieldprivate Integer executeRes;@TableFieldprivate String errorInfo;
}

Mapper和Service层都是用了mybatisplus偷工减料,就不累赘了。

重点是,我们的日志怎么切入?切入在什么地方?quartz实际上当trigger时间到了的时候,他去执行Job的实现类。也就是说我们的日志应该切入在RestRequestJob类中,那接下来改装一下executeInternal方法

protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
//        log.info("hello quartz");ScheduleJobLog scheduleJobLog = new ScheduleJobLog();ScheduleJob scheduleJob = null;try {scheduleJobLog.setStartTime(new Date());scheduleJob = new ScheduleJob();BeanUtils.copyProperties(context.getMergedJobDataMap().get("TASK_PROPERTIES"), scheduleJob);log.info("calling...{}",scheduleJob.getUrl());
//            String s = HttpUtil.get(scheduleJob.getUrl());HttpResponse httpRes = HttpRequest.get(scheduleJob.getUrl()).execute();log.info(String.valueOf(httpRes.getStatus()));if (httpRes.getStatus() == HttpStatus.HTTP_NOT_FOUND) {throw new Exception(String.valueOf(HttpStatus.HTTP_NOT_FOUND));}scheduleJobLog.setExecuteRes(1);    // 执行成功} catch (Exception e) {scheduleJobLog.setExecuteRes(-1);    // 执行失败scheduleJobLog.setErrorInfo(e.getMessage());throw new RuntimeException(e);} finally {scheduleJobLog.setEndTime(new Date());scheduleJobLog.setSchdJobId(scheduleJob.getId());ScheduleJobLogService logService = SpringUtil.getBean(ScheduleJobLogService.class);logService.save(scheduleJobLog);}}

SpringUtil.getBean是使用了hutool的工具,目的是在不被spring管理的类中也能拿到Bean。

到这里看起来貌似没什么问题,能增删改定时任务了,日志也能插入了。但是经验告诉我,如果碰到那种定时任务需要同步几万数据入库的,http的长连接也扛不住,而且一直在等待也对性能造成极大的影响。

那么能不能说,发出去的请求,到对应业务接口自己执行得了,不管你的死活,你执行完之后告诉我一声执行结果得了。这就是异步思想。当http请求过来的时候,任务类中把执行的日志中的id也带过去,业务接口这边一旦收到,马上就返回结果,告诉任务类这边,收到执行指令,你安就得了。任务类把执行日志的状态设置为【执行中】。业务接口执行结束之后调用一下,定时任务日志的接口,把状态更新即可。

  1. 新增更新执行状态接口
@RestController
public class ScheduleJobLogController {@Autowiredprivate ScheduleJobLogService logService;@PutMapping("/scheduleJobLog")public ResultBean updateStatus (@RequestBody ScheduleJobLog scheduleJobLog) {logService.updateStatus(scheduleJobLog);return ResultBean.SUCCESS();}
}

2.实现更新状态方法updateStatus

@Service
public class ScheduleJobLogService extends ServiceImpl<ScheduleJobLogMapper, ScheduleJobLog> {public void updateStatus(ScheduleJobLog scheduleJobLog) {ScheduleJobLog jobLog = this.getById(scheduleJobLog.getId());jobLog.setExecuteRes(scheduleJobLog.getExecuteRes());jobLog.setErrorInfo(scheduleJobLog.getErrorInfo());jobLog.setEndTime(new Date());  // 更新执行结束时间this.saveOrUpdate(jobLog);}}

在业务执行接口上有比较大的变动

@RestController
public class ExecuteJobController {@GetMapping("/scheduleJob/helloWorld")public ResultBean sayHelloWorld(Long logId) {ScheduleJobLog jobLog = new ScheduleJobLog();jobLog.setId(logId);new Thread(() -> {try {// 模拟超时长业务
//                Thread.sleep(2000);jobLog.setExecuteRes(1);//throw new Exception("just test");} catch (Exception e) {jobLog.setExecuteRes(-1);jobLog.setErrorInfo(e.getMessage());throw new RuntimeException(e);} finally {HttpRequest.put("http://localhost:8080/scheduleJobLog").body(JSONUtil.toJsonStr(jobLog)).execute();}}).start();return ResultBean.SUCCESS("hello world!");}
}

在实际的测试过程中,RestRequestJob的实现方法中,由于入库操作在finally中,当业务接口比较简单时,会出现数据库的并发问题,日志记录未入库,导致scheduleJobLogService.getById()方法空指针,因此要先入库再发送http请求业务接口

    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {ScheduleJobLog scheduleJobLog = new ScheduleJobLog();ScheduleJob scheduleJob =  new ScheduleJob();long snowflakeNextId = IdUtil.getSnowflakeNextId();ScheduleJobLogService logService = SpringUtil.getBean(ScheduleJobLogService.class);try {BeanUtils.copyProperties(context.getMergedJobDataMap().get("TASK_PROPERTIES"), scheduleJob);scheduleJobLog.setExecuteRes(0);    // 正在执行scheduleJobLog.setStartTime(new Date());scheduleJobLog.setId(snowflakeNextId);scheduleJobLog.setSchdJobId(scheduleJob.getId());logService.save(scheduleJobLog);    // 避免出现并发问题,应该先入库log.info("calling...{}", scheduleJob.getUrl()+"?logId="+snowflakeNextId);String reqUrl = scheduleJob.getUrl()+"?logId="+snowflakeNextId;HttpResponse httpRes = HttpRequest.get(reqUrl).timeout(10000).execute();log.info("logId" + snowflakeNextId + "; url: " + reqUrl);if (httpRes.getStatus() == HttpStatus.HTTP_NOT_FOUND) {throw new Exception(String.valueOf(HttpStatus.HTTP_NOT_FOUND));}} catch (Exception e) {scheduleJobLog.setExecuteRes(-1);    // 执行失败scheduleJobLog.setErrorInfo(e.getMessage());logService.saveOrUpdate(scheduleJobLog);throw new RuntimeException(e);}}

总结

在本文中重点是讲解实现思路,代码并不算怎么优雅,格式优化的地方还有很多,参数校验也不够严谨,可以根据自己的需求改造。

代码拉取

开源项目,最好就是各路大神能一起维护这个项目。

仓库地址:https://gitcode.net/interestANd/quartz-job

相关文章:

基于quartz实现定时任务管理系统

基于quartz实现定时任务管理系统 背景 说起定时任务框架&#xff0c;首先想到的是Quartz。这是定时任务的老牌框架了&#xff0c;它的优缺点都很明显。借助PowerJob 的readme文档的内容简单带过一下这部分。 除了上面提到&#xff0c;还有elastic-job-lite、quartzui也是相当…...

vue-element-admin:基于element-ui 的一套后台管理系统集成方案

文章目录一、vue-element-admin1、vue-element-admin1.1简介1.2安装2、vue-admin-template2.1简介2.2安装一、vue-element-admin 1、vue-element-admin 1.1简介 vue-element-admin是基于element-ui 的一套后台管理系统集成方案。 GitHub地址&#xff1a;https://github.com…...

KVM-7、KVM 虚拟机创建的几种方式

通过对 qemu-kvm、libvirt 的学习,总结三种创建虚拟机的方式: (1)通过 qemu-kvm 创建 (2)通过 virt-install 创建 (3)通过 virt-manager 创建 在使用这三种创建虚拟机前提是 宿主机必须支持 cpu 的硬件虚拟化技术(Intel 是 vmx,AMD 是svm),通过下面方式进行查看…...

Hadoop三大框架之HDFS

一、概述HDFS产生的背景及定义HDFS产生背景随着数据量越来越大&#xff0c;在一个操作系统存不下所有的数据&#xff0c;那么就分配到更多的操作系统管理的磁盘中&#xff0c;但是不方便管理和维护&#xff0c;需要一种系统来管理多台机器上的文件&#xff0c;这就是分布式文件…...

好好的系统,为什么要分库分表?

不急于上手实战 ShardingSphere 框架&#xff0c;先来复习下分库分表的基础概念&#xff0c;技术名词大多晦涩难懂&#xff0c;不要死记硬背理解最重要&#xff0c;当你捅破那层窗户纸&#xff0c;发现其实它也就那么回事。 什么是分库分表 分库分表是在海量数据下&#xff0…...

多种调度模式下的光储电站经济性最优储能容量配置分析(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

二分法(适用于任何题型!!!)

今天看二分法看了一天&#xff0c;看吐了&#xff0c;现在讲讲怎么做类题。 只讲两种做法&#xff08;实则是可合并为一种&#xff09;&#xff0c;任何题型都可以转化为这种做法&#xff01;&#xff01;&#xff01;是任何&#xff01; 首先&#xff0c;设置 int left,righ…...

js常见的七种继承及实现

在 JavaScript 中&#xff0c;常见的继承方式有以下七种&#xff1a;大厂面试题分享 面试题库前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★地址&#xff1a;前端面试题库1. 原型链继承原型链继承是 JavaScript 中一种基于原型的继承方式&#…...

案例分析之——理由Mybatis动态SQL实现复用

无复用思想的做法&#xff1a; 在没有复用思想的时候&#xff0c;就只顾着实现功能。比如开发过程中涉及到两个表的更新功能&#xff0c;每需要更新一处&#xff0c;就写一个接口&#xff0c;结果出现了写了11个接口的情况。 这样虽然功能实现了&#xff0c;可是可能自…...

MCM 箱模型建模方法及大气 O3 来源解析实用干货

OBM 箱模型可用于模拟光化学污染的发生、演变过程&#xff0c;研究臭氧的生成机制和进行敏感性分析&#xff0c;探讨前体物的排放对光化学污染的影响。箱模型通常由化学机理、物理过程、初始条件、输入和输出模块构成&#xff0c;化学机理是其核心部分。MCM (Master Chemical M…...

【独家】华为OD机试 - 最长连续交替方波信号(C 语言解题)

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 最近更新的博客使用说明本期…...

代码随想录算法训练营第二十一天打卡 | 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先

打卡第21天&#xff0c;继续二叉树&#xff0c;前几天终于补完了&#xff0c;感觉难度上来了。 今日任务 530.二叉搜索树的最小绝对差501.二叉搜索树中的众数 二叉树的最近公共祖先 530.二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不…...

免费下载丨一看即会,Serverless 技术进阶必读百宝书

过去一年&#xff0c;全球正在加速推进云计算的 Serverless 化进程。Serverless 架构已经逐渐从“被接受”走向了“被学习”和“被应用”。云的产品体系正在 Serverless 化&#xff0c;从计算、存储、数据库到中间件&#xff0c;越来越多的云产品采用了 Serverless 模式。服务器…...

SQL语句的加锁方式 - Mysql 锁机制

SQL语句的加锁方式 - Mysql锁机制 SELECT ... FROM SELECT ... FOR UPDATE / SELECT ... FOR SHARED MODE SELECT ... LOCK IN SHARE MODE SELECT ... FOR UPDATE UPDATE ... WHERE ... DELETE FROM ... WHERE ... INSERT INSERT ... ON DUPLICATE KEY UPDATE REPLACE Mysql锁机…...

C#开发的OpenRA的游戏主界面怎么样创建4

继续游戏主界面创建的主题, 前面已经说到怎么样找到mainmenu.yaml来显示主界面,也说了怎么样找到各个子控件类。 现在就来仔细分析一下,主界面每一部分的功能。 比如下面这个区域的界面是怎么样创建: 要创建这一小部分的界面显示,也是需要做很多的工作。 因为在这里所有UI…...

覆盖5大主流开发平台的报表控件,它值得你一看

为什么大家现在都在使用第三方报表工具呢&#xff1f; 第三方报表工具是数据库存储&#xff0c;数据库程序通常可以存放的数据量是相当大的&#xff0c;可以处理非常复杂的数据结构关系&#xff0c;报表数据交互速度也非常快。不仅能够提高开发效率&#xff0c;还能实现灵活美…...

【冲刺蓝桥杯的最后30天】day4

大家好&#x1f603;&#xff0c;我是想要慢慢变得优秀的向阳&#x1f31e;同学&#x1f468;‍&#x1f4bb;&#xff0c;断更了整整一年&#xff0c;又开始恢复CSDN更新&#xff0c;从今天开始更新备战蓝桥30天系列&#xff0c;一共30天&#xff0c;如果对你有帮助或者正在备…...

spring boot actuator 动态修改日志级别

1 日志级别 Spring Boot Actuator包括在运行时查看和配置应用程序日志级别的功能。您可以查看整个列表&#xff0c;也可以查看单个记录器的配置&#xff0c;该配置由显式配置的日志级别和日志框架给出的有效日志级别组成。这些级别可以是: TRACEDEBUGINFOWARNERRORFATALOFFnu…...

兴达易控Modbus转Profinet网关连接1200Profinet转modbus接三菱A800变频器案例

下面介绍A800 变频器通过兴达易控modbus转profinet网关&#xff0c;使1200plc无需编程实现Profinet转modbus协议转换&#xff0c;把modbus变频器轻松组网 网络拓扑如下图 打开博图组态加载GSD文件&#xff0c;modbus转profinet网关从站接口接入到1200PLC上 配置modbus转profine…...

「SAP ABAP」OPEN SQL(四)【FROM语句】

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…...

一文吃透 SpringMVC 中的转发和重定向

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

Hbase操作命令

目录 创建表&#xff0c;表中有两个列族 baseinfo, schoolinfo 查看指定表全名空间中的表 查看表描述 禁用/启用 查看是否启用/禁用 删除表 注意&#xff0c;首先要将删除的表设置为禁用状态才可以删除&#xff0c;否则会报错 新增列族 删除列族 更改列族存储版本的限制 增…...

1>LINK : fatal error LNK1104: cannot open file ‘libconvtname.obj‘

我自己最后找到问题原因是&#xff1a; 引用的库名称没有.lib&#xff0c;只有libconvtname。 改成完整的libconvtname.lib即可。 以下是chatGPT的回答 The error message "fatal error LNK1104: cannot open file libconvtname.obj" usually occurs when Visual S…...

数据结构——链表OJ题目讲解(1)

作者&#xff1a;几冬雪来 时间&#xff1a;2023年3月7日 内容&#xff1a;数据结构链表OJ题目讲解 题目来源&#xff1a;力扣和牛客 目录 前言&#xff1a; 刷题&#xff1a; 1.移出链表元素&#xff1a; 2.链表的中间结点&#xff1a; 3. 链表中倒数第k个结点&#xff1…...

LeetCode_二分搜索_困难_154.寻找旋转排序数组中的最小值 II

目录1.题目2.思路3.代码实现&#xff08;Java&#xff09;1.题目 已知一个长度为 n 的数组&#xff0c;预先按照升序排列&#xff0c;经由 1 到 n 次 旋转 后&#xff0c;得到输入数组。例如&#xff0c;原数组 nums [0,1,4,4,5,6,7] 在变化后可能得到&#xff1a; 若旋转 4…...

面向对象设计模式:创建型模式之建造者模式

一、引入 Build&#xff1a;建造和构建具有建筑结构的大型物体 建楼&#xff1a;打牢地基、搭建框架、然后自下而上一层层盖起来。构建物体&#xff1a;通常需要先建造组成这个物体的各个部分&#xff0c;然后分阶段把它们组装起来 二、建造者模式 2.1 Intent 意图 Separate…...

集成学习boosting、bagging、stacking

目录 一、介绍 二、三种架构学习 &#xff08;1&#xff09;boosting &#xff08;2&#xff09;bagging &#xff08;3&#xff09;stacking 一、介绍&#xff1a; 对于单个模型来说很难拟合复杂的数&#xff0c;模型的抗干扰能力较低&#xff0c;所以我们希望可以集成多…...

数据模型(上):模型分类和模型组成

1.模型分类 ​ 数据模型是一种由符号、文本组成的集合,用以准确表达信息景观,达到有效交流、沟通的目的。数据建模者要求能与来自不同部门,具有不同技术背景,不同业务经验,不同技术水平的人员交流、沟通。数据建模者要了解每个人员的观点,并通过反馈证明理解无误,最终作…...

郑州轻工业大学2022-2023(2) 数据结构题目集 - ZZULI

6-1 线性表元素的区间删除 6-1 线性表元素的区间删除 分数 20 全屏浏览题目 切换布局 作者 DS课程组 单位 浙江大学 给定一个顺序存储的线性表&#xff0c;请设计一个函数删除所有值大于min而且小于max的元素。删除后表中剩余元素保持顺序存储&#xff0c;并且相对位置不能改变…...

【Python语言基础】——Python MySQL Drop Table

Python语言基础——Python MySQL Drop Table 文章目录Python语言基础——Python MySQL Drop Table一、Python MySQL Drop Table一、Python MySQL Drop Table 删除表 您可以使用 “DROP TABLE” 语句来删除已有的表&#xff1a; 实例 删除 “customers” 表&#xff1a; import…...

那些cps网站做的比较好/游戏代理怎么找渠道

2019独角兽企业重金招聘Python工程师标准>>> 前几天&#xff0c;写android代码的时候&#xff0c;遇到R.java报出identifier expected的error。 <string name "3d"> 3D </string> 这样写的问题在于虽然符合XML的规范&#xff0c;但是由于A…...

荆州市建设委员会网站/如何搭建一个网站

使用外部邮箱来发生邮件明显好处就是防止其他邮箱服务器当垃圾邮件处理&#xff0c;另一方面能降低收邮件延迟。 下面开始进行使用外部邮箱配置&#xff1a; zabbix服务端配置&#xff1a; 操作系统&#xff1a;CentOS7_x64 1、 安装一个邮件发送程序mailx工具&#xff08;msm…...

删掉cache wordpress/万网域名查询官网

原文 对于我们CSS开发者来说经常听到一些关于LESS和SASS的信息,但是我们并不知道这都是什么意思,这篇文章就是为我们而准备的.在调查这些语言后,我已经发现它们都是一些js文件,运行后会产生相应CSS文件给我们.为什么我们要使用LESS和SASS它们呢,因为我们可以避免CSS中需要的重复…...

建设银行的登录网站/百度搜索引擎关键词优化

题解报告&#xff08;CDUT暑期集训——第二场&#xff09; D - Game HDU - 6312 思路&#xff1a;水题 Alice一直是必胜态AC代码#include<stdio.h> #include<iostream> #include<math.h> #include<algorithm> #include<string> #include<stri…...

南京汽车集团网站建设/杭州百度快照优化排名推广

OpenCV提供createTrackbar()函式&#xff0c;可以在視窗上產生滑桿&#xff0c;讓使用者自己調整輸入&#xff0c;接著用這輸入值執行預計的操作&#xff0c;另外有getTrackbarPos()和setTrackbarPos()函式&#xff0c;讓我們對滑桿進行進一步的操作。 OpenCV 產生滑桿 int c…...

软件下载网站 知乎/搜索引擎免费登录入口

1.安装查看是否安装了FTP&#xff1a;rpm -qa|grep vsftpd安装&#xff1a;yum -y install vsftpdchkconfig vsftpd on 启动vsftpd&#xff1a; systemctl start vsftpd.service vsftpd几种用户&#xff1a;本地用户、虚拟用户、匿名用户。 主动模式、被动模式&#xff1a; POR…...