Springboot整合Mybaits启动过程

Springboot整合Mybaits启动过程

  • 1.前言
  • 2.MybatisAutoConfiguration
  • 3.SqlSessionFactoryBean
    • 3.1 XMLConfigBuilder.parse()
      • 3.1.1 XMLMapperBuilder.parse()
        • 3.1.1.1 XMLStatementBuilder.parse()
  • 4.SqlSession
    • 4.1 Executor

1.前言

直接加载mybatis配置文件,然后创建SqlSessionFactory ,获取SqlSession来进行数据库操作

//加载配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("ybatis-config.xml");
//创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//通过sqlsessionfactory得到sqlsession(sqlsession是操作数据库的关键对象)
SqlSession sqlSession = sqlSessionFactory.openSession();
}

而当Springboot整合Mybatis 时,这个 SqlSessionFactory 对象应该交由 Spring 容器来管理。
Spirngboot自动装配:Spirngboot自动装配

2.MybatisAutoConfiguration

我们知道springboot开箱即用,就是因为它能够自动装配,而自动装配的原理,就时会从META-INFO下的spring.factories文件中获取要自动装配的类。
在这里插入图片描述
进入MybatisAutoConfiguration类中

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {......@Bean@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {// 创建SqlSessionFactoryBean对象SqlSessionFactoryBean factory = new SqlSessionFactoryBean();// 设置数据源factory.setDataSource(dataSource);// 设置VFS为SpringBootVFSfactory.setVfs(SpringBootVFS.class);if (StringUtils.hasText(this.properties.getConfigLocation())) {// 如果配置了配置文件路径,则设置ConfigLocationfactory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));}Configuration configuration = this.properties.getConfiguration();if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {// 如果未配置配置文件路径且没有指定配置对象,则创建一个新的Configuration对象configuration = new Configuration();}if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {// 如果存在配置对象且有配置自定义器,则依次调用自定义器的customize()方法对配置对象进行自定义for (ConfigurationCustomizer customizer : this.configurationCustomizers) {customizer.customize(configuration);}}// 设置配置对象factory.setConfiguration(configuration);if (this.properties.getConfigurationProperties() != null) {// 如果配置了额外的配置属性,则设置到SqlSessionFactoryBean中factory.setConfigurationProperties(this.properties.getConfigurationProperties());}if (!ObjectUtils.isEmpty(this.interceptors)) {// 如果存在拦截器,则设置到SqlSessionFactoryBean中factory.setPlugins(this.interceptors);}if (this.databaseIdProvider != null) {// 如果存在数据库ID提供者,则设置到SqlSessionFactoryBean中factory.setDatabaseIdProvider(this.databaseIdProvider);}if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {// 如果配置了类型别名包路径,则设置到SqlSessionFactoryBean中factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());}if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {// 如果配置了类型处理器包路径,则设置到SqlSessionFactoryBean中factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());}if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {// 如果存在Mapper文件路径,则解析并设置到SqlSessionFactoryBean中factory.setMapperLocations(this.properties.resolveMapperLocations());}// 返回创建的SqlSessionFactory对象return factory.getObject();}......
}
@org.springframework.context.annotation.Configuration: 
这个注解表示这个类是一个配置类,Spring会在启动时加载并解析它。@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }): 
这个注解表示只有在类路径中存在SqlSessionFactorySqlSessionFactoryBean类时才会进行自动配置。@ConditionalOnBean(DataSource.class): 
这个注解表示只有当容器中已经存在DataSource类型的Bean时才会进行自动配置。@EnableConfigurationProperties(MybatisProperties.class): 
这个注解启用了MybatisProperties类的配置。它会将该类的属性值绑定到MybatisAutoConfiguration类中。@AutoConfigureAfter(DataSourceAutoConfiguration.class): 
这个注解表示该自动配置类需要在DataSourceAutoConfiguration类之后进行自动配置。

从上面代码可以知道,SqlSessionFactory Bean在这里被创建和配置,交给spring容器管理,SqlSessionFactory又是怎么创建的呢?我们来看SqlSessionFactoryBean

3.SqlSessionFactoryBean

