MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)
1.1创建ThreadLocal工具类(作为业务逻辑结果存放类)
package org.springblade.sample.utils;public class QueryContext {private static final ThreadLocal<Long> totalInThreadLocal = new ThreadLocal<>();public static void setTotalIn(long totalIn) {totalInThreadLocal.set(totalIn);}public static long getTotalIn() {return totalInThreadLocal.get() != null ? totalInThreadLocal.get() : 0;}public static void clear() {totalInThreadLocal.remove();}
}
2.1重构Mybatis-plus分页查询total总数逻辑(通过实现InnerInterceptor接口进行重构,代码如下,根据注释进行对应业务调整即可)
package org.springblade.sample.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.parser.JsqlParserGlobal;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;
import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;
import groovy.util.logging.Slf4j;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.*;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.sample.utils.QueryContext;import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;/*** 分页拦截器* <p>* 默认对 left join 进行优化,虽然能优化count,但是加上分页的话如果1对多本身结果条数就是不正确的** @author hubin* @since 3.4.0*/
@lombok.extern.slf4j.Slf4j
@Data
@NoArgsConstructor
@org.springframework.context.annotation.Configuration
@Slf4j
public class PaginationInnerInterceptor implements InnerInterceptor {/*** 获取jsqlparser中count的SelectItem*/protected static final List<SelectItem> COUNT_SELECT_ITEM = Collections.singletonList(new SelectExpressionItem(new Column().withColumnName("COUNT(*)")).withAlias(new Alias("total")));protected static final Map<String, MappedStatement> countMsCache = new ConcurrentHashMap<>();protected final Log logger = LogFactory.getLog(this.getClass());/*** 表名*/private final String V_SAMPLE_COVID19 = "v_sample_covid19";private final String T_SAMPLE_COVID19 = "t_sample_covid19";private final String T_SAMPLE_FLU = "t_sample_flu";private final String T_SAMPLE_HADV = "t_sample_hadv";private final String T_SAMPLE_HMPV = "t_sample_hmpv";private final String T_SAMPLE_HPIV = "t_sample_hpiv";private final String T_SAMPLE_METAV = "t_sample_metav";private final String T_SAMPLE_RSV = "t_sample_rsv";/*** 溢出总页数后是否进行处理*/protected boolean overflow;/*** 单页分页条数限制*/protected Long maxLimit;/*** 数据库类型* <p>* 查看 {@link #findIDialect(Executor)} 逻辑*/private DbType dbType;/*** 方言实现类* <p>* 查看 {@link #findIDialect(Executor)} 逻辑*/private IDialect dialect;/*** 生成 countSql 优化掉 join* 现在只支持 left join** @since 3.4.2*/protected boolean optimizeJoin = true;public PaginationInnerInterceptor(DbType dbType) {this.dbType = dbType;}public PaginationInnerInterceptor(IDialect dialect) {this.dialect = dialect;}/**** 重构willDoQuery获取序列未上传数量(根据检索结果进行实时更新)* @param executor Executor(可能是代理对象)* @param ms MappedStatement* @param parameter parameter* @param rowBounds rowBounds* @param resultHandler resultHandler* @param boundSql boundSql*/public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);if (page == null || page.getSize() < 0 || !page.searchCount() || resultHandler != Executor.NO_RESULT_HANDLER) {return true;}BoundSql countSql;BoundSql countSqlIn;MappedStatement countMs = buildCountMappedStatement(ms, page.countId());if (countMs != null) {countSql = countMs.getBoundSql(parameter);countSqlIn = countMs.getBoundSql(parameter);} else {countMs = buildAutoCountMappedStatement(ms);String countSqlStr = autoCountSql(page, boundSql.getSql());PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter);countSqlIn = new BoundSql(countMs.getConfiguration(), countSqlStr + " and seq_num = '0'", mpBoundSql.parameterMappings(), parameter);PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());PluginUtils.setAdditionalParameter(countSqlIn, mpBoundSql.additionalParameters());}// 执行第一个查询 (result)CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql);List<Object> result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql);long total = 0;if (CollectionUtils.isNotEmpty(result)) {// 个别数据库 count 没数据不会返回 0Object o = result.get(0);if (o != null) {total = Long.parseLong(o.toString());}}page.setTotal(total);long totalIn = 0;//获取表名String tableName = getTableFromSql(boundSql.getSql());// 执行第二个查询 (resultIn)if (StringUtil.isNotBlank(tableName)) {//根据表名判断是否进行查询if (this.suitTableName(tableName)) {CacheKey cacheKeyIn = executor.createCacheKey(countMs, parameter, rowBounds, countSqlIn);List<Object> resultIn = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKeyIn, countSqlIn);log.info("未上传序列数:{}", Long.parseLong(resultIn.get(0).toString()));if (CollectionUtils.isNotEmpty(resultIn)) {// 个别数据库 count 没数据不会返回 0Object o = resultIn.get(0);if (o != null) {totalIn = Long.parseLong(o.toString());}}}}// 将 totalIn 设置到 ThreadLocal 中QueryContext.setTotalIn(totalIn);return continuePage(page);}@Overridepublic void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);if (null == page) {return;}// 处理 orderBy 拼接boolean addOrdered = false;String buildSql = boundSql.getSql();List<OrderItem> orders = page.orders();if (CollectionUtils.isNotEmpty(orders)) {addOrdered = true;buildSql = this.concatOrderBy(buildSql, orders);}// size 小于 0 且不限制返回值则不构造分页sqlLong _limit = page.maxLimit() != null ? page.maxLimit() : maxLimit;if (page.getSize() < 0 && null == _limit) {if (addOrdered) {PluginUtils.mpBoundSql(boundSql).sql(buildSql);}return;}handlerLimit(page, _limit);IDialect dialect = findIDialect(executor);final Configuration configuration = ms.getConfiguration();DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize());PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);List<ParameterMapping> mappings = mpBoundSql.parameterMappings();Map<String, Object> additionalParameter = mpBoundSql.additionalParameters();model.consumers(mappings, configuration, additionalParameter);mpBoundSql.sql(model.getDialectSql());mpBoundSql.parameterMappings(mappings);}/*** 获取分页方言类的逻辑** @param executor Executor* @return 分页方言类*/protected IDialect findIDialect(Executor executor) {if (dialect != null) {return dialect;}if (dbType != null) {dialect = DialectFactory.getDialect(dbType);return dialect;}return DialectFactory.getDialect(JdbcUtils.getDbType(executor));}/*** 获取指定的 id 的 MappedStatement** @param ms MappedStatement* @param countId id* @return MappedStatement*/protected MappedStatement buildCountMappedStatement(MappedStatement ms, String countId) {if (StringUtils.isNotBlank(countId)) {final String id = ms.getId();if (!countId.contains(StringPool.DOT)) {countId = id.substring(0, id.lastIndexOf(StringPool.DOT) + 1) + countId;}final Configuration configuration = ms.getConfiguration();try {return CollectionUtils.computeIfAbsent(countMsCache, countId, key -> configuration.getMappedStatement(key, false));} catch (Exception e) {logger.warn(String.format("can not find this countId: [\"%s\"]", countId));}}return null;}/*** 构建 mp 自用自动的 MappedStatement** @param ms MappedStatement* @return MappedStatement*/protected MappedStatement buildAutoCountMappedStatement(MappedStatement ms) {final String countId = ms.getId() + "_mpCount";final Configuration configuration = ms.getConfiguration();return CollectionUtils.computeIfAbsent(countMsCache, countId, key -> {MappedStatement.Builder builder = new MappedStatement.Builder(configuration, key, ms.getSqlSource(), ms.getSqlCommandType());builder.resource(ms.getResource());builder.fetchSize(ms.getFetchSize());builder.statementType(ms.getStatementType());builder.timeout(ms.getTimeout());builder.parameterMap(ms.getParameterMap());builder.resultMaps(Collections.singletonList(new ResultMap.Builder(configuration, Constants.MYBATIS_PLUS, Long.class, Collections.emptyList()).build()));builder.resultSetType(ms.getResultSetType());builder.cache(ms.getCache());builder.flushCacheRequired(ms.isFlushCacheRequired());builder.useCache(ms.isUseCache());return builder.build();});}/*** 获取自动优化的 countSql** @param page 参数* @param sql sql* @return countSql*/protected String autoCountSql(IPage<?> page, String sql) {if (!page.optimizeCountSql()) {return lowLevelCountSql(sql);}try {Select select = (Select) JsqlParserGlobal.parse(sql);SelectBody selectBody = select.getSelectBody();// https://github.com/baomidou/mybatis-plus/issues/3920 分页增加union语法支持if (selectBody instanceof SetOperationList) {return lowLevelCountSql(sql);}PlainSelect plainSelect = (PlainSelect) select.getSelectBody();Distinct distinct = plainSelect.getDistinct();GroupByElement groupBy = plainSelect.getGroupBy();// 包含 distinct、groupBy 不优化if (null != distinct || null != groupBy) {return lowLevelCountSql(select.toString());}// 优化 order by 在非分组情况下List<OrderByElement> orderBy = plainSelect.getOrderByElements();if (CollectionUtils.isNotEmpty(orderBy)) {boolean canClean = true;for (OrderByElement order : orderBy) {// order by 里带参数,不去除order byExpression expression = order.getExpression();if (!(expression instanceof Column) && expression.toString().contains(StringPool.QUESTION_MARK)) {canClean = false;break;}}if (canClean) {plainSelect.setOrderByElements(null);}}for (SelectItem item : plainSelect.getSelectItems()) {if (item.toString().contains(StringPool.QUESTION_MARK)) {return lowLevelCountSql(select.toString());}}// 包含 join 连表,进行判断是否移除 join 连表if (optimizeJoin && page.optimizeJoinOfCountSql()) {List<Join> joins = plainSelect.getJoins();if (CollectionUtils.isNotEmpty(joins)) {boolean canRemoveJoin = true;String whereS = Optional.ofNullable(plainSelect.getWhere()).map(Expression::toString).orElse(StringPool.EMPTY);// 不区分大小写whereS = whereS.toLowerCase();for (Join join : joins) {if (!join.isLeft()) {canRemoveJoin = false;break;}FromItem rightItem = join.getRightItem();String str = "";if (rightItem instanceof Table) {Table table = (Table) rightItem;str = Optional.ofNullable(table.getAlias()).map(Alias::getName).orElse(table.getName()) + StringPool.DOT;} else if (rightItem instanceof SubSelect) {SubSelect subSelect = (SubSelect) rightItem;/* 如果 left join 是子查询,并且子查询里包含 ?(代表有入参) 或者 where 条件里包含使用 join 的表的字段作条件,就不移除 join */if (subSelect.toString().contains(StringPool.QUESTION_MARK)) {canRemoveJoin = false;break;}str = subSelect.getAlias().getName() + StringPool.DOT;}// 不区分大小写str = str.toLowerCase();if (whereS.contains(str)) {/* 如果 where 条件里包含使用 join 的表的字段作条件,就不移除 join */canRemoveJoin = false;break;}for (Expression expression : join.getOnExpressions()) {if (expression.toString().contains(StringPool.QUESTION_MARK)) {/* 如果 join 里包含 ?(代表有入参) 就不移除 join */canRemoveJoin = false;break;}}}if (canRemoveJoin) {plainSelect.setJoins(null);}}}// 优化 SQLplainSelect.setSelectItems(COUNT_SELECT_ITEM);return select.toString();} catch (JSQLParserException e) {// 无法优化使用原 SQLlogger.warn("optimize this sql to a count sql has exception, sql:\"" + sql + "\", exception:\n" + e.getCause());} catch (Exception e) {logger.warn("optimize this sql to a count sql has error, sql:\"" + sql + "\", exception:\n" + e);}return lowLevelCountSql(sql);}/*** 无法进行count优化时,降级使用此方法** @param originalSql 原始sql* @return countSql*/protected String lowLevelCountSql(String originalSql) {return SqlParserUtils.getOriginalCountSql(originalSql);}/*** 查询SQL拼接Order By** @param originalSql 需要拼接的SQL* @return ignore*/public String concatOrderBy(String originalSql, List<OrderItem> orderList) {try {Select select = (Select) JsqlParserGlobal.parse(originalSql);SelectBody selectBody = select.getSelectBody();if (selectBody instanceof PlainSelect) {PlainSelect plainSelect = (PlainSelect) selectBody;List<OrderByElement> orderByElements = plainSelect.getOrderByElements();List<OrderByElement> orderByElementsReturn = addOrderByElements(orderList, orderByElements);plainSelect.setOrderByElements(orderByElementsReturn);return select.toString();} else if (selectBody instanceof SetOperationList) {SetOperationList setOperationList = (SetOperationList) selectBody;List<OrderByElement> orderByElements = setOperationList.getOrderByElements();List<OrderByElement> orderByElementsReturn = addOrderByElements(orderList, orderByElements);setOperationList.setOrderByElements(orderByElementsReturn);return select.toString();} else if (selectBody instanceof WithItem) {// todo: don't known how to resolereturn originalSql;} else {return originalSql;}} catch (JSQLParserException e) {logger.warn("failed to concat orderBy from IPage, exception:\n" + e.getCause());} catch (Exception e) {logger.warn("failed to concat orderBy from IPage, exception:\n" + e);}return originalSql;}protected List<OrderByElement> addOrderByElements(List<OrderItem> orderList, List<OrderByElement> orderByElements) {List<OrderByElement> additionalOrderBy = orderList.stream().filter(item -> StringUtils.isNotBlank(item.getColumn())).map(item -> {OrderByElement element = new OrderByElement();element.setExpression(new Column(item.getColumn()));element.setAsc(item.isAsc());element.setAscDescPresent(true);return element;}).collect(Collectors.toList());if (CollectionUtils.isEmpty(orderByElements)) {return additionalOrderBy;}// github pull/3550 优化排序,比如:默认 order by id 前端传了name排序,设置为 order by name,idadditionalOrderBy.addAll(orderByElements);return additionalOrderBy;}/*** count 查询之后,是否继续执行分页** @param page 分页对象* @return 是否*/protected boolean continuePage(IPage<?> page) {if (page.getTotal() <= 0) {return false;}if (page.getCurrent() > page.getPages()) {if (overflow) {//溢出总页数处理handlerOverflow(page);} else {// 超过最大范围,未设置溢出逻辑中断 list 执行return false;}}return true;}/*** 处理超出分页条数限制,默认归为限制数** @param page IPage*/protected void handlerLimit(IPage<?> page, Long limit) {final long size = page.getSize();if (limit != null && limit > 0 && (size > limit || size < 0)) {page.setSize(limit);}}/*** 处理页数溢出,默认设置为第一页** @param page IPage*/protected void handlerOverflow(IPage<?> page) {page.setCurrent(1);}@Overridepublic void setProperties(Properties properties) {PropertyMapper.newInstance(properties).whenNotBlank("overflow", Boolean::parseBoolean, this::setOverflow).whenNotBlank("dbType", DbType::getDbType, this::setDbType).whenNotBlank("dialect", ClassUtils::newInstance, this::setDialect).whenNotBlank("maxLimit", Long::parseLong, this::setMaxLimit).whenNotBlank("optimizeJoin", Boolean::parseBoolean, this::setOptimizeJoin);}protected String getTableFromSql(String sql) {String regex = "(?i)from\\s+([a-zA-Z0-9_]+)";Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(sql);if (matcher.find()) {return matcher.group(1);}return null;}/*** 判断是否进行未上传序列数计算* @param tableName 表名*/private boolean suitTableName(String tableName) {switch (tableName) {case V_SAMPLE_COVID19:case T_SAMPLE_RSV:case T_SAMPLE_COVID19:case T_SAMPLE_FLU:case T_SAMPLE_HADV:case T_SAMPLE_HMPV:case T_SAMPLE_HPIV:case T_SAMPLE_METAV:return true;default:return false;}}
}
相关文章:
MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)
1.1创建ThreadLocal工具类(作为业务逻辑结果存放类) package org.springblade.sample.utils;public class QueryContext {private static final ThreadLocal<Long> totalInThreadLocal new ThreadLocal<>();public static void setTotalIn…...
Jmeter对图片验证码的处理【超详细】
Jmeter对图片验证码的处理 在web端的登录接口经常会有图片验证码的输入,而且每次登录时图片验证码都是随机的;当通过jmeter做接口登录的时候要对图片验证码进行识别出图片中的字段,然后再登录接口中使用; 通过jmeter对图片验证码…...
匈牙利算法
匈牙利算法 1 概念2 依据3 目的4步骤5 案例6 测试 1 概念 匈牙利算法基于代价矩阵找到最小代价的分配方法,是解决分配问题中最优匹配(最小代价)的算法。 2 依据 代价矩阵的行或列同时加或减一个数,得到新的代价矩阵的最优匹配与原代价矩阵相同。 3 目…...
nginx学习总结(不包含安装过程)
1. nginx常见配置 http服务上支持【若干虚拟主机】。每个虚拟主机对应一个server配置项,配置项里面包含该虚拟主机相关的配置。 server{listen 80 default;server_name www.yonqin.com;index index.html index.htm index.php;root /data/www;location ~ .*\.(gif|…...
Android10 rk3399 以太网接入流程分析
Netd守护进程服务 Netd模块是Android中专门负责网络管理和控制的后台守护进程开发板路径./etc/init/netd.rc service netd /system/bin/netdclass mainsocket dnsproxyd stream 0660 root inetsocket mdns stream 0660 root systemsocket fwmarkd stream 0660 root inetonres…...
HTML与数据抓取:GET与POST方法详解
讲GET和POST就不能只讲GET和POST 你要讲HTTP请求的基本概念: HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最为广泛的一种网络协议,主要用于Web浏览器与Web服务器之间的数据通信。HTTP是一个基于…...
DG常用启动方法与异常查找
--查看主库的状态: select a.inst_id,a.db_unique_name,a.database_role, a.protection_level,a.protection_mode,a.open_mode,a.log_mode,a.switchover_status, b.host_name,b.thread# from gv$database a left join gv$instance b on a.inst_idb.inst_id order by…...
C++ OpenGL学习笔记(4、绘制贴图纹理)
相关链接: C OpenGL学习笔记(1、Hello World空窗口程序) C OpenGL学习笔记(2、绘制橙色三角形绘制、绿色随时间变化的三角形绘制) C OpenGL学习笔记(3、绘制彩色三角形、绘制彩色矩形) 通过前面…...
以二进制形式创建gitea仓库
1、官方文档: 数据库准备 | Gitea Documentation 使用二进制文件安装 | Gitea Documentation 2、具体操作 1)创建gitea数据库 2)检查是否安装 Git。要求 Git 版本 > 2.0。 如需升级git请参考以下链接:linux升级git版本-C…...
数据库系统原理:数据库安全性与权限控制
2.1vue技术 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。 [5] 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项…...
bugkctf 渗透测试1超详细版
bugkctf 渗透测试1 下发环境进行访问 场景1 通过查看网页源代码成功找到1个flag 得到flag和提示 <!--flag{950d83a16ad47127859d414009432171} PS:下个flag网站管理员才能看到哦-->场景2 根据提示需要管理员权限,进行登录,直接登录需要邮箱和…...
window安装TradingView
目录 下载安装包 修改文件后缀,解压 将K线换成国内涨红跌绿样式 下载安装包 https://www.tradingview.com/desktop/ 下载完成后是.msix格式文件 (我在win10和win11的系统中尝试运行msix都没有成功,所以放弃直接双击运行msixÿ…...
网络下载ts流媒体
网络下载ts流媒体 查看下载排序合并 很多视频网站,尤其是微信小程序中的长视频无法获取到准确视频地址,只能抓取到.ts片段地址,下载后发现基本都是5~8秒时长。 例如: 我们需要将以上地址片段全部下载后排序后再合成新的长视频。 …...
log4j2漏洞复现(CVE-2021-44228)
靶场环境 步骤一:设置出战规则 步骤二:开启靶场 cd vulhub cd log4j cd CVE-2021-44228 docker-compose up -d docker ps 访问端口 靶机开启 步骤三:外带注入 获得dnslog 靶机访问dnslog 得到dnslog的二级域名信息 步骤四:构造…...
应用(APP)部署容器化演进之路
应用(Application)部署容器化演进之路 一、应用程序部署痛点 1.1 应用程序部署流程 举例:部署一个JAVA编程语言开发的Web应用,以War包放入Tomcat方式部署。 部署过程如下: 服务器配置运行环境:JAVA代码运行环境&am…...
Pytorch | 从零构建EfficientNet对CIFAR10进行分类
Pytorch | 从零构建EfficientNet对CIFAR10进行分类 CIFAR10数据集EfficientNet设计理念网络结构性能特点应用领域发展和改进 EfficientNet结构代码详解结构代码代码详解MBConv 类初始化方法前向传播 forward 方法 EfficientNet 类初始化方法前向传播 forward 方法 训练过程和测…...
Saprk和Flink的区别
1 、设计理念方面 Spark 的技术理念是使用微批来模拟流的计算,基于 Micro-batch ,数据流以时间为单位被切分为一个个 批次,通过分布式数据集RDD 进行批量处理,是一种伪实时。 Flink 是基于事件驱动的,是面向流的处理…...
Spring Boot 动态定时任务管理系统(轻量级实现)
Spring Boot项目中,实现动态增删和启停定时任务的功能对于许多应用场景来说至关重要。虽然Quartz框架是一个广泛使用的解决方案,但其复杂性和重量级特性可能使得项目变得臃肿和难以维护。为了解决这个问题,本项目旨在实现一个轻量级的定时任务…...
开发微信小程序的过程与心得
起因 作为家长,我近期参与了学校的护学岗工作。在这个过程中,我发现需要使用水印相机来记录护学活动,但市面上大多数水印相机应用都要求开通会员才能使用完整功能。作为一名程序员,我决定利用自己的技术背景,开发一个…...
项目代码第6讲:UpdownController.cs;理解 工艺/工序 流程、机台信息;前端的“历史 警报/工艺 记录”
一、UpdownController.cs 1、前端传入 当用户在下图的“记录查询”中的 两个界面选项 中,点击“导出”功能时,向后端发起请求,请求服务器下载文件的权限 【权限是在Program.cs中检测的,这个控制器里只需要进行“谁在哪个接口下载了文件”的日志记录】 【导出:是用户把…...
Apache RocketMQ 5.1.3安装部署文档
官方文档不好使,可以说是一坨… 关键词:Apache RocketMQ 5.0 JDK 17 废话少说,开整。 1.版本 官网地址,版本如下。 https://rocketmq.apache.org/download2.配置文件 2.1namesrv端口 在ROCKETMQ_HOME/conf下 新增namesrv.pro…...
uni-app开发收货地址管理
目录 一:功能描述 二:功能实现 一:功能描述 收货地址在个人中心的我的地址里面,点击我的地址可以查看我的收货地址列表,可以新增收货地址,点击特定收货地址可以编辑和删除该地址信息。 二:功能实现 1:收货地址列表 <view v-for="(item, index) in data_lis…...
BOE(京东方)“向新2025”年终媒体智享会落地成都 持续创新引领产业步入高价值增长新纪元
12月20日,BOE(京东方)“向新 2025”年终媒体智享会的脚步从上海延伸至成都。川渝之地,作为 BOE(京东方)产业生态战略布局中的关键一子,此刻再度成为行业瞩目的焦点。本次活动全面回溯了BOE(京东方)在2024年多个关键领域斩获的斐然佳绩,深入剖析了六大维度构建的“向新”发展格局…...
JavaWeb - ⭐ AOP 面相切面编程原理及用户校验功能实战
一、概述 定义: AOP (Aspect Oriented Programming 面向切面编程) ,一种面向方法编程的思想 功能:管理 bean 对象的过程中,通过底层的动态代理机制对特定方法进行功能的增强或改变 实现方式:动态代理技术,…...
鸿道Intewell工业操作系统,三大关键技术,领跑行业前沿
鸿道工业操作系统,是软件定义控制的工业实时操作系统,具备三大关键技术: 1、确定性计算与高实时响应(微秒级、关键指标) 提供确定性的计算与高实时的系统环境,保障工业生产运作的灵活高效。 2、微内核、并发处理(微内核的强大…...
java 对ElasticSearch数据库操作封装工具类(对你是否适用嘞)
在 Java 中封装 ElasticSearch(ES)数据库操作(ES版本之间变动挺大的,别轻易换版本),可以使得与 ES 的交互更加简洁和易于维护。通过封装常见的操作,如插入文档、查询、更新和删除等,…...
HarmonyOS NEXT 技术实践-基于基础视觉服务实现骨骼点识别
本示例展示了如何在HarmonyOS Next中实现基于基础视觉服务的骨骼点识别功能。骨骼点识别是计算机视觉中的一项重要技术,广泛应用于运动分析、健身监控和增强现实等领域。通过使用HarmonyOS Next提供的视觉API,开发者能够轻松地对人物图像进行骨骼点检测&…...
广州大学计算机组成原理课程设计
一.课设性质,目的,任务 《计算机组成与系统结构课程设计》是计算机学院各专业集中实践性环节之一,是学习完《计算机组成与系统结构》课程后进行的一次全面的综合练习。其目的是综合运用所学计算机原理知识,设计并实现一台模型计算…...
【漏洞复现】CVE-2021-45788 SQL Injection
漏洞信息 NVD - cve-2021-45788 Time-based SQL Injection vulnerabilities were found in Metersphere v1.15.4 via the “orders” parameter. Authenticated users can control the parameters in the “order by” statement, which causing SQL injection. API: /test…...
【Java基础面试题035】什么是Java泛型的上下界限定符?
回答重点 Java泛型的上下界限定符用于对泛型类型参数进行范围限制,主要有上界限定符和下届限定符。 1)上界限定符 (? extends T): 定义:通配符?的类型必须是T或者T的子类,保证集合元素一定是T或者T的子类作用&…...
无需GPU也能跑的修图神器(祛痣、去水印、去路人)
背景 在这个视觉内容创作盛行的时代,大家对图片质量要求愈发严苛。无论是日常分享的自拍照想祛痣变得完美无瑕,还是摄影作品需去水印彰显原创,亦或是旅游打卡照要去路人还原美景。如今,一款能在 CPU 上流畅运行的修改神器应运而生…...
R语言数据分析案例47-上海译文出版社旗舰店图书分析和可视化
一、研究背景 随着数字化时代的发展,图书出版行业面临着日益激烈的市场竞争。上海译文出版社作为一家知名的出版机构,其旗舰店的图书销售数据蕴含着丰富的信息。对最新入库图书进行深入分析和可视化呈现,有助于出版社更好地了解市场动态、读…...
牵手红娘:牵手App红娘助力“牵手”,脱单精准更便捷
随着互联网的普及,现代青年的社交圈层加速扩大,他们的恋爱观也正经历着前所未有的转变。在繁忙的工作之余,人们希望能够找到一种既高效又真诚的交友方式。于是,线上交友平台成为了他们寻找爱情的新选择。让不同文化背景、不同工作…...
什么是根服务器?有什么作用?
你知道什么是根服务器吗?在互联网的庞大架构中,根服务器很多人对它的了解并不深入。那么,根服务器到底是什么,它有什么作用呢? 什么是根服务器? 根服务器是互联网域名系统(DNS)的一部分,负责管理和维护最顶层的域名信息。简单…...
Tavily人工智能代理和检索增强生成应用设计的搜索引擎
Tavily人工智能代理和检索增强生成应用设计的搜索引擎 Tavily是一款专为人工智能代理和检索增强生成应用设计的搜索引擎.以下是其使用示例: 安装与配置 需先安装相应的包,并获取API密钥来设置环境变量. %pip install -qU "langchain-community>=0.2.11" tavi…...
web的五个Observer API
IntersectionObserver: 一个元素从不可见到可见,从可见到不可见 ??IntersectionObserver是一种浏览器提供的 JavaScript API,用于监测元素与视窗的交叉状态。它可以告诉开发者一个元素是否进入或离开视窗,以及两者的交叉区域的…...
CSS学习记录19
CSS文本效果 CSS text-overflow 属性规定应如何向用户呈现未显示的溢出的内容。 //裁剪 text-overflow: clip; //隐藏 text-overflow: ellipsis; CSS字换行(Word Wrapping) CSS word-wrap 属性使长文字能够被折断并换到下一行。 如果一个单词太长而…...
使用Amazon Bedrock的无服务器的智能工作流
使用Amazon Bedrock的无服务器的智能工作流 智能工作流基于用户输入处理不可预见的任务,比如发起API调用。无服务器架构可以高效地管理这些任务和不同的工作负载,而无需维护服务器,从而加快部署速度。 你将学习如何通过使用带有保护措施的智能…...
SDMTSP:粒子群优化算法PSO求解单仓库多旅行商问题,可以更改数据集和起点(MATLAB代码)
一、单仓库多旅行商问题 单仓库多旅行商问题(Single-Depot Multiple Travelling Salesman Problem, SD-MTSP):𝑚个推销员从同一座中心城市出发,访问其中一定数量的城市并且每座城市只能被某一个推销员访问一次&#x…...
Java中的访问修饰符:分类、作用及应用场景
在Java编程中,访问修饰符(Access Modifiers)是控制类、方法、变量和构造函数访问权限的关键工具。通过合理使用访问修饰符,可以有效地封装代码,保护数据,并确保代码的安全性和可维护性。本文将详细介绍Java…...
【day16】Java开发常用API
模块15回顾 在深入探讨模块16之前,让我们回顾一下【day15】中的重点内容: String类: 构造方法:String(), String(String s), String(char[] chars), String(byte[] bytes), String(char[] chars, int offset, int count), String…...
LLaMA-Factory(二)界面解析
这里写目录标题 1. 基础选项语言模型选择模型路径微调方法检查点路径量化等级量化方法提示模板RoPE加速方式 2.模型训练界面训练方式数据集超参数设置其他参数部分参数设置LoRA参数设置RLHF参数设置GaLore参数设置BAdam参数设置训练 3.评估预测界面4.Chat界面5.导出Export界面 …...
(14)CT137A- 动态数码管设计
(1)了解板卡原理图中数码管的特性:共阳极数码管,公共端连接了电源,FPGA IO口低电平有效,另外,可以看到位选端FPGA位选低电平时选通。 (2)刷新时间的设定:众所…...
RT-DETR学习笔记(2)
七、IOU-aware query selection 下图是原始DETR。content query 是初始化为0的label embedding, position query 是通过nn.Embedding初始化的一个嵌入矩阵,这两部分没有任何的先验信息,导致DETR的收敛慢。 RT-DETR则提出要给这两部分(conten…...
JS、JSX 和 TSX 的对比指南
JS、JSX 和 TSX 的对比指南 1. JavaScript (.js) 简介 JavaScript 是一种轻量级的、解释型的编程语言,是 Web 开发中最基础的语言之一。 优点 学习曲线较低,容易入门使用范围广泛,生态系统成熟灵活性高,支持多种编程范式不需…...
【STM32】RTT-Studio中HAL库开发教程十:EC800M-4G模块使用
文章目录 一、简介二、模块测试三、OneNet物联网配置四、完整代码五、测试验证 一、简介 EC800M4G是一款4G模块,本次实验主要是进行互联网的测试,模块测试,以及如何配置ONENET设备的相关参数,以及使用STM32F4来测试模块的数据上报…...
增加nginx配置文件(conf.d), 管理多个项目
1.切换到nginx目录下, 新建conf.d文件夹 mkdir conf.d 2.赋予conf.d权限 chmod 777 conf.d 3.进入conf.d, 编辑conf文件 vim zc_travel.conf server { listen 13101; server_name localhost;location / {root /home/baoxin/app/web/insight-radar-rcfx-pre/html_dev;index …...
服务器中了挖矿病毒-应急响应
事件:客户反映服务器响应很慢,出行卡顿,服务器操作系统为windows server 2008。 现场解决步骤: 1、打开任务管理器,看到一个javs.exe的程序,占用CPU使用率比较高,怀疑可能是木马。 2、右键打开…...
pid学习感悟
飞行控制PID算法——无人机飞控_飞控算法-CSDN博客 首先pid是对现测量值和目标值的差值,即偏差值的操作 p是线性操作,偏差越大,则对于控制单元操作的值越大 d是微分操作,我们知道微分其实就是数值变化的斜率,所以当偏…...
Centos7.9安装openldap+phpldapadmin+grafana配置LDAP登录最详细步骤 亲测100%能行
一、部署LDAP 1、安装LDAP yum install -y openldap-servers openldap-clients openldap openldap-devel compat-openldap openldap-servers-sql systemctl start slapd systemctl enable slapd2、创建第一个管理账号密码(设置为ldapadmin) slappass…...