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

聊聊mybatis-plus的sql加载顺序

本文主要研究一下如果mybatis mapper定义了多个同名方法会不会有问题

MybatisConfiguration

com/baomidou/mybatisplus/core/MybatisConfiguration.java

    /*** MybatisPlus 加载 SQL 顺序:* <p> 1、加载 XML中的 SQL </p>* <p> 2、加载 SqlProvider 中的 SQL </p>* <p> 3、XmlSql 与 SqlProvider不能包含相同的 SQL </p>* <p>调整后的 SQL优先级:XmlSql > sqlProvider > CurdSql </p>*/@Overridepublic void addMappedStatement(MappedStatement ms) {if (mappedStatements.containsKey(ms.getId())) {/** 说明已加载了xml中的节点; 忽略mapper中的 SqlProvider 数据*/logger.error("mapper[" + ms.getId() + "] is ignored, because it exists, maybe from xml file");return;}mappedStatements.put(ms.getId(), ms);}

MybatisSqlSessionFactoryBean

com/baomidou/mybatisplus/extension/spring/MybatisSqlSessionFactoryBean.java

    /*** Build a {@code SqlSessionFactory} instance.* <p>* The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a* {@code SqlSessionFactory} instance based on an Reader. Since 1.3.0, it can be specified a* {@link Configuration} instance directly(without config file).* </p>** @return SqlSessionFactory* @throws IOException if loading the config file failed*/protected SqlSessionFactory buildSqlSessionFactory() throws Exception {//......if (this.mapperLocations != null) {if (this.mapperLocations.length == 0) {LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");} else {for (Resource mapperLocation : this.mapperLocations) {if (mapperLocation == null) {continue;}try {XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());xmlMapperBuilder.parse();} catch (Exception e) {throw new IOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);} finally {ErrorContext.instance().reset();}LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");}}}}

MybatisSqlSessionFactoryBean的buildSqlSessionFactory方法会根据mapperLocations的配置取加载xml配置,即加载xml的mapper信息

XMLMapperBuilder

org/apache/ibatis/builder/xml/XMLMapperBuilder.java

  public void parse() {if (!configuration.isResourceLoaded(resource)) {configurationElement(parser.evalNode("/mapper"));configuration.addLoadedResource(resource);bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();}private void bindMapperForNamespace() {String namespace = builderAssistant.getCurrentNamespace();if (namespace != null) {Class<?> boundType = null;try {boundType = Resources.classForName(namespace);} catch (ClassNotFoundException e) {// ignore, bound type is not required}if (boundType != null && !configuration.hasMapper(boundType)) {// Spring may not know the real resource name so we set a flag// to prevent loading again this resource from the mapper interface// look at MapperAnnotationBuilder#loadXmlResourceconfiguration.addLoadedResource("namespace:" + namespace);configuration.addMapper(boundType);}}}

XMLMapperBuilder的parse方法会执行configurationElement,即加载xml的mapper方法,之后执行bindMapperForNamespace,加载对应java mapper的方法

MybatisMapperRegistry

com/baomidou/mybatisplus/core/MybatisMapperRegistry.java

    @Overridepublic <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {// TODO 如果之前注入 直接返回return;// TODO 这里就不抛异常了
//                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {// TODO 这里也换成 MybatisMapperProxyFactory 而不是 MapperProxyFactoryknownMappers.put(type, new MybatisMapperProxyFactory<>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.// TODO 这里也换成 MybatisMapperAnnotationBuilder 而不是 MapperAnnotationBuilderMybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}

MybatisMapperRegistry通过MybatisMapperAnnotationBuilder进行parse

MybatisMapperAnnotationBuilder

com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java

    public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) {loadXmlResource();configuration.addLoadedResource(resource);String mapperName = type.getName();assistant.setCurrentNamespace(mapperName);parseCache();parseCacheRef();IgnoreStrategy ignoreStrategy = InterceptorIgnoreHelper.initSqlParserInfoCache(type);for (Method method : type.getMethods()) {if (!canHaveStatement(method)) {continue;}if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()&& method.getAnnotation(ResultMap.class) == null) {parseResultMap(method);}try {// TODO 加入 注解过滤缓存InterceptorIgnoreHelper.initSqlParserInfoCache(ignoreStrategy, mapperName, method);parseStatement(method);} catch (IncompleteElementException e) {// TODO 使用 MybatisMethodResolver 而不是 MethodResolverconfiguration.addIncompleteMethod(new MybatisMethodResolver(this, method));}}// TODO 注入 CURD 动态 SQL , 放在在最后, because 可能会有人会用注解重写sqltry {// https://github.com/baomidou/mybatis-plus/issues/3038if (GlobalConfigUtils.isSupperMapperChildren(configuration, type)) {parserInjector();}} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new InjectorResolver(this));}}parsePendingMethods();}

这里通过反射获取对应java mapper的方法(这里的顺序是先接口本身定义的方法,然后是逐层继承的接口定义的方法),然后挨个执行parseStatement,接着执行parserInjector来处理内置的通过SqlMethod提供的内置方法

parseStatement

    private static final Set<Class<? extends Annotation>> statementAnnotationTypes = Stream.of(Select.class, Update.class, Insert.class, Delete.class, SelectProvider.class, UpdateProvider.class,InsertProvider.class, DeleteProvider.class).collect(Collectors.toSet());void parseStatement(Method method) {final Class<?> parameterTypeClass = getParameterType(method);final LanguageDriver languageDriver = getLanguageDriver(method);getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> {final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method);final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType();final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options) x.getAnnotation()).orElse(null);final String mappedStatementId = type.getName() + StringPool.DOT + method.getName();final KeyGenerator keyGenerator;String keyProperty = null;String keyColumn = null;if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {// first check for SelectKey annotation - that overrides everything elseSelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class).map(x -> (SelectKey) x.getAnnotation()).orElse(null);if (selectKey != null) {keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);keyProperty = selectKey.keyProperty();} else if (options == null) {keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;} else {keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;keyProperty = options.keyProperty();keyColumn = options.keyColumn();}} else {keyGenerator = NoKeyGenerator.INSTANCE;}Integer fetchSize = null;Integer timeout = null;StatementType statementType = StatementType.PREPARED;ResultSetType resultSetType = configuration.getDefaultResultSetType();boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = !isSelect;boolean useCache = isSelect;if (options != null) {if (FlushCachePolicy.TRUE.equals(options.flushCache())) {flushCache = true;} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {flushCache = false;}useCache = options.useCache();fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348timeout = options.timeout() > -1 ? options.timeout() : null;statementType = options.statementType();if (options.resultSetType() != ResultSetType.DEFAULT) {resultSetType = options.resultSetType();}}String resultMapId = null;if (isSelect) {ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);if (resultMapAnnotation != null) {resultMapId = String.join(StringPool.COMMA, resultMapAnnotation.value());} else {resultMapId = generateResultMapName(method);}}assistant.addMappedStatement(mappedStatementId,sqlSource,statementType,sqlCommandType,fetchSize,timeout,// ParameterMapIDnull,parameterTypeClass,resultMapId,getReturnType(method),resultSetType,flushCache,useCache,// TODO gcode issue #577false,keyGenerator,keyProperty,keyColumn,statementAnnotation.getDatabaseId(),languageDriver,// ResultSetsoptions != null ? nullOrEmpty(options.resultSets()) : null);});}

parseStatement这里解析带有Select.class, Update.class, Insert.class, Delete.class, SelectProvider.class, UpdateProvider.class, InsertProvider.class, DeleteProvider.class注解的方法,然后通过assistant.addMappedStatement注册到configuration的mappedStatements中,key为statementId(type.getName() + StringPool.DOT + method.getName())

parserInjector

com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java

    void parserInjector() {GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);}

com/baomidou/mybatisplus/core/injector/AbstractSqlInjector.java

    @Overridepublic void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);if (modelClass != null) {String className = mapperClass.toString();Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());if (!mapperRegistryCache.contains(className)) {TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);List<AbstractMethod> methodList = this.getMethodList(mapperClass, tableInfo);if (CollectionUtils.isNotEmpty(methodList)) {// 循环注入自定义方法methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));} else {logger.debug(mapperClass.toString() + ", No effective injection method was found.");}mapperRegistryCache.add(className);}}}

com/baomidou/mybatisplus/core/injector/AbstractMethod.java

    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {this.configuration = builderAssistant.getConfiguration();this.builderAssistant = builderAssistant;this.languageDriver = configuration.getDefaultScriptingLanguageInstance();/* 注入自定义方法 */injectMappedStatement(mapperClass, modelClass, tableInfo);}protected MappedStatement addInsertMappedStatement(Class<?> mapperClass, Class<?> parameterType, String id,SqlSource sqlSource, KeyGenerator keyGenerator,String keyProperty, String keyColumn) {return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.INSERT, parameterType, null,Integer.class, keyGenerator, keyProperty, keyColumn);}protected MappedStatement addMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource,SqlCommandType sqlCommandType, Class<?> parameterType,String resultMap, Class<?> resultType, KeyGenerator keyGenerator,String keyProperty, String keyColumn) {String statementName = mapperClass.getName() + DOT + id;if (hasMappedStatement(statementName)) {logger.warn(LEFT_SQ_BRACKET + statementName + "] Has been loaded by XML or SqlProvider or Mybatis's Annotation, so ignoring this injection for [" + getClass() + RIGHT_SQ_BRACKET);return null;}/* 缓存逻辑处理 */boolean isSelect = sqlCommandType == SqlCommandType.SELECT;return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType,null, null, null, parameterType, resultMap, resultType,null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn,configuration.getDatabaseId(), languageDriver, null);}

这里会通过statementName(mapperClass.getName() + DOT + id)j检测是否存在,如果不存在则添加

org/apache/ibatis/builder/MapperBuilderAssistant.java

  public MappedStatement addMappedStatement(String id,SqlSource sqlSource,StatementType statementType,SqlCommandType sqlCommandType,Integer fetchSize,Integer timeout,String parameterMap,Class<?> parameterType,String resultMap,Class<?> resultType,ResultSetType resultSetType,boolean flushCache,boolean useCache,boolean resultOrdered,KeyGenerator keyGenerator,String keyProperty,String keyColumn,String databaseId,LanguageDriver lang,String resultSets) {if (unresolvedCacheRef) {throw new IncompleteElementException("Cache-ref not yet resolved");}id = applyCurrentNamespace(id, false);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType).resource(resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired(valueOrDefault(flushCache, !isSelect)).useCache(valueOrDefault(useCache, isSelect)).cache(currentCache);ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);if (statementParameterMap != null) {statementBuilder.parameterMap(statementParameterMap);}MappedStatement statement = statementBuilder.build();configuration.addMappedStatement(statement);return statement;}public String applyCurrentNamespace(String base, boolean isReference) {if (base == null) {return null;}if (isReference) {// is it qualified with any namespace yet?if (base.contains(".")) {return base;}} else {// is it qualified with this namespace yet?if (base.startsWith(currentNamespace + ".")) {return base;}if (base.contains(".")) {throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);}}return currentNamespace + "." + base;}

添加的话,最后的id会拼接上当前的namespace

小结

如果mybatis mapper定义了多个同名方法,则启动时不会报错,但是会有error日志告知同名方法被忽略。整体加载顺序是xml的方法优先于java mapper定义的方法,优先于自定义的SqlMethod;而xml或者java mapper方法都是以最先出现的为准。

相关文章:

聊聊mybatis-plus的sql加载顺序

序 本文主要研究一下如果mybatis mapper定义了多个同名方法会不会有问题 MybatisConfiguration com/baomidou/mybatisplus/core/MybatisConfiguration.java /*** MybatisPlus 加载 SQL 顺序&#xff1a;* <p> 1、加载 XML中的 SQL </p>* <p> 2、加载 SqlP…...

基于jeecg-boot的flowable流程审批时增加下一个审批人设置

更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/nbcio-boot 前端代码&#xff1a;https://gitee.com/nbacheng/nbcio-vue.git 在线演示&#xff08;包括H5&#xff09; &#xff1a; http://122.227.135.243:9888 因为有时…...

HTML 与 CSS 有什么区别?

HTML&#xff08;超文本标记语言&#xff09;和 CSS&#xff08;层叠样式表&#xff09;是构建网页的两个核心技术。HTML负责定义网页的结构和内容&#xff0c;而CSS则用于控制网页的样式和布局。虽然它们在构建网页时密切相关&#xff0c;但它们在功能和用途上有明显的区别。 …...

服务器数据恢复-vmware ESXI虚拟机数据恢复案例

服务器数据恢复环境&#xff1a; 从物理机迁移一台虚拟机到ESXI&#xff0c;迁移后做了一个快照。该虚拟机上部署了一个SQLServer数据库&#xff0c;存放了5年左右的数据。ESXI上有数十台虚拟机&#xff0c;EXSI连接了一台EVA存储&#xff0c;所有的虚拟机都在EVA存储上。 服务…...

Rabbitmq的Shovel

Federation 具备的数据转发功能类似&#xff0c; Shovel 够可靠、持续地从一个 Broker 中的队列 ( 作为源端&#xff0c;即source)拉取数据并转发至另一个 Broker 中的交换器 ( 作为目的端&#xff0c;即 destination) 。作为源端的队列和作为目的端的交换器可以同时位于…...

华为手机实用功能介绍

一、内置app介绍 分四块介绍&#xff0c;包括出门款、规划款、工作款和生活款。 出门款&#xff1a;红色框框部分&#xff0c;照镜子化妆/看天气 规划款&#xff1a;黄色框框部分&#xff0c;日程表/计划表/番茄时间/计时 工作款&#xff1a;蓝色框框部分&#xff0c;便笺/录…...

算法题打卡day50-动态规划 | 123.买卖股票的最佳时机III、188.买卖股票的最佳时机IV

123. 买卖股票的最佳时机 III - 力扣&#xff08;LeetCode&#xff09; 状态&#xff1a;查看索引含义和初始化思路后AC。 增加了两次的限制&#xff0c;相应的就是需要考虑的状态改变&#xff0c;具体的索引含义在代码中&#xff1a; class Solution { public:int maxProfit(…...

jvm与锁

今天是《面霸的自我修养》的第二弹&#xff0c;内容是Java并发编程中关于Java内存模型&#xff08;Java Memory Model&#xff09;和锁的基础理论相关的问题。这两块内容的八股文倒是不多&#xff0c;但是难度较大&#xff0c;接下来我们就一起一探究竟吧。 数据来源&#xff…...

零基础安装pycuda

零基础安装pycuda 前言安装Visual Studio安装C/C环境添加环境变量 安装pycuda查看系统位数查看python版本下载whl文件 前言 最近开始学习基于python的cuda编程&#xff0c;记录一下pycuda的安装。 在安装pycuda之前&#xff0c;首先需要有NVIDIA的独立显卡并且要安装CUDA和CUD…...

Streamlit 讲解专栏(十一):数据可视化-图表绘制详解(中)

文章目录 1 前言2 绘制交互式散点图3 定制图表主题4 增强数据可视化的交互性与注释步骤1步骤二 5 结语 1 前言 在上一篇博文《 Streamlit 讲解专栏&#xff08;十&#xff09;&#xff1a;数据可视化-图表绘制详解&#xff08;上&#xff09;》中&#xff0c;我们学习了一些关…...

d3dx9_35.dll丢失怎么解决

今天&#xff0c;我将为大家介绍关于电脑d3dx9_35.dll丢失的4种详细修复方法。希望通过这次分享&#xff0c;能够帮助大家解决在日常工作和生活中遇到的一些问题。 首先&#xff0c;让我们来了解一下d3dx9_35.dll是什么&#xff1f; d3dx9_35.dll是一个非常重要的动态链接库文…...

Ansible自动化运维工具(二)

目录 &#xff08;6&#xff09;copy模块 &#xff08;7&#xff09;file模块 ​编辑​编辑&#xff08;8&#xff09;hostname模块 &#xff08;9&#xff09;ping模块 &#xff08;10&#xff09;yum 模块 &#xff08;11&#xff09;service/system模块 ​编辑 ​…...

uniapp中使用原生canvas标签绘制视频帧来模拟拍照,拍照后将图绘制在另外一个canvas上编辑画图,这样反复操作

uniapp中使用原生canvas标签绘制视频帧来模拟拍照&#xff0c;拍照后将图绘制在另外一个canvas上编辑画图&#xff0c;这样反复操作会导致ios系统上白屏&#xff0c;canvas2d上下文为null,经查阅找到相关资料 IOS 创建Canvas过多导致getContext(‘2d’) 返回null 总 Canvas 内存…...

机器视觉工程师们,学习是工作以外的事情

面试时&#xff0c;领导问你&#xff0c;很多技术问题&#xff0c;你永远的回答是&#xff0c;我可以学。 公司以为你来公司的目标就是学习&#xff0c;学完就跑。 那你进公司的目标到底是什么&#xff1f; 我认为你&#xff0c;你最好想好再回答。 对于每一家公司来说&#xf…...

数据驱动的生活:探索未来七天生活指数API的应用

前言 随着科技的不断发展&#xff0c;数据已经成为我们生活中不可或缺的一部分。从社交媒体上的点赞和分享&#xff0c;到电子邮件和搜索引擎的历史记录&#xff0c;数据正在以前所未有的速度积累。而这些数据的利用不仅仅停留在社交媒体或商业领域&#xff0c;它们还可以为我…...

【数据分享】2006-2021年我国城市级别的集中供热相关指标(免费获取\20多项指标)

《中国城市建设统计年鉴》中细致地统计了我国城市市政公用设施建设与发展情况&#xff0c;在之前的文章中&#xff0c;我们分享过基于2006-2021年《中国城市建设统计年鉴》整理的2006—2021年我国城市级别的市政设施水平相关指标、2006-2021年我国城市级别的各类建设用地面积数…...

2022年研究生数学建模竞赛优秀论文汇总

A题&#xff1a;移动场景超分辨定位问题 参考代码论文1 论文2 论文3 论文4 论文5 论文6 论文7B题&#xff1a; 方形件排样优化与订单组批问题探析 参考代码论文1 论文2 论文3 论文4 论文5 论文6 论文7C题&#xff1a; 汽车制造涂装-总装缓存调序区调度优化问题论文1 论文2 论文…...

阿里云申请免费SSL证书的两种验证方式及配置服务器Tomcat升级HTTPS协议

通用教程&#xff0c;其他服务商的免费 SSL 证书也差不多是这个流程。&#xff08;至少腾讯云的操作步骤和本文是一致&#xff0c;嘻嘻&#xff01;&#xff09; 申请 SSL 证书 首先在阿里云上创建并申请 SSL 证书&#xff0c;之后选择 DNS 验证的方式&#xff0c;一种是手动配…...

SQL Server 和 MySql 语法和关键字的区别

SQL Server 和 MySql 语法和关键字的区别 ——用于SQLServer到MySql的转换 mysql的ifnull()函数对应sql的isnull()函数;mysql的存储过程中变量的定义去掉;mysql的每句结束要用";"SQLServer存储过程的AS在MySql中需要用begin .....end替换字符串连接用concat()函数;…...

2023_Spark_实验三:基于IDEA开发Scala例子

一、创建一个空项目&#xff0c;作为整个项目的基本框架 二、创建SparkStudy模块&#xff0c;用于学习基本的Spark基础 三、创建项目结构 1、在SparkStudy模块下的pom.xml文件中加入对应的依赖&#xff0c;并等待依赖包下载完毕。 在pom.xml文件中加入对应的依赖 ​<!-- S…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表

##鸿蒙核心技术##运动开发##Sensor Service Kit&#xff08;传感器服务&#xff09;# 前言 在运动类应用中&#xff0c;运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据&#xff0c;如配速、距离、卡路里消耗等&#xff0c;用户可以更清晰…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...