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);
            });
            // entity
            outputEntity(tableInfo, objectMap);
            // mapper and xml
            outputMapper(tableInfo, objectMap);
            // service
            outputService(tableInfo, objectMap);
            // controller
            outputController(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);
}

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

    @Override
    public 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
    #else
    var ${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}
#else
        return null
#end
    }

#end
    override 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>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32(HAL)串口中断接收

目录 1、简介 2 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 串口外设配置 2.3 项目生成 3、KEIL端程序整合 1、简介 本文对HAL串口中断函数进行介绍。 2 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 串口外设配置 2.3 项目生成 3、KEIL端程序整合 首先在main.c文件中进行…

一套AI+医疗模式的医院智慧导诊系统源码:springboot+redis+mybatis plus+mysql

一套AI医疗模式的医院智慧导诊系统源码 相关技术&#xff1a; 技术架构&#xff1a;springbootredismybatis plusmysqlRocketMQ 开发语言&#xff1a;java 开发工具&#xff1a;IDEA 前端框架&#xff1a;Uniapp 后端框架&#xff1a;springboot 数 据 库&#xff1a;mys…

华为OD机试真题 JavaScript 实现【名字的漂亮度】【牛客练习题】

目录 一、题目描述二、输入描述三、输出描述四、解题思路五、JavaScript算法源码 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试&#xff0c;发现新题目&#x…

React的UmiJS搭建的项目集成海康威视h5player播放插件H5视频播放器开发包 V2.1.2

最近前端的一个项目&#xff0c;大屏需要摄像头播放&#xff0c;摄像头厂家是海康威视的&#xff0c;网上找了一圈都没有React集成的&#xff0c;特别是没有使用UmiJS搭脚手架搭建的&#xff0c;所以记录一下。 海康威视的开放平台的API地址&#xff0c;相关插件和文档都可以下…

CUDA_CHECK(cudaFree(...))报错CUDA error 1

GPT-3.5太好用了&#xff0c;报错情况如下&#xff1a; 总结一下, 在使用cudaFree释放之前cudaMalloc()分配的GPU内存时&#xff0c;报错cuda error,最有可能的几个原因就是&#xff1a; 试图释放已经释放的gpu内存&#xff0c;在调用cudafree&#xff08;&#xff09;时确保没…

C# Solidworks二次开发:自动创建虚拟零件及使用注意事项

今天要讲的是关于在solidworks中如何自动创建虚拟零件的功能&#xff0c;也就是solidworks中插入新零件这个功能。 实现这个功能需要使用的API如下所示&#xff1a; InsertNewVirtualPart&#xff08;swFaceOrPlane1, out swcomp2&#xff09;&#xff1b; 其中这个方法中使…

【深度学习】InST,Inversion-Based Style Transfer with Diffusion Models,论文,风格迁移,实战

代码&#xff1a;https://github.com/zyxElsa/InST 论文&#xff1a;https://arxiv.org/abs/2211.13203 文章目录 AbstractIntroductionRelated WorkImage style transferText-to-image synthesisInversion of diffusion models MethodOverview ExperimentsComparison with Sty…

登月再进一步:Apollo自动驾驶的里程碑

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★前端炫酷代码分享 ★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ 解决算法&#xff0c;一个专栏就够了★ ★ 架…

spark-sql数据重复之File Output Committer问题

前言 我们先来回顾下之前介绍过的三种Committer&#xff1a;FileOutputCommitter V1、FileOutputCommitter V2、S3A Committer&#xff0c;其基本代表了整体的演进趋势。 核心代码讲解详细参照&#xff1a;Spark CommitCoordinator 保证数据一致性 OutputCommitter commitTask…

Android如何用系统签名打包应用

前言 应用使用系统签名可以在用户不需要手动授权的情况下自动获取权限。适合一些定制系统中集成apk的方案商。 步骤 需要在AndroidManifest.xml中添加共享系统进程属性&#xff1a; android:sharedUserId"android.uid.system"如下图所示&#xff1a; 找到系统定制…