我们可以看到SqlSessionFactoryBean实现了InitializingBean接口,覆写了afterPropertiesSet方法

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {......@Overridepublic void afterPropertiesSet() throws Exception {notNull(dataSource, "Property 'dataSource' is required");notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),"Property 'configuration' and 'configLocation' can not specified with together");// 这里使用了建造者模式进行SqlSessionFactory的创建this.sqlSessionFactory = buildSqlSessionFactory();}protected SqlSessionFactory buildSqlSessionFactory() throws IOException {Configuration configuration;XMLConfigBuilder xmlConfigBuilder = null;if (this.configuration != null) {configuration = this.configuration;if (configuration.getVariables() == null) {configuration.setVariables(this.configurationProperties);} else if (this.configurationProperties != null) {configuration.getVariables().putAll(this.configurationProperties);}} else if (this.configLocation != null) {// 构建XmlConfigBuilder,准备解析XML文件的基础环境xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);configuration = xmlConfigBuilder.getConfiguration();} else {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");}configuration = new Configuration();if (this.configurationProperties != null) {configuration.setVariables(this.configurationProperties);}}if (this.objectFactory != null) {configuration.setObjectFactory(this.objectFactory);}if (this.objectWrapperFactory != null) {configuration.setObjectWrapperFactory(this.objectWrapperFactory);}if (this.vfs != null) {configuration.setVfsImpl(this.vfs);}if (hasLength(this.typeAliasesPackage)) {String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);for (String packageToScan : typeAliasPackageArray) {configuration.getTypeAliasRegistry().registerAliases(packageToScan,typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);if (LOGGER.isDebugEnabled()) {LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");}}}if (!isEmpty(this.typeAliases)) {for (Class<?> typeAlias : this.typeAliases) {configuration.getTypeAliasRegistry().registerAlias(typeAlias);if (LOGGER.isDebugEnabled()) {LOGGER.debug("Registered type alias: '" + typeAlias + "'");}}}if (!isEmpty(this.plugins)) {for (Interceptor plugin : this.plugins) {configuration.addInterceptor(plugin);if (LOGGER.isDebugEnabled()) {LOGGER.debug("Registered plugin: '" + plugin + "'");}}}if (hasLength(this.typeHandlersPackage)) {String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);for (String packageToScan : typeHandlersPackageArray) {configuration.getTypeHandlerRegistry().register(packageToScan);if (LOGGER.isDebugEnabled()) {LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");}}}if (!isEmpty(this.typeHandlers)) {for (TypeHandler<?> typeHandler : this.typeHandlers) {configuration.getTypeHandlerRegistry().register(typeHandler);if (LOGGER.isDebugEnabled()) {LOGGER.debug("Registered type handler: '" + typeHandler + "'");}}}if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmlstry {configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));} catch (SQLException e) {throw new NestedIOException("Failed getting a databaseId", e);}}if (this.cache != null) {configuration.addCache(this.cache);}if (xmlConfigBuilder != null) {try {// xml文件解析xmlConfigBuilder.parse();if (LOGGER.isDebugEnabled()) {LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");}} catch (Exception ex) {throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);} finally {ErrorContext.instance().reset();}}if (this.transactionFactory == null) {this.transactionFactory = new SpringManagedTransactionFactory();}configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));if (!isEmpty(this.mapperLocations)) {for (Resource mapperLocation : this.mapperLocations) {if (mapperLocation == null) {continue;}try {XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),configuration, mapperLocation.toString(), configuration.getSqlFragments());xmlMapperBuilder.parse();} catch (Exception e) {throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);} finally {ErrorContext.instance().reset();}if (LOGGER.isDebugEnabled()) {LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");}}} else {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");}}// 构建SqlSessionFactory,返回的是DefaultSqlSessionFactoryreturn this.sqlSessionFactoryBuilder.build(configuration);}......
}

从上面代码可以知道经过一系列操作最后通过sqlSessionFactoryBuilder创建了SqlSessionFactory。下面来看一下XML文件的解析

3.1 XMLConfigBuilder.parse()

