SpringBoot结合Liquibase实现数据库变更管理

news2025/1/13 15:29:19

《从零打造项目》系列文章

工具

  • 比MyBatis Generator更强大的代码生成器

ORM框架选型

  • SpringBoot项目基础设施搭建

  • SpringBoot集成Mybatis项目实操

  • SpringBoot集成MybatisPlus项目实操

  • SpringBoot集成Spring Data JPA项目实操

数据库变更管理

  • 数据库变更管理:Liquibase or Flyway

  • SpringBoot结合Liquibase实现数据库变更管理

定时任务框架

  • 待更新

缓存

  • 待更新

安全框架

  • 待更新

开发规范

  • 待更新

前言

在《SpringBoot项目基础设施搭建》一文中有提到过 liquibase,以及还自定义了一个 Maven 插件,可能大家当时看到这块内容,虽然好奇但不知道该如何使用。本文将带着大家实操一个 SpringBoot 结合 Liquibase 的项目,看看如何新增数据表、修改表字段、初始化数据等功能,顺带使用一下 Liquibase 模版生成器插件。

如果对 Liquibase 不了解,可以先看一下我的上一篇文章《数据库变更管理:Liquibase or Flyway》。

实操

本项目包含两个小项目,一个是 liquibase 模版生成器插件,项目名叫做 liquibase-changelog-generate,另一个项目是 liquibase 应用,叫做 springboot-liquibase。

Liquibase模版生成器插件

创建一个 maven 项目 liquibase-changelog-generate,本项目具备生成 xml 和 yaml 两种格式的 changelog,个人觉得 yaml 格式的 changelog 可读性更高。

1、导入依赖

<dependencies>
  <!-- https://mvnrepository.com/artifact/org.apache.maven/maven-plugin-api -->
  <dependency>
    <groupId>org.apache.maven</groupId>
    <artifactId>maven-plugin-api</artifactId>
    <version>3.8.6</version>
  </dependency>
  <dependency>
    <groupId>org.apache.maven.plugin-tools</groupId>
    <artifactId>maven-plugin-annotations</artifactId>
    <version>3.6.4</version>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.5</version>
  </dependency>

</dependencies>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-plugin-plugin</artifactId>
      <version>3.6.4</version>
      <!-- 插件执行命令前缀 -->
      <configuration>
        <goalPrefix>hresh</goalPrefix>
        <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
      </configuration>
    </plugin>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <version>2.6.3</version>
    </plugin>
    <!-- 编码和编译和JDK版本 -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>

2、定义一个接口,提前准备好公用代码,主要是判断 changelog id 是否有非法字符,并且生成 changelog name。

public interface LiquibaseChangeLog {

  default String getChangeLogFileName(String sourceFolderPath) {
    System.out.println("> Please enter the id of this change:");
    Scanner scanner = new Scanner(System.in);
    String changeId = scanner.nextLine();
    if (StrUtil.isBlank(changeId)) {
      return null;
    }

    String changeIdPattern = "^[a-z][a-z0-9_]*$";
    Pattern pattern = Pattern.compile(changeIdPattern);
    Matcher matcher = pattern.matcher(changeId);
    if (!matcher.find()) {
      System.out.println("Change id should match " + changeIdPattern);
      return null;
    }

    if (isExistedChangeId(changeId, sourceFolderPath)) {
      System.out.println("Duplicate change id :" + changeId);
      return null;
    }

    Date now = new Date();
    String timestamp = DateUtil.format(now, "yyyyMMdd_HHmmss_SSS");
    return timestamp + "__" + changeId;
  }

  default boolean isExistedChangeId(String changeId, String sourceFolderPath) {
    File file = new File(sourceFolderPath);
    File[] files = file.listFiles();
    if (null == files) {
      return false;
    }

    for (File f : files) {
      if (f.isFile()) {
        if (f.getName().contains(changeId)) {
          return true;
        }
      }
    }
    return false;
  }
}

3、每个 changelog 文件中的 changeSet 都有一个 author 属性,用来标注是谁创建的 changelog,目前我的做法是执行终端命令来获取 git 的 userName,如果有更好的实现,望不吝赐教。