C++函数模板

2023年8月2日&#xff0c;周三上午 目录 模板的分类语法模板实例化示例程序深入理解模板参数 模板的分类 模板分为函数模板和类模板。 语法 template<typename 模板参数1,...> 返回值类型 函数名(参数列表){} template是一个关键字&#xff0c;用于声明模板 <>…

MPLS虚拟专用网跨域--OptionB方案

OptionB方案 跨域VPN-OptionB中,两个ASBR通过MP-EBGP交换它们从各自AS的PE设备接收的标签VPN-IPv4路由。图中,VPN LSP表示私网隧道,LSP表示公网隧道。 跨域VPN-OptionB方案中,ASBR接收本域内和域外传过来的所有跨域VPN-IPv4路由,再把VPN-IPv4路由发布出去。但MPLS VPN的…

C++多线程环境下的单例类对象创建

使用C无锁编程实现多线程下的单例模式 贺志国 2023.8.1 一、尺寸较小的类单例对象创建 如果待创建的单例类SingletonForMultithread内包含的成员变量较少&#xff0c;整个类占用的内存空间较小&#xff0c;则可以使用如下方法来创建单例对象&#xff08;如果类的尺寸较大则不…

穷举深搜暴搜回溯剪枝(1)

一)全排列: 46. 全排列 - 力扣&#xff08;LeetCode&#xff09; 1)先画出决策树: 越详细越好&#xff0c;就是我们在进行暴力枚举这道题的过程中&#xff0c;如何不重不漏地将所有的情况全部枚举到&#xff0c;把这个思想历程给画下来&#xff0c;就可以了&#xff0c;把每一步…

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

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

k8s部署xxl-job分布式任务调度服务

一、背景 什么时候需要把xxl-job部署到k8s里 当你的java服务部署到K8S后&#xff0c;因为xxl-job的任务调度器需要对注册上来的执行器进行健康检测&#xff0c;而java服务作为执行器&#xff0c;注册地址是pod的Ip地址&#xff1b;所以&#xff0c;调度器想要访问执行器的网路…

DevExpress WPF Tree List组件,让数据可视化程度更高!(二)

DevExpress WPF Tree List组件是一个功能齐全、数据感知的TreeView-ListView混合体&#xff0c;可以把数据信息显示为REE、GRID或两者的组合&#xff0c;在数据绑定或非绑定模式下&#xff0c;具有完整的数据编辑支持。 在上文中&#xff08;点击这里回顾DevExpress WPF Tree …

JavaScript 简单实现观察者模式和发布-订阅模式

JavaScript 简单实现观察者模式和发布-订阅模式 1. 观察者模式1.1 什么是观察者模式1.2 代码实现 2. 发布-订阅模式2.1 什么是发布-订阅模式2.2 代码实现2.2.1 基础版2.2.2 取消订阅2.2.3 订阅一次 1. 观察者模式 1.1 什么是观察者模式 概念&#xff1a;观察者模式定义对象间…

IBM Spectrum LSF (“LSF“ ,简称为负载共享设施) 用户案例

IBM Spectrum LSF (“LSF” &#xff0c;简称为负载共享设施) 用户案例 IBM Spectrum LSF (“LSF” &#xff0c;简称为负载共享设施) 软件是业界领先的企业级软件。 LSF 在现有异构 IT 资源之间分配工作&#xff0c;以创建共享&#xff0c;可扩展且容错的基础架构&#xff0c…

纯css实现九宫格图片

本篇文章所分享的内容主要涉及到结构伪类选择器&#xff0c;不熟悉的小伙伴可以了解一下&#xff0c;在常用的css选择器中我也有分享相关内容。 话不多说&#xff0c;接下来我们直接上代码&#xff1a; <!DOCTYPE html> <html lang"en"><head>&l…