public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;// xml配置文件转化成ConfigurationparseConfiguration(parser.evalNode("/configuration"));return configuration;}
  private void parseConfiguration(XNode root) {try {// 该方法中,将<configuration>下的所有节点分开处理propertiesElement(root.evalNode("properties"));Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);loadCustomLogImpl(settings);typeAliasesElement(root.evalNode("typeAliases"));pluginElement(root.evalNode("plugins"));objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));reflectorFactoryElement(root.evalNode("reflectorFactory"));settingsElement(settings);environmentsElement(root.evalNode("environments"));databaseIdProviderElement(root.evalNode("databaseIdProvider"));typeHandlerElement(root.evalNode("typeHandlers"));// 解析映射文件,并将相关信息保存到Configuration对象中mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);}}
private void mapperElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {String mapperPackage = child.getStringAttribute("name");configuration.addMappers(mapperPackage);} else {String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource);InputStream inputStream = Resources.getResourceAsStream(resource);// 创建XMLMapperBuilderXMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());// mapper文件解析mapperParser.parse();} else if (resource == null && url != null && mapperClass == null) {ErrorContext.instance().resource(url);InputStream inputStream = Resources.getUrlAsStream(url);XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());mapperParser.parse();} else if (resource == null && url == null && mapperClass != null) {Class<?> mapperInterface = Resources.classForName(mapperClass);configuration.addMapper(mapperInterface);} else {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");}}}}}

3.1.1 XMLMapperBuilder.parse()

public void parse() {if (!configuration.isResourceLoaded(resource)) {configurationElement(parser.evalNode("/mapper"));configuration.addLoadedResource(resource);bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();}private void configurationElement(XNode context) {try {String namespace = context.getStringAttribute("namespace");if (namespace == null || namespace.equals("")) {throw new BuilderException("Mapper's namespace cannot be empty");}builderAssistant.setCurrentNamespace(namespace);cacheRefElement(context.evalNode("cache-ref"));cacheElement(context.evalNode("cache"));parameterMapElement(context.evalNodes("/mapper/parameterMap"));resultMapElements(context.evalNodes("/mapper/resultMap"));sqlElement(context.evalNodes("/mapper/sql"));buildStatementFromContext(context.evalNodes("select|insert|update|delete"));} catch (Exception e) {throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);}}
private void buildStatementFromContext(List<XNode> list) {if (configuration.getDatabaseId() != null) {buildStatementFromContext(list, configuration.getDatabaseId());}buildStatementFromContext(list, null);}private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {for (XNode context : list) {final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);try {statementParser.parseStatementNode();} catch (IncompleteElementException e) {configuration.addIncompleteStatement(statementParser);}}}
3.1.1.1 XMLStatementBuilder.parse()
public void parseStatementNode() {String id = context.getStringAttribute("id");String databaseId = context.getStringAttribute("databaseId");if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {return;}String nodeName = context.getNode().getNodeName();SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);boolean useCache = context.getBooleanAttribute("useCache", isSelect);boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);includeParser.applyIncludes(context.getNode());String parameterType = context.getStringAttribute("parameterType");Class<?> parameterTypeClass = resolveClass(parameterType);String lang = context.getStringAttribute("lang");LanguageDriver langDriver = getLanguageDriver(lang);// Parse selectKey after includes and remove them.processSelectKeyNodes(id, parameterTypeClass, langDriver);// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)KeyGenerator keyGenerator;String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);if (configuration.hasKeyGenerator(keyStatementId)) {keyGenerator = configuration.getKeyGenerator(keyStatementId);} else {keyGenerator = context.getBooleanAttribute("useGeneratedKeys",configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;}SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));Integer fetchSize = context.getIntAttribute("fetchSize");Integer timeout = context.getIntAttribute("timeout");String parameterMap = context.getStringAttribute("parameterMap");String resultType = context.getStringAttribute("resultType");Class<?> resultTypeClass = resolveClass(resultType);String resultMap = context.getStringAttribute("resultMap");String resultSetType = context.getStringAttribute("resultSetType");ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);String keyProperty = context.getStringAttribute("keyProperty");String keyColumn = context.getStringAttribute("keyColumn");String resultSets = context.getStringAttribute("resultSets");builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache, useCache, resultOrdered,keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);}

到这里我们可以看到整个解析过程是对xml文件使用相应的xmlBuilder的parse()方法层层解析

4.SqlSession

在这里插入图片描述
构建好SqlSessionFactory后,就能获取到SqlSession。我们知道通过sqlSessionFactoryBuilder创建了SqlSessionFactory,并且返回的是实现了SqlSessionFactory的DefaultSqlSessionFactory,通过openSession()方法则可以获取SqlSession

public interface SqlSessionFactory {SqlSession openSession();SqlSession openSession(boolean autoCommit);SqlSession openSession(Connection connection);SqlSession openSession(TransactionIsolationLevel level);SqlSession openSession(ExecutorType execType);SqlSession openSession(ExecutorType execType, boolean autoCommit);SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);SqlSession openSession(ExecutorType execType, Connection connection);Configuration getConfiguration();}
@Overridepublic SqlSession openSession() {return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);// 创建执行器final Executor executor = configuration.newExecutor(tx, execType);// 这里返回的是实现了SqlSeesion的DefaultSqlSessionreturn new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}

