SpringBoot第29讲:SpringBoot集成MySQL - MyBatis-Plus代码自动生成

SpringBoot第29讲:SpringBoot集成MySQL - MyBatis-Plus代码自动生成

本文是SpringBoot第29讲,主要介绍 MyBatis-Plus代码自动生成,以及产生此类代码生成工具的背景和此类工具的基本实现原理。

文章目录

  • SpringBoot第29讲:SpringBoot集成MySQL - MyBatis-Plus代码自动生成
    • 1、知识准备
      • 1.1、MyBatis-Plus相关
      • 1.2、为什么会产生此类代码生成工具?
      • 1.3、什么是模板引擎?
    • 2、简单示例
      • 2.1、准备DB
      • 2.2、添加POM依赖
      • 2.3、代码生成配置
      • 2.4、生成代码
    • 3、进一步理解
      • 3.1、代码生成的基本原理
      • 3.2、如何看MyBatis-Plus生成代码的功能?
    • 4、示例源码
    • Action1:使用mybatis-plus代码生成器报错:Exception in thread “main” java.lang.NoClassDefFoundError: org/apache/velocity/context/Contex

1、知识准备

需要了解MyBatis和MyBatis-Plus基础,并了解产生此类代码生成工具的背景和基本原理。

1.1、MyBatis-Plus相关

SpringBoot集成MySQL - MyBatis-Plus方式

1.2、为什么会产生此类代码生成工具?

由于CRUD的工作占了普通开发很多工作,而这些工作是重复的,所以出现了此类的代码生成工具。这些工具通过模板引擎来生成代码,常见于三方集成工具,IDE插件等等。

1.3、什么是模板引擎?

模板引擎可以在代码生成过程中减少大量机械重复工作,大大提高开发效率,良好的设计使得代码重用,后期维护都降低成本。一个好的模板引擎的使用要考虑的方面无外乎:功能是否强大,使用是否简单,整合性、扩展性与灵活性,性能

比如:

  • Velocity
  • FreeMarker
  • Thymeleaf

img

2、简单示例

这里展示通过MyBatis-Plus生成代码实现的

2.1、准备DB

创建MySQL的schema test_db, 导入SQL 文件如下