public class GitUtil {

  public static String getGitUserName() {
    try {
      String cmd = "git config user.name";
      Process p = Runtime.getRuntime().exec(cmd);
      InputStream is = p.getInputStream();
      BufferedReader reader = new BufferedReader(new InputStreamReader(is));
      String line = reader.readLine();
      p.waitFor();
      is.close();
      reader.close();
      p.destroy();
      return line;
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
    return "hresh";
  }
}

4、生成 xml 格式的 changelog

@Mojo(name = "generateModelChangeXml", defaultPhase = LifecyclePhase.PACKAGE)
public class LiquibaseChangeLogXml extends AbstractMojo implements LiquibaseChangeLog {

  // 配置的是本maven插件的配置,在pom使用configration标签进行配置 property就是名字,
  // 在配置里面的标签名字。在调用该插件的时候会看到
  @Parameter(property = "sourceFolderPath")
  private String sourceFolderPath;

  @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    System.out.println("Create a new empty model changelog in liquibase yaml file.");
    String userName = GitUtil.getGitUserName();

    String changeLogFileName = getChangeLogFileName(sourceFolderPath);
    if (StrUtil.isNotBlank(changeLogFileName)) {
      generateXmlChangeLog(changeLogFileName, userName);
    }
  }

  private void generateXmlChangeLog(String changeLogFileName, String userName) {
    String changeLogFileFullName = changeLogFileName + ".xml";
    File file = new File(sourceFolderPath, changeLogFileFullName);
    String content = "<?xml version=\"1.1\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
        + "<databaseChangeLog xmlns=\"http://www.liquibase.org/xml/ns/dbchangelog\"\n"
        + "  xmlns:ext=\"http://www.liquibase.org/xml/ns/dbchangelog-ext\"\n"
        + "  xmlns:pro=\"http://www.liquibase.org/xml/ns/pro\"\n"
        + "  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
        + "  xsi:schemaLocation=\"http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/pro http://www.liquibase.org/xml/ns/pro/liquibase-pro-latest.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd\">\n"
        + "  <changeSet author=\" " + userName + "\" id=\"" + changeLogFileName + "\">\n"
        + "  </changeSet>\n"
        + "</databaseChangeLog>";
    try {
      FileWriter fw = new FileWriter(file.getAbsoluteFile());
      BufferedWriter bw = new BufferedWriter(fw);
      bw.write(content);
      bw.close();
      fw.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

5、生成 yaml 格式的 changelog

@Mojo(name = "generateModelChangeYaml", defaultPhase = LifecyclePhase.PACKAGE)
public class LiquibaseChangeLogYaml extends AbstractMojo implements LiquibaseChangeLog {

  // 配置的是本maven插件的配置,在pom使用configration标签进行配置 property就是名字,
  // 在配置里面的标签名字。在调用该插件的时候会看到
  @Parameter(property = "sourceFolderPath")
  private String sourceFolderPath;

  @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    System.out.println("Create a new empty model changelog in liquibase yaml file.");
    String userName = GitUtil.getGitUserName();

    String changeLogFileName = getChangeLogFileName(sourceFolderPath);
    if (StrUtil.isNotBlank(changeLogFileName)) {
      generateYamlChangeLog(changeLogFileName, userName);
    }
  }

  private void generateYamlChangeLog(String changeLogFileName, String userName) {
    String changeLogFileFullName = changeLogFileName + ".yml";
    File file = new File(sourceFolderPath, changeLogFileFullName);
    String content = "databaseChangeLog:\n"
        + "  - changeSet:\n"
        + "      id: " + changeLogFileName + "\n"
        + "      author: " + userName + "\n"
        + "      changes:";
    try {
      FileWriter fw = new FileWriter(file.getAbsoluteFile());
      BufferedWriter bw = new BufferedWriter(fw);
      bw.write(content);
      bw.close();
      fw.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

6、执行 mvn install 命令,然后会在 maven 的 repository 文件中生成对应的 jar 包。

项目整体结构如下图所示:

liquibase 模版生成器项目结构

因为个人感觉 yaml 文件看起来比较简洁,所以虽然插件提供了两种格式,但后续我选择 yaml 文件。

Liquibase项目

本项目只是演示如何通过 Liquibase 新增数据表、修改表字段、初始化数据等功能,并不涉及具体的业务功能,所以代码部分会比较少。

1、引入依赖

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.6.3</version>
  <relativePath/>
</parent>

<properties>
  <java.version>1.8</java.version>
  <mysql.version>8.0.19</mysql.version>
  <org.projectlombok.version>1.18.20</org.projectlombok.version>
  <druid.version>1.1.18</druid.version>
  <liquibase.version>4.16.1</liquibase.version>
</properties>

<dependencies>
  <!-- 实现对 Spring MVC 的自动化配置 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>${mysql.version}</version>
    <scope>runtime</scope>
  </dependency>
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>${druid.version}</version>
  </dependency>
  <dependency>
    <groupId>org.liquibase</groupId>
    <artifactId>liquibase-core</artifactId>
    <version>4.16.1</version>
  </dependency>
  <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</artifactId>
    <version>3.5.1</version>
  </dependency>

</dependencies>

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    <plugin>
      <groupId>org.liquibase</groupId>
      <artifactId>liquibase-maven-plugin</artifactId>
      <version>4.16.1</version>
      <configuration>
        <!--properties文件路径,该文件记录了数据库连接信息等-->
        <propertyFile>src/main/resources/application.yml</propertyFile>
        <propertyFileWillOverride>true</propertyFileWillOverride>
      </configuration>
    </plugin>
    <plugin>
      <groupId>com.msdn.hresh</groupId>
      <artifactId>liquibase-changelog-generate</artifactId>
      <version>1.0-SNAPSHOT</version>
      <configuration>
        <sourceFolderPath>src/main/resources/liquibase/changelogs/
        </sourceFolderPath><!-- 当前应用根目录 -->
      </configuration>
    </plugin>
  </plugins>
</build>

2、application.yml 配置如下:

server:
  port: 8088

spring:
  application:
    name: springboot-liquibase
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mysql_db?serverTimezone=Hongkong&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
  liquibase:
    enabled: true
    change-log: classpath:liquibase/master.xml
    # 记录版本日志表
    database-change-log-table: databasechangelog
    # 记录版本改变lock表
    database-change-log-lock-table: databasechangeloglock

mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    lazy-loading-enabled: true

changeLogFile: src/main/resources/liquibase/master.xml
#输出文件路径配置
#outputChangeLogFile: src/main/resources/liquibase/out/out.xml

3、resources 目录下创建 Liquibase 相关文件,主要是 master.xml

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">

  <!--  定义公共参数,供数据库中使用-->
  <property name="id" value="int(11)" dbms="mysql"/>
  <property name="time" value="timestamp" dbms="mysql"/>

  <includeAll path="liquibase/changelogs"/>

</databaseChangeLog>

还需要创建 liquibase/changelogs 目录。

4、创建一个启动类,准备启动项目

@SpringBootApplication
public class LiquibaseApplication {

  public static void main(String[] args) {
    SpringApplication.run(LiquibaseApplication.class, args);
  }
}

接下来我们就进行测试使用 Liquibase 来进行数据库变更控制。

创建表

准备通过 Liquibase 来创建数据表,首先点击下面这个命令:

image-20220927212757676

然后在控制台输入 create_table_admin,回车,我们可以看到对应的文件如下:

image-20221124162316987

我们填充上述文件,将建表字段加进去。

databaseChangeLog:
  - changeSet:
      id: 20221124_161016_997__create_table_admin
      author: hresh
      changes:
        - createTable:
            tableName: admin
            columns:
              - column:
                  name: id
                  type: ${id}
                  autoIncrement: true
                  constraints:
                    primaryKey: true
                    nullable: false
              - column:
                  name: name
                  type: varchar(50)
              - column:
                  name: password
                  type: varchar(100)
              - column:
                  name: create_time
                  type: ${time}

关于 Liquibase yaml SQL 格式推荐去官网查询。

启动项目后,先来查看控制台输出:

liquibase执行日志

接着去数据库中看 databasechangelog 表记录

databasechangelog 表记录

以及 admin 表结构

admin表字段

新增表字段

使用我们的模版生成器插件,输入 add_column_address_in_admin,回车得到一个模版文件,比如说我们在 admin 表中新增 address 字段。

databaseChangeLog:
  - changeSet:
      id: 20221124_163754_923__add_column_address_in_admin
      author: hresh
      changes:
        - addColumn:
            tableName: admin
            columns:
              - column:
                  name: address
                  type: varchar(100)

再次重启项目,这里我就不贴控制台输出日志了,直接去数据库中看 admin 表的变化。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VwKKrqFR-1669598492531)(https://www.hreshhao.com/wp-content/uploads/2022/11/image-20221124164430229.png)]

创建索引

输入 create_index_in_admin,回车得到模版文件,然后填充内容:

databaseChangeLog:
  - changeSet:
      id: 20221124_164641_992__create_index_in_admin
      author: hresh
      changes:
        - createIndex:
            tableName: admin
            indexName: idx_name
            columns:
              - column:
                  name: name

查看 admin 表变化:

admin表字段

如果要修改索引,一般都是先删再增,删除索引可以这样写:

databaseChangeLog:
  - changeSet:
      id: 20221124_164641_992__create_index_in_admin
      author: hresh
      changes:
        - dropIndex:
            tableName: admin
            indexName: idx_name

初始化数据

输入 init_data_in_admin ,修改模版文件

databaseChangeLog:
  - changeSet:
      id: 20221124_165413_348__init_data_in_admin
      author: hresh
      changes:
        - sql:
            dbms: mysql
            sql: "insert into admin(name,password) values('hresh','1234')"
            stripComments:  true

重启项目后,可以发现数据表中多了一条记录。

关于 Liquibase 还有很多操作没介绍,等大家实际应用时再去发掘了,这里就不一一介绍了。

Liquibase 好用是好用,那么有没有可视化的界面呢?答案当然是有的。

plugin-生成数据库修改文档

双击liquibase plugin面板中的liquibase:dbDoc选项,会生成数据库修改文档,默认会生成到target目录中,如下图所示

liquibase文档

访问index.html会展示如下页面,简直应有尽有

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rNEztBno-1669598492532)(https://www.hreshhao.com/wp-content/uploads/2022/11/image-20221124171209541.png)]

关于 liquibase 的更多有意思的命令使用,可以花时间再去挖掘一下,这里就不过多介绍了。

问题

控制台输出 liquibase.changelog Reading resource 读取了很多没必要的文件

控制台截图如下所示:

image-20221124105341305

我们查找一个 AbstractChangeLogHistoryService 文件所在位置,发现它是 liquibase-core 包下的文件,如下所示:

liquibase-core文件展示

为什么会这样呢?首先来看下我们关于 liquibase 的配置,如下图所示:

image-20221124105629800

其中 master.xml 文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">

  <property name="id" value="int(11)" dbms="mysql"/>
  <property name="time" value="timestamp" dbms="mysql"/>

  <includeAll path="liquibase/changelog/"/>

</databaseChangeLog>

从上面可以看出,resource 目录下关于 liquibase 的文件夹和 liquibase-core 中的一样,难道是因为重名导致读取了那些文件,我们试着修改一下文件夹名称,将 changelog 改为 changelogs,顺便修改 master.xml。

再次重启项目,发现控制台就正常输出了。

简单去看了下 Liquibase 的执行流程,看看读取 changelog 时做了哪些事情,最终定位到 liquibase.integration.spring.SpringResourceAccessor 文件中的 list()方法,源码如下:

public SortedSet<String> list(String relativeTo, String path, boolean recursive, boolean includeFiles, boolean includeDirectories) throws IOException {
  String searchPath = this.getCompletePath(relativeTo, path);
  if (recursive) {
    searchPath = searchPath + "/**";
  } else {
    searchPath = searchPath + "/*";
  }

  searchPath = this.finalizeSearchPath(searchPath);
  Resource[] resources = ResourcePatternUtils.getResourcePatternResolver(this.resourceLoader).getResources(searchPath);
  SortedSet<String> returnSet = new TreeSet();
  Resource[] var9 = resources;
  int var10 = resources.length;

  for(int var11 = 0; var11 < var10; ++var11) {
    Resource resource = var9[var11];
    boolean isFile = this.resourceIsFile(resource);
    if (isFile && includeFiles) {
      returnSet.add(this.getResourcePath(resource));
    }

    if (!isFile && includeDirectories) {
      returnSet.add(this.getResourcePath(resource));
    }
  }

  return returnSet;
}

其中 searchPath 变量值为 classpath*:/liquibase/changelog/**,然后通过 ResourcePatternUtils 读取文件时,就把 liquibase-core 包下同路径的文件都扫描出来了。如下图所示:

image-20221124113407281

所以我们的应对措施暂时定为修改 changelog 目录名为 changelogs。

总结

感兴趣的朋友可以去我的 Github 下载相关代码,如果对你有所帮助,不妨 Star 一下,谢谢大家支持!

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

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

相关文章

[附源码]Python计算机毕业设计Django的党务管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

行为型模式-命令模式

package per.mjn.pattern.command;import java.util.HashMap; import java.util.Map;// 订单类 public class Order {// 餐桌号码private int diningTable;// 点的餐品和份数private Map<String, Integer> foodDir new HashMap<>();public int getDiningTable() {…

[附源码]计算机毕业设计springboot高校车辆管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

家居建材企业竞争白热化,如何通过供应商协同系统转型升级,提高核心竞争力

伴随房地产高景气时代逐渐退去&#xff0c;新房销售红利期或已接近尾声&#xff0c;家居建材需求正迈入平稳发展新阶段&#xff0c;企业之间竞争更加白热化。在面对数字化时代的快速发展&#xff0c;很多家居建材企业已达成这样的共识&#xff1a;数字化是企业未来发展的必由之…

人工智能岗位可以考什么证书?考试难不难?

最近几年人工智能在市场上的热度越来越大&#xff0c;很多企业都会利用这个项目来发展自己新渠道&#xff0c;那么想进入这一行的人需要怎么提升自己的技能呢&#xff1f;那就是考取人工智能相关的证书&#xff0c;目前阿里云人工智能是国内市场最热门的认证分为两个等级&#…

(2)点云库PCL学习——剔除点云值

1、主要参考 (1) 点云离群点剔除 — open3d python_Coding的叶子的博客-CSDN博客_离群点去除 (2) open3d之点云异常值去除&#xff08;笔记5&#xff09;_Satellite_H的博客-CSDN博客 2、剔除的方法 2.1无效值剔除 详见我的上一篇blob &#xff08;1&#xff09;点云库…

C语言—指针进阶(详解篇)

目录 1.字符指针 1.1字符指针定义 1.2 字符指针用法 2.指针数组 2.1 指针数组定义及使用 3.数组指针 3.1 数组指针定义 3.2 &数组名和数组名 3.3 数组指针的基本用法 4. 数组参数、指针参数 5. 函数指针 5.1 函数指针定义既基本使用 5.2 有趣的代码 6. 函…

BMS 信息资源e分享平台

今天分享的是一款关于医学的企业内部实战系统。当时某药企内部面临现状是医学人力资源有限、信息量需求大、信息资源传递途径受限&#xff0c;覆盖范围小。为解决目前面临的问题&#xff0c;提高信息资源的统一性、准确性和安全性&#xff0c;优化资源获取流程&#xff0c;提高…

[vite.js]按需加载自动注册组件

最近(后知后觉)发现各大ui组件库的按需引入&#xff0c;在使用vite构建项目的时候&#xff0c;都推荐使用unplugin-vue-components插件自动解析ui组件来自动注册&#xff1b;就是说不需要再import { ... } from ..了&#xff0c;该插件会自动帮助解析并注册成组件。其实之前用n…

数字文档管理不能落后的 5 个原因

数字文档管理不能落后的 5 个原因 信息管理对于几乎每个行业的组织都至关重要。从财富 500 强企业到医疗机构&#xff0c;您处理文件的地点和方式都很重要。如果您坚持基于纸张的流程&#xff0c;那么您可能会落后于其他企业而且冒着很大的风险。 在许多组织中&#xff0c;数字…

Linux的Jdk安装教程

liunx下Jdk安装教程 1.创建jdk的安装目录&#xff08;/usr/local/src&#xff09; 确保安装的目录是空的&#xff0c;如果不是空的&#xff0c;删除一下&#xff0c;或者放在自己其他的目录也可以 mkdir -p /usr/local/src/jdk这里可能会出现之前安装过jdk&#xff0c;可以用下…

用HTML+CSS做一个漂亮简单的轻量级图片相册博客网站(web前端期末大作业)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

web表单(详解)

目录 1. 表单的概述 1.1 表单组成 2. 表单标记 2.1 input标记 2.2 select标记 2.3 textarea标记 3.HTML5新增标记 3.1 datalist标记 3.2 date 输入类型 3.3 color输入类型 3.4 button标记 3.5 details标记和summary标记 3.6 progress标记 3.7 meter标记 4 综合…

【pen200-lab】10.11.1.5

pen200-lab 学习笔记 【pen200-lab】10.11.1.5 &#x1f525;系列专栏&#xff1a;pen200-lab &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月29日&#x1f334; &#x1f36d;作者…

阿里资深架构师谈 Java 进阶攻略:7 大技能 +12 份进阶笔记 + 面试 150 题

以下都是阿里大牛推荐的主流技术&#xff0c;当你全部掌握上述的这些技术那么你就已经是 P8 级别&#xff0c;而且你也已经形成了自己的体系&#xff0c;当更加新潮的技术出来时那么你自己稍微花点时间就能吃透&#xff0c;毕竟那时候你已经不是以前的那个你了&#xff0c; 懂底…

竞赛——【蓝桥杯】2022年12月第十四届蓝桥杯模拟赛第二期C/C++

1、最小的2022 问题描述 请找到一个大于 2022 的最小数&#xff0c;这个数转换成二进制之后&#xff0c;最低的 6 个二进制为全为 0 。 请将这个数的十进制形式作为答案提交。 答案提交 这是一道结果填空的题&#xff0c;你只需要算出结果后提交即可。本题的结果为一个整数…

分享 2022 年最受欢迎的黑科技工具(二)

Hello, everybody &#xff0c;2022 年最受欢迎的黑科技工具&#xff08;二&#xff09;&#xff0c;收藏一波吧&#xff0c;您的在看、转发、点赞就是对tuonioooo最大的支持&#xff01; 1.Sampler 项目地址&#xff1a;https://github.com/sqshq/sampler 官网地址&#xf…

Fmoc-PEG4-NHS酯,1314378-14-7 含有Fmoc保护胺和NHS酯

●英文&#xff1a;Fmoc-PEG4-NHS酯 ●外观以及性质&#xff1a;粘性液体或固体粉末&#xff0c;一般取决于分子量&#xff0c;是一种含有Fmoc保护胺和NHS酯的PEG连接剂。亲水性PEG间隔物增加了在水介质中的溶解度。Fmoc基团可在碱性条件下脱保护以获得游离胺&#xff0c;其可…

springboot+java+vue大学生求职招聘就业岗位匹配推荐系统

目 使用人职匹配推荐系统分为管理员和用户、企业三个权限子模块。 管理员所能使用的功能主要有&#xff1a;首页、个人中心、用户管理、企业管理、岗位信息管理、岗位类型管理、应聘信息管理、应聘状况管理、平台费用管理、系统管理等。 用户可以实现&#xff1b;首页、个人中…

JavaScript基础语法(运算符)

JavaScript基础语法&#xff08;运算符&#xff09; 概述 相比于Java,JS多了一个&#xff0c;其他的下面的运算符都一样。 一元运算符&#xff1a;&#xff0c;– 算术运算符&#xff1a;&#xff0c;-&#xff0c;*&#xff0c;/&#xff0c;% 赋值运算符&#xff1a;&…