在这里插入图片描述

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {this.configuration = configuration;this.executor = executor;this.dirty = false;this.autoCommit = autoCommit;}

在DefaultSqlSession我们可以看到很多我们熟悉的对数据库操作的方法
在这里插入图片描述

4.1 Executor

默认执行器:SimpleExecutor

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor executor;if (ExecutorType.BATCH == executorType) {executor = new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);} else {executor = new SimpleExecutor(this, transaction);}if (cacheEnabled) {executor = new CachingExecutor(executor);}executor = (Executor) interceptorChain.pluginAll(executor);return executor;}

从代码中可以看到根据不同的执行器类型创建相应的执行器。
(1)SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
(2)ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
(3)BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。
(4)CachingExecutor:CachingExecutor是一个Executor接口的装饰器,它为Executor对象增加了二级缓存的相关功能,委托的执行器对象可以是SimpleExecutor、ReuseExecutor、BatchExecutor中任一一个。执行 update 方法前判断是否清空二级缓存;执行 query 方法前先在二级缓存中查询,命中失败再通过被代理类查询。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/273142.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构二叉树续

在前边我们讲完了二叉树的一些代码结构 现在呢我们需要进一步去细化 我们传参数组后&#xff0c;让数组里面的数据进行调整 如何调整成堆呢&#xff1f; 建堆 所以我们分装一个成堆的函数 还是先去断言 然后创建空间 这里我们需要用到一个memcpy函数 memcpy函数是用来…

css-vxe-form-item中输入框加自定义按钮(校验位置错误)

1.浮动错误效果 提示内容不对 2.不使用浮动&#xff0c;使用行内块元素 代码如下 <vxe-form-item title"yoyo:" field"assembleWorkNo" span"8"><template #default><vxe-input style"width:70%;display:inline-block;&quo…

SQLite3中的callback回调函数注意的细节

调用 sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)该例程提供了一个执行 SQL 命令的快捷方式&#xff0c; SQL 命令由 sql 参数提供&#xff0c;可以由多个 SQL 命令组成。 在这里&#xff0c; 第一个参数 sqlite3 是打开的数据库对…

Grafana dashboards as ConfigMaps

文章目录 1. 简介2. 创建 configmaps3. grafana 界面查看 1. 简介 将 Grafana 仪表板存储为 Kubernetes ConfigMap 相比传统的通过 Grafana 界面导入仪表板有以下一些主要优点: 版本控制&#xff1a; ConfigMap 可以存储在版本控制系统(如Git)中,便于跟踪和管理仪表板的变更历…

牛客周赛 Round 36

赛况 C题可惜&#xff0c;比赛时模拟没有想明白&#xff0c;只对了一半&#xff0c;赛后看了大佬们的题解后恍然大悟&#xff0c;而F题是压根没思路&#xff0c;况且F题部分分也比较难拿。 题目列表 A-小红的数位删除 思路 将读入的数字整除10做三次后输出即可 参考代码 #inc…

Golang搭建grpc环境

简介 OS : Windows 11 Golang 版本: go1.22.0 grpc : 1.2 protobuffer: 1.28代理 没有代理国内环境下载不了库七牛CDN &#xff08;试过可用&#xff09; go env -w GOPROXYhttps://goproxy.cn,direct阿里云代理(运行grpc时下载包出现报错 ): go env -w GOPROXYhttps://mirr…

Qt ini配置文件

ini文件用于保存用户的设置操作&#xff0c;下列以背景颜色设置为例子 暂时默认设置为白色背景 这段代码放置在主窗口的构造函数中&#xff0c;用于初始化读取ini文件 QString color;//第一个参数在我这里ini文件时相对路径需要放在工程中&#xff0c;也可以写绝对路径QSettin…

运维:记一次寻找定时任务并删除的经历

前言 我相信接手别人的服务器、或者在没有任何文档的情况去看自己原先的服务器,都或多或少会遇到莫名其妙的服务器独有规则。 比如你服务本身跑的好好的,突然啪的一下,没了! 什么原因导致的呢?其中,很大可能是定时任务在作祟。 原因分析 本次,我遇到的问题是:在Ubuntu系…

017集——圆弧(ARC)转多段线(lwpolyline)——cad vba 中按一定精度拟合加点实现

在国土资源管理项目中&#xff0c;我们经常会遇到CAD转gis数据实现入库&#xff0c;而cad中的arc圆弧转为gis数据只能转出弧的顶点坐标&#xff0c;导致图形变形失真。若一个一个对弧进行手工增加点转为多段线&#xff0c;耗时耗力&#xff0c;效率极其低下。这里给出解决方案&…