DROP TABLE IF EXISTS `tb_role`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tb_role` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) NOT NULL,`role_key` varchar(255) NOT NULL,`description` varchar(255) DEFAULT NULL,`create_time` datetime DEFAULT NULL,`update_time` datetime DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;--
-- Dumping data for table `tb_role`
--LOCK TABLES `tb_role` WRITE;
/*!40000 ALTER TABLE `tb_role` DISABLE KEYS */;
INSERT INTO `tb_role` VALUES (1,'admin','admin','admin','2021-09-08 17:09:15','2021-09-08 17:09:15');
/*!40000 ALTER TABLE `tb_role` ENABLE KEYS */;
UNLOCK TABLES;--
-- Table structure for table `tb_user`
--DROP TABLE IF EXISTS `tb_user`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tb_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`user_name` varchar(45) NOT NULL,`password` varchar(45) NOT NULL,`email` varchar(45) DEFAULT NULL,`phone_number` int(11) DEFAULT NULL,`description` varchar(255) DEFAULT NULL,`create_time` datetime DEFAULT NULL,`update_time` datetime DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;--
-- Dumping data for table `tb_user`
--LOCK TABLES `tb_user` WRITE;
/*!40000 ALTER TABLE `tb_user` DISABLE KEYS */;
INSERT INTO `tb_user` VALUES (1,'pdai','dfasdf','suzhou.daipeng@gmail.com',1212121213,'afsdfsaf','2021-09-08 17:09:15','2021-09-08 17:09:15');
/*!40000 ALTER TABLE `tb_user` ENABLE KEYS */;
UNLOCK TABLES;--
-- Table structure for table `tb_user_role`
--DROP TABLE IF EXISTS `tb_user_role`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tb_user_role` (`user_id` int(11) NOT NULL,`role_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;--
-- Dumping data for table `tb_user_role`
--LOCK TABLES `tb_user_role` WRITE;
/*!40000 ALTER TABLE `tb_user_role` DISABLE KEYS */;
INSERT INTO `tb_user_role` VALUES (1,1);
/*!40000 ALTER TABLE `tb_user_role` ENABLE KEYS */;
UNLOCK TABLES;

2.2、添加POM依赖

包括mybatis-plus-generator 和默认的模板引擎velocity依赖的 velocity-engine-core。

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.2</version>
</dependency>
<dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.2</version>
</dependency>

2.3、代码生成配置

mybatis-plus-generator 3.5.1 及其以上版本,对历史版本不兼容!3.5.1 以下的请参考这里

import com.baomidou.mybatisplus.generator.FastAutoGenerator;/*** This class is for xxxx.* @author qiwenjie*/
public class TestGenCode {public static void main(String[] args) {FastAutoGenerator.create("jdbc:mysql://localhost:3306/db_user?useSSL=false&autoReconnect=true&characterEncoding=utf8", "root", "qwj930828").globalConfig(builder ->builder.author("qiwenjie") // 设置作者.enableSwagger() // 开启 swagger 模式).packageConfig(builder ->builder.parent("springboot.mysql.mybatisplus.anno") // 设置父包名.moduleName("gencode") // 设置父包模块名).strategyConfig(builder ->builder.addInclude("tb_user", "tb_role", "tb_user_role")).injectionConfig(InjectionConfig.Builder::fileOverride).execute();}
}

2.4、生成代码

在这里插入图片描述

3、进一步理解

主要了解MyBatis-Plus生成代码的原理。

3.1、代码生成的基本原理

其实代码生成是非常简单的,有了模板引擎的介绍,我们再看下MyBatis-Plus的代码生成工具是如何生成代码的。

配置的装载, FastAutoGenerator 本质上就是通过builder注入各种配置,并将它交给代码生成主类:AutoGenerator

public void execute() {new AutoGenerator(this.dataSourceConfigBuilder.build())// 全局配置.global(this.globalConfigBuilder.build())// 包配置.packageInfo(this.packageConfigBuilder.build())// 策略配置.strategy(this.strategyConfigBuilder.build())// 注入配置.injection(this.injectionConfigBuilder.build())// 模板配置.template(this.templateConfigBuilder.build())// 执行.execute(this.templateEngine);
}

AutoGenerator中execute方法,包括初始化配置和模板引擎(默认是Velocity),然后将配置交给模板引擎初始化执行文件输出

/*** 生成代码** @param templateEngine 模板引擎*/
public void execute(AbstractTemplateEngine templateEngine) {logger.debug("==========================准备生成文件...==========================");// 初始化配置if (null == config) {config = new ConfigBuilder(packageInfo, dataSource, strategy, template, globalConfig, injection);}if (null == templateEngine) {// 为了兼容之前逻辑,采用 Velocity 引擎 【 默认 】templateEngine = new VelocityTemplateEngine();}templateEngine.setConfigBuilder(config);// 模板引擎初始化执行文件输出templateEngine.init(config).batchOutput().open();logger.debug("==========================文件生成完成!!!==========================");
}

模板引擎中batchOuput方法中,包含获取表的信息并根据模板来生成类文件。

/*** 批量输出 java xml 文件*/
@NotNull
public AbstractTemplateEngine batchOutput() {try {ConfigBuilder config = this.getConfigBuilder();List<TableInfo> tableInfoList = config.getTableInfoList();tableInfoList.forEach(tableInfo -> {Map<String, Object> objectMap = this.getObjectMap(config, tableInfo);Optional.ofNullable(config.getInjectionConfig()).ifPresent(t -> {t.beforeOutputFile(tableInfo, objectMap);// 输出自定义文件outputCustomFile(t.getCustomFile(), tableInfo, objectMap);});// entityoutputEntity(tableInfo, objectMap);// mapper and xmloutputMapper(tableInfo, objectMap);// serviceoutputService(tableInfo, objectMap);// controlleroutputController(tableInfo, objectMap);});} catch (Exception e) {throw new RuntimeException("无法创建文件,请检查配置信息!", e);}return this;
}

获取表的列表,由ConfigBuilder完成

public List<TableInfo> getTableInfoList() {if (tableInfoList.isEmpty()) {// TODO 暂时不开放自定义List<TableInfo> tableInfos = new IDatabaseQuery.DefaultDatabaseQuery(this).queryTables();if (!tableInfos.isEmpty()) {this.tableInfoList.addAll(tableInfos);}}return tableInfoList;
}

然后获取上述单个表(tableInfo)的具体信息(objectMap)

/*** 渲染对象 MAP 信息** @param config    配置信息* @param tableInfo 表信息对象* @return ignore*/
@NotNull
public Map<String, Object> getObjectMap(@NotNull ConfigBuilder config, @NotNull TableInfo tableInfo) {StrategyConfig strategyConfig = config.getStrategyConfig();Map<String, Object> controllerData = strategyConfig.controller().renderData(tableInfo);Map<String, Object> objectMap = new HashMap<>(controllerData);Map<String, Object> mapperData = strategyConfig.mapper().renderData(tableInfo);objectMap.putAll(mapperData);Map<String, Object> serviceData = strategyConfig.service().renderData(tableInfo);objectMap.putAll(serviceData);Map<String, Object> entityData = strategyConfig.entity().renderData(tableInfo);objectMap.putAll(entityData);objectMap.put("config", config);objectMap.put("package", config.getPackageConfig().getPackageInfo());GlobalConfig globalConfig = config.getGlobalConfig();objectMap.put("author", globalConfig.getAuthor());objectMap.put("kotlin", globalConfig.isKotlin());objectMap.put("swagger", globalConfig.isSwagger());objectMap.put("date", globalConfig.getCommentDate());// 启用 schema 处理逻辑String schemaName = "";if (strategyConfig.isEnableSchema()) {// 存在 schemaName 设置拼接 . 组合表名schemaName = config.getDataSourceConfig().getSchemaName();if (StringUtils.isNotBlank(schemaName)) {schemaName += ".";tableInfo.setConvert(true);}}objectMap.put("schemaName", schemaName);objectMap.put("table", tableInfo);objectMap.put("entity", tableInfo.getEntityName());return objectMap;
}

根据TableInfo和objectMap输出类文件,以输出Entity实体类为例

/*** 输出实体文件** @param tableInfo 表信息* @param objectMap 渲染数据* @since 3.5.0*/
protected void outputEntity(@NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {String entityName = tableInfo.getEntityName();String entityPath = getPathInfo(OutputFile.entity);if (StringUtils.isNotBlank(entityName) && StringUtils.isNotBlank(entityPath)) {getTemplateFilePath(template -> template.getEntity(getConfigBuilder().getGlobalConfig().isKotlin())).ifPresent((entity) -> {String entityFile = String.format((entityPath + File.separator + "%s" + suffixJavaOrKt()), entityName);outputFile(new File(entityFile), objectMap, entity, getConfigBuilder().getStrategyConfig().entity().isFileOverride());});}
}

在outputFile中来确定生成文件的名字和路径

/*** 输出文件** @param file         文件* @param objectMap    渲染信息* @param templatePath 模板路径* @param fileOverride 是否覆盖已有文件* @since 3.5.2*/
protected void outputFile(@NotNull File file, @NotNull Map<String, Object> objectMap, @NotNull String templatePath, boolean fileOverride) {if (isCreate(file, fileOverride)) {try {// 全局判断【默认】boolean exist = file.exists();if (!exist) {File parentFile = file.getParentFile();FileUtils.forceMkdir(parentFile);}writer(objectMap, templatePath, file);} catch (Exception exception) {throw new RuntimeException(exception);}}
}

最后通过writer方法生成文件

/*** 将模板转化成为文件** @param objectMap    渲染对象 MAP 信息* @param templatePath 模板文件* @param outputFile   文件生成的目录* @throws Exception 异常* @since 3.5.0*/
public void writer(@NotNull Map<String, Object> objectMap, @NotNull String templatePath, @NotNull File outputFile) throws Exception {this.writer(objectMap, templatePath, outputFile.getPath());logger.debug("模板:" + templatePath + ";  文件:" + outputFile);
}

本质上就是调用模板引擎来生成

    @Overridepublic void writer(@NotNull Map<String, Object> objectMap, @NotNull String templatePath, @NotNull File outputFile) throws Exception {Template template = velocityEngine.getTemplate(templatePath, ConstVal.UTF8);try (FileOutputStream fos = new FileOutputStream(outputFile);OutputStreamWriter ow = new OutputStreamWriter(fos, ConstVal.UTF8);BufferedWriter writer = new BufferedWriter(ow)) {template.merge(new VelocityContext(objectMap), writer);}}

比如Entity,velocityEngine.getTemplate会获取如下entity.vm模板生成Entity的类文件。

package ${package.Entity};#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${swagger})
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
#end/*** <p>* $!{table.comment}* </p>** @author ${author}* @since ${date}*/
#if(${table.convert})
@TableName("${schemaName}${table.name}")
#end
#if(${swagger})
@ApiModel(value = "${entity}对象", description = "$!{table.comment}")
#end
#if(${superEntityClass})
class ${entity} : ${superEntityClass}#if(${activeRecord})<${entity}>#end() {
#elseif(${activeRecord})
class ${entity} : Model<${entity}>() {
#elseif(${entitySerialVersionUID})
class ${entity} : Serializable {
#else
class ${entity} {
#end## ----------  BEGIN 字段循环遍历  ----------
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if("$!field.comment" != "")#if(${swagger})@ApiModelProperty(value = "${field.comment}")#else/*** ${field.comment}*/#end
#end
#if(${field.keyFlag})
## 主键
#if(${field.keyIdentityFlag})@TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
#elseif(!$null.isNull(${idType}) && "$!idType" != "")@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
#elseif(${field.convert})@TableId("${field.annotationColumnName}")
#end
## 普通字段
#elseif(${field.fill})
## -----   存在字段填充设置   -----
#if(${field.convert})@TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
#else@TableField(fill = FieldFill.${field.fill})
#end
#elseif(${field.convert})@TableField("${field.annotationColumnName}")
#end
## 乐观锁注解
#if(${field.versionField})@Version
#end
## 逻辑删除注解
#if(${field.logicDeleteField})@TableLogic
#end#if(${field.propertyType} == "Integer")var ${field.propertyName}: Int? = null#elsevar ${field.propertyName}: ${field.propertyType}? = null#end#end
## ----------  END 字段循环遍历  ----------
#if(${entityColumnConstant})companion object {
#foreach($field in ${table.fields})const val ${field.name.toUpperCase()} : String = "${field.name}"#end}#end
#if(${activeRecord})override fun pkVal(): Serializable? {
#if(${keyPropertyName})return ${keyPropertyName}
#elsereturn null
#end}#endoverride fun toString(): String {return "${entity}{" +
#foreach($field in ${table.fields})
#if($!{foreach.index}==0)"${field.propertyName}=" + ${field.propertyName} +
#else", ${field.propertyName}=" + ${field.propertyName} +
#end
#end"}"}
}

同理生成mapper, service, controller等文件。是不是很简单?

3.2、如何看MyBatis-Plus生成代码的功能?

简单而言,对于初学者好像能生成代码作用很大,实际情况是很鸡肋!

  • 从上面的源码我们可以看出,生成类只适合单表结构,表的关联无法处理
  • 对于单表的CRUD类,如果可以自动化生成,必然是可以很好的抽象的,而BaseMapper, BaseServiceImpl的封装已经足够了
  • 通常真正可以通过一体化集成前端代码的生成,才有一定的意义;
  • 当然少部分情况快速提供接口的可以考虑,不过其实也省不了什么时间

4、示例源码

todo

Action1:使用mybatis-plus代码生成器报错:Exception in thread “main” java.lang.NoClassDefFoundError: org/apache/velocity/context/Contex

原因:可能是使用的MyBatis-Plus版本太低了

解决方案:在pom.xml文件中添加以下依赖即可

<dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.2</version>
</dependency>

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

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

相关文章

【Linux】Centos7 的 Systemctl 与 创建系统服务 (shell脚本)

Systemctl systemctl 命令 # 启动 systemctl start NAME.service # 停止 systemctl stop NAME.service # 重启 systemctl restart NAME.service # 查看状态 systemctl status NAME.service # 查看所有激活系统服务 systemctl list-units -t service # 查看所有系统服务 syste…

PHP高级检索功能的实现以及动态拼接sql

我们学习了解了这么多关于PHP的知识&#xff0c;不知道你们对PHP高级检索功能的实现以及动态拼接sql是否已经完全掌握了呢&#xff0c;如果没有&#xff0c;那就跟随本篇文章一起继续学习吧! PHP高级检索功能的实现以及动态拼接sql。完成的功能有&#xff1a;可以单独根据一个…

华为云hcip核心知识笔记(数据库服务规划)

华为云hcip核心知识笔记&#xff08;数据库服务规划&#xff09; 1.云数据接库优势 1.1云数据库优点有&#xff1a; 易用性强&#xff1a;能欧快速部署和运行 高扩展&#xff1a;开放式架构和云计算存储分离 低成本&#xff1a;按需使用&#xff0c;成本更加低廉 2.云数据库r…

微软开测“Moment4”启动包:Win11 23H2要来了

近日&#xff0c; 有用户在Win11最新的7月累积更新中发现&#xff0c;更新文件中已经开始出现了对“Moment4”的引用。 具体来说&#xff0c;在7月累积更新中&#xff0c;微软加入了“Microsoft-Windows-UpdateTargeting-ClientOS-SV2Moment4-EKB”“Microsoft-Windows-23H2Ena…

2023年【零声教育】13代C/C++Linux服务器开发高级架构师课程体系分析

对于零声教育的C/CLinux服务器高级架构师的课程到2022目前已经迭代到13代了&#xff0c;像之前小编也总结过&#xff0c;但是课程每期都有做一定的更新&#xff0c;也是为了更好的完善课程跟上目前互联网大厂的岗位技术需求&#xff0c;之前课程里面也包含了一些小的分支&#…

音频客观感知MOS对比,对ViSQOL、PESQ、MosNet(神经网络MOS分)和polqa一致性对比和可信度雁阵

原创&#xff1a;转载需附链接&#xff1a; 音频客观感知MOS对比&#xff0c;对ViSQOL、PESQ、MosNet&#xff08;神经网络MOS分&#xff09;和polqa一致性对比和可信度雁阵_machine-lv的博客-CSDN博客谢谢&#xff01; 本文章以标准polqa的mos分为可信前提&#xff0c;验证vis…

MPAndroidChart学习及问题处理

1.添加依赖 项目目录->app->build.gradle dependencies {implementation com.github.PhilJay:MPAndroidChart:v3.0.3 }项目目录->app->setting.gradle dependencyResolutionManagement {repositories {maven { url https://jitpack.io }} }高版本的gradle添加依…

2023年第四届“华数杯”数学建模思路 - 案例:感知机原理剖析及实现

# 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 一、感知机的直观理解 感知机应该属于机器学习算法中最简单的一种算法&#xff0c;其原理可以看下图&#xff1a; 比如说我们有一个坐标轴&#xff08;图中的…

关于视频汇聚融合EasyCVR平台多视频播放协议的概述

视频监控综合管理平台EasyCVR具备视频融合能力&#xff0c;平台基于云边端一体化架构&#xff0c;具有强大的数据接入、处理及分发能力&#xff0c;平台既具备传统安防视频监控的能力与服务&#xff0c;也支持AI智能检测技术的接入&#xff0c;可应用在多行业领域的智能化监管场…

直线模组如何进行精度校准?

直线模组是一种高精度的传动元件&#xff0c;而精度是直线模组的重要指标&#xff0c;在直线模组的使用中&#xff0c;我们应该尽可能的避免直线模组的精度受损&#xff0c;这样才能够有真正的发挥出直线模组的稳定性。 直线模组的精度一般是指重复定位精度和导向精度&#xff…

PyTorch(安装及卸载)

目录 1. 安装 2. 卸载 参考文献 为什么用PyTorch&#xff1a;简单来说&#xff0c;19年之前tensorflow是大哥&#xff0c;19年tensorflow和PyTorch双龙并行&#xff0c;20年之后PyTorch一往无前。宗旨&#xff0c;哪个用的人多用哪个。 1. 安装 1. 先打开Anaconda Prompt&…

uniapp自定义消息语音

需求是后端推送的消息APP要响自定义语音&#xff0c;利用官方插件&#xff0c;总结下整体流程 uniapp后台配置 因为2.0只支持uniapp自己的后台发送消息&#xff0c;所以要自己的后台发送消息只能用1.0 插件地址和代码 插件地址: link let isIos (plus.os.name "iOS&qu…

C++内存管理

目录 一.C中内存区域划分 一.C中内存区域划分 1.栈又叫堆栈--非静态局部变量/函数参数/返回值等等&#xff0c;栈是向下增长的。 2.内存映射段是高效的I/O映射方式&#xff0c;用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存&#xff0c;做进程间通信。 …

手撕SpringBoot的自定义启动器

一. 前言 哈喽&#xff0c;大家好&#xff0c;最近金九银十&#xff0c;又有不少小伙伴私信辉哥&#xff0c;说自己在面试时被问到SpringBoot如何自定义启动器&#xff0c;结果自己不知道该怎么回答。那么今天就手把手地带着大家&#xff0c;去看看在SpringBoot中到底该怎么实…

亚马逊买家账号ip关联怎么处理

对于亚马逊买家账号&#xff0c;同样需要注意IP关联问题。在亚马逊的眼中&#xff0c;如果多个买家账号共享相同的IP地址&#xff0c;可能会被视为潜在的操纵、违规或滥用行为。这种情况可能导致账号受到限制或处罚。 处理亚马逊买家账号IP关联问题&#xff0c;建议采取以下步骤…

生化危机5找不到xlive.dll,要如何修复xlive.dll缺失

有朋友反映说他在玩生化危机5的时候&#xff0c;突然电脑就弹出一个找不到xlive.dll&#xff0c;然后游戏就打不开了&#xff0c;一直都很懵逼&#xff0c;不知道怎么处理这个问题&#xff0c;今天小编就来给大家详细的讲讲&#xff0c;找不到xlive.dll要怎么去修复&#xff01…

危化品行业防雷检测综合解决方案

危化品是指具有毒害、腐蚀、爆炸、燃烧、助燃等性质&#xff0c;能够对人体、设施或者环境造成危害的化学品。危化品的生产、储存、运输、使用等过程中&#xff0c;都存在着遭受雷击引发火灾或者爆炸事故的风险。因此&#xff0c;对危化品场所进行防雷检测&#xff0c;是保障危…

IDEA中修改类头的文档注释信息

IDEA中修改类头的文档注释信息 选择File--Settings--Editor--File and Code Templates--Includes&#xff0c;可以把文档注释写成这种的 /**author: Arbicoralcreate: ${YEAR}-${MONTH}-${DAY} ${TIME}Description: */这样回看就可以很清楚的看到自己创建脚本的时间&#xff…

什么是注意力机制?注意力机制的计算规则

我们观察事物时&#xff0c;之所以能够快速判断一种事物(当然允许判断是错误的)&#xff0c;是因为我们大脑能够很快把注意力放在事物最具有辨识度的部分从而作出判断&#xff0c;而并非是从头到尾的观察一遍事物后&#xff0c;才能有判断结果&#xff0c;正是基于这样的理论&a…

基于 FFlogs API 快速实现的 logs 颜色查询小爬虫

文章目录 找到接口解析响应需要平均颜色和过本次数&#xff1f; 找到接口 首先试了一下爬虫&#xff0c;发现和wow一样官网上有暴露的 API&#xff0c;链接在&#xff1a;FFlogs v1 API 文档链接 通过查询官方提供的 API 接口得知&#xff1a; user_name 角色名字 api_key …