微软亚太区AI智能应用创新业务负责人许豪,将出席“ISIG-AIGC技术与应用发展峰会”

3月16日&#xff0c;第四届「ISIG中国产业智能大会」将在上海中庚聚龙酒店拉开序幕。本届大会由苏州市金融科技协会指导&#xff0c;企智未来科技&#xff08;AIGC开放社区、RPA中国、LowCode低码时代&#xff09;主办。大会旨在聚合每一位产业成员的力量&#xff0c;深入探索A…

[QT]自定义的QtabWidget

需求 最近有一个需求就是一个QTabWidget要求有四个tab页在左侧用于显示主页面&#xff0c;在右侧有一个关于按钮&#xff0c;点击后用于弹出窗口显示一些程序相关信息。主要是怎么实现右侧按钮 相关代码 #ifndef MYTABWIDGET_H #define MYTABWIDGET_H#include <QWidget&g…

如何保证消息的可靠传输

数据的丢失问题&#xff0c;可能出现在生产者、MQ、消费者中 生产者丢失&#xff1a; 生产者将数据发送到 RabbitMQ 的时候&#xff0c;可能数据就在半路给搞丢了&#xff0c;因为网络问题啥的&#xff0c;都有可能。此时可以选择用 RabbitMQ 提供的事务功能&#xff0c;就是生…

std::function模板类性能问题

背景 题目&#xff1a;最近发现记忆化搜索真的很好用&#xff0c;于是在做力扣上记忆化搜索相关的题目时&#xff0c;用这种方法重做了一下买卖股票问题。 问题来源 在写递归代码的时候&#xff0c;我学习了一种匿名函数的写法&#xff0c;直接在函数体内写function<int(…

SpringBoot 多环境的配置(附带有截图)

文章目录 概要整体配置流程配置详细说明技术细节小结 概要 多环境开发 在实际项目开发中&#xff0c;一般需要针对不同的运行环境&#xff0c;如开发环境、测试环境、生产环境等&#xff0c;每个运行环境的数据库等配置都不相同&#xff0c;每次发布测试、更新生产都需要手动…

【MATLAB第99期】#源码分享 | 基于MATLAB的SHEPard模型多输入单输出回归预测模型

【MATLAB第99期】#源码分享 | 基于MATLAB的SHEPard模型多输入单输出回归预测模型 Shepard模型(简称SP模型)就是一种直观的、可操作的相似预测法&#xff0c;常用于插值。相似预测法基本原理按照相似原因产生相似结果的原则&#xff0c;从历史样本中集中找出与现在的最相似的一…

基于java ssm springboot女士电商平台系统

基于java ssm springboot女士电商平台系统源码文档设计 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末…

Vue3学习记录(六)--- 组合式API之依赖注入和异步组件

一、依赖注入 1、简介 ​ 在前面的笔记中&#xff0c;我们学习过父组件向子组件传递数据时&#xff0c;需要借助props来实现。但如果父组件想要向孙子组件传递数据&#xff0c;那就要连续使用两层props逐级向下传递&#xff0c;如果要接收数据的是更深层的后代组件&#xff0…

数据库(mysql)-新手笔记-基本知识点(1)

基本概念 数据库 Database :存储数据的容器 表 Table : 在数据库中存储的基本结构,它由行和列组成 行 Row : 表中的一条记录 列 Column : 表中的字段,定义了数据的类型和约束 数据类型 数据值 如 INT(整型),FLAOT(浮点型) ,DECIMAL (精确小数点) 字符串 如 VARCHAR(可变长度字…

基于OpenCV的图形分析辨认03

目录 一、前言 二、实验目的 三、实验内容 四、实验过程 一、前言 编程语言&#xff1a;Python&#xff0c;编程软件&#xff1a;vscode或pycharm&#xff0c;必备的第三方库&#xff1a;OpenCV&#xff0c;numpy&#xff0c;matplotlib&#xff0c;os等等。 关于OpenCV&…

微信小程序云开发教程——墨刀原型工具入门(编辑页面)

引言 作为一个小白&#xff0c;小北要怎么在短时间内快速学会微信小程序原型设计&#xff1f; “时间紧&#xff0c;任务重”&#xff0c;这意味着学习时必须把握微信小程序原型设计中的重点、难点&#xff0c;而非面面俱到。 要在短时间内理解、掌握一个工具的使用&#xf…