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

在成都如何找到做网站的公司福州做网站的公司电话

在成都如何找到做网站的公司,福州做网站的公司电话,北京专业网站设计推荐,定制网络开发流程目录 一、前言 1.1 单独执行初始化sql 1.2 程序自动执行 二、数据库版本升级管理问题 三、spring 框架sql自动管理机制 3.1 jdbcTemplate 方式 3.1.1 创建数据库 3.1.2 创建 springboot 工程 3.1.3 初始化sql脚本 3.1.4 核心配置类 3.1.5 执行sql初始化 3.2 配置文…目录 一、前言 1.1 单独执行初始化sql 1.2 程序自动执行 二、数据库版本升级管理问题 三、spring 框架sql自动管理机制 3.1 jdbcTemplate 方式 3.1.1 创建数据库 3.1.2 创建 springboot 工程 3.1.3 初始化sql脚本 3.1.4 核心配置类 3.1.5 执行sql初始化 3.2 配置文件方式 四、Flyway 实现数据库版本自动管理 4.1 Flyway 简介 4.2 Flyway 执行流程与原理 4.3 Flyway sql脚本命名规范 4.3.1 sql命名参考示例 4.3.2 sql命名规范补充说明 4.4 SpringBoot集成Flyway 4.4.1 引入基础依赖 4.4.2 配置Flyway参数 4.4.3 准备升级sql脚本 4.4.4 测试效果一执行sql初始化建表 4.4.5 测试效果二执行sql创建索引 4.5 使用经验与问题总结 五、Liquibase 实现数据库版本自动管理 5.1 Liquibase简介 5.2 Liquibase 优势 5.3 SpringBoot集成Liquibase 5.3.1 导入依赖 5.3.2 新建master.xml 5.3.3 创建sql文件 5.3.4 自定义Liquibase配置类 5.3.5 效果测试一 5.3.6 效果测试二 5.4 Flyway与Liquibase的对比 六、自研数据库版本管理SDK工具 6.1 实现思路 6.2 核心代码 七、写在文末 一、前言 当springboot微服务项目完成从开发到测试全流程后通常来说最终交付产物是一个完整的安装包。在交付产物中一个重要文件就是sql脚本即项目在部署完成之后运行需要的数据库表。简而言之就是如何在完成项目安装后减少或降低sql执行的效率或者说如何在项目实施时减少人工对sql相关的干预呢结合实践经验有下面几种思路提供参考 1.1 单独执行初始化sql sql脚本统一放在一个外部文件中在项目安装部署之前先执行初始化sql然后再安装项目。 1好处sql文件单独维护与项目代码分开可以避免文件管理上的麻烦 2缺点实施时需要较多的人力干预如果sql与工程代码是多人维护升级中遇到问题需要多方协调人力参与排查问题而且这种方式难以做到自动化。 1.2 程序自动执行 顾名思义这种方式依托程序自动完成sql的执行即项目安装之前通过程序就可以完成所需sql表的初始化相比上面单独执行sql的方式来说尽量减少了人工的干预即人力成本的浪费。 二、数据库版本升级管理问题 在正式开始讨论如何使用程序自动管理sql之前先来聊聊为什么会涉及数据库版本的管理问题来看下面这张图 从这张图可还原下相对真实的开发场景 V1版本初始化数据表给部分表创建索引同时初始化部分数据 V1版本的业务开发完成并上线 V2版本新的需求来了需要继续追加新表之前的某个表需要补充字段同时某个表需要批量修改数据以适配新的业务 V3... 三、spring 框架sql自动管理机制 spring框架可以说早已成为很多微服务项目的标配所以很多外部组件都提供了与spring框架整合的SDK或独立jar包这里提供两种常用的外部jar可以比较容易实现sql脚本的自动化执行分别是jdbc-template与spring自带的配置方式下面分别进行说明 3.1 jdbcTemplate 方式 下面演示使用jdbcTemplate完成一个sql脚本自动执行的过程。 3.1.1 创建数据库 创建一个数据库名为bank可自定 create database bank; 3.1.2 创建 springboot 工程 创建一个spring boot工程目录结构如下 3.1.3 初始化sql脚本 在resource目录下创建一个db的文件夹里面有一个init.sql文件初始化sql脚本如下 -- 初始化建表sql use bank;-- tb_user表建表 CREATE TABLE tb_user (id int(12) NOT NULL,user_name varchar(32) DEFAULT NULL,password varchar(64) DEFAULT NULL,phone varchar(32) DEFAULT NULL,address varchar(64) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8;-- tb_role表建表 CREATE TABLE tb_role (id int(12) NOT NULL,role_name varchar(32) DEFAULT NULL,role_type varchar(64) DEFAULT NULL,code varchar(32) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8;-- tb_role 表增加索引 alter tb_role add index idx_code(code); 3.1.4 核心配置类 import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.core.io.Resource; import org.springframework.jdbc.datasource.init.DataSourceInitializer; import org.springframework.jdbc.datasource.init.DatabasePopulator; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.stereotype.Component;import javax.sql.DataSource;/*** 成功执行*/ Component Slf4j public class DatabaseInitializer implements InitializingBean {Value(classpath:db/init.sql)private Resource dataScript;Beanpublic DataSourceInitializer dataSourceInitializer(final DataSource dataSource) {final DataSourceInitializer initializer new DataSourceInitializer();// 设置数据源initializer.setDataSource(dataSource);initializer.setDatabasePopulator(databasePopulator());return initializer;}private DatabasePopulator databasePopulator() {final ResourceDatabasePopulator populator new ResourceDatabasePopulator();populator.addScripts(dataScript);log.info(sql脚本初始化完成);return populator;}Overridepublic void afterPropertiesSet() throws Exception {}} 3.1.5 执行sql初始化 全部的配置到这里就完成了启动工程即可执行由于这种方式是实现了InitializingBean即在容器完成bean的初始化过程中执行所以伴随容器的启动即可完成sql的自动执行执行之后可看到如下效果 启动完成后数据表也创建出来了 3.2 配置文件方式 也可以直接通过配置文件的方式按照约定配置好相关的属性之后即可完成sql脚本的自动初始化参考配置如下相关的配置说明见备注 spring:datasource:type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://IP:3306/bank?createDatabaseIfNotExisttrueuseUnicodetrueuseJDBCCompliantTimezoneShifttrueuseLegacyDatetimeCodefalseserverTimezoneAsia/ShanghaicharacterEncodingutf8driver-class-name: com.mysql.jdbc.Driverusername: rootpassword: root#初始化sql建表相关配置属性 schema: classpath:db/init.sql #初始化sql语句DDL相关data: classpath:db/data.sql # 追加数据添加字段索引等separator: ; # sql语句中的结束分隔符通常为;continue-on-error: false #执行错误时是否继续initialization-mode: always #是否每次启动工程时都执行platform: mysql #使用哪种数据库sqlScriptEncoding: utf-8 #编码 缺点上述这种方式有一个明显的缺点就是第一次初始化之后再次启动时如果配置initialization-mode仍然开启的话将会报错只能手动修改配置。 四、Flyway 实现数据库版本自动管理 4.1 Flyway 简介 Flyway是一个款数据库版本管理工具程序通过集成Flyway可实现启动项目时自动执行项目中数据库迭代升级所需Sql语句从而减少sql升级时人工介入与干预。 Flyway是独立于数据库的应用、管理并跟踪数据库变更的数据库版本管理工具。通俗来讲Flyway就像Git管理代码那样管理不同版数据库本sql脚本从而做到数据库同步升级。 4.2 Flyway 执行流程与原理 Flyway 是如何实现自动管理不同版本的sql文件呢下面给出其执行过程 按照Flyway 的配置要求完成相关的配置信息 运行项目会在默认的数据库下没有指定的话就用在项目的数据库下创建Flyway自带的数据表用于存储Flyway运行中的数据比如记录了本次升级的版本号时间等 对工程文件目录进行扫描主要是需要初始化加载或运行的sql文件路径然后Flyway执行数据迁移时将基于sql脚本版本号进行排序按顺序执行这个由Flyway内部的机制保障 初次执行完sql脚本之后将会在Flyway运行时数据表中记录关键的信息比如本次升级的版本号下次项目继续启动时将会对比sql文件以确定是否需要再次执行sql升级 4.3 Flyway sql脚本命名规范 下图是一张权威的关于使用Flyway命名sql脚本的规范说明。 关于这张图做如下的说明 Prefix (前缀) V 为版本迁移U 为回滚迁移R 为可重复迁移。在日常开发中我们使用 V 前缀表示版本迁移。绝大多数情况下我们只会使用 V 前缀。 Version (版本号) 每一个迁移脚本都需要一个对应一个唯一的版本号。而脚本的执行顺序按照版本号的顺序。一般情况下每次发生了sql脚本的变更我们使用数字自增即可。 Separator (分隔符) 两个 _ 即 __ 约定的一种配置建议按这种规范执行。 Description (描述) 在下面的示例中我们使用 init或update一般见名知义即可。 Suffix (后缀) .sql 可配置不过一般可以不用配置默认即为.sql。 4.3.1 sql命名参考示例 sql命名规范非常重要涉及到开发中可能会踩坑的问题为了更好的加深对此问题的理解我们以几个命名文件为例加以说明 有如下几个sql文件 V20230810__init.sql V20230810__data.sql V20230810.01_init.sql V20230810.02_data.sql V大写 固定大写必须大写小写不执行约定规范 20230810.01 20230810是日期后面用.01代表序号如果前面日期相同但是后面的序号不一样.02比.01版本高.03比.02版本高Flyway 在执行sql脚本时是严格按照版本号的顺序进行 执行顺序 Flyway 比较两个 SQL 文件的先后顺序时采用 采用左对齐原则, 缺位用 0 代替 。 举几个例子一看便知 1.0.1.1 比 1.0.1 版本高1.0.10 比 1.0.9.4 版本高1.0.10 和 1.0.010 版本号一样每个版本号部分的前导 0 会被忽略 如下是下文两个需要执行的sql 4.3.2 sql命名规范补充说明 需重复运行的SQL则以大写的“R”开头后面再以两个下划线分割接着跟文件名称最后以.sql结尾比如R20230809.03index.sql; 当与V开头的sql文件在一起执行时V开头的SQL执行优先级要比R开头的SQL优先级高 4.4 SpringBoot集成Flyway 下面介绍如何与springboot进行整合使用实现程序的自动版本管理需求如下 1、V1版本初始化建表 2、V2版本给表初始化一些数据补充索引、字段等 3、V3版本依次类推.. 4.4.1 引入基础依赖 dependencygroupIdorg.flywaydb/groupIdartifactIdflyway-core/artifactIdversion6.0.7/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactIdversion2.3.2.RELEASE/version/dependency 4.4.2 配置Flyway参数 在application.yml中添加如下配置可视情况补充 spring:datasource:type: com.alibaba.druid.pool.DruidDataSourceurl: jdbc:mysql://IP:3306/bankdriver-class-name: com.mysql.jdbc.Driverusername: rootpassword: rootflyway:enabled: trueencoding: UTF-8cleanDisabled: true## 迁移sql脚本文件存放路径默认db/migrationlocations: classpath:db/migration## 迁移sql脚本文件名称的前缀默认VsqlMigrationPrefix: V## 迁移sql脚本文件名称的分隔符默认2个下划线__sqlMigrationSeparator: __# 迁移sql脚本文件名称的后缀sqlMigrationSuffixes: .sqlvalidateOnMigrate: true# 设置为true当迁移发现数据库非空且存在没有元数据的表时自动执行基准迁移新建schema_version表baselineOnMigrate: true 更多的配置参数可以进入到FlywayProperties这个类中详细查看上述配置中可结合备注进行了解其中核心的配置包括 locations即升级sql的文件位置sqlMigrationSuffixes执行sql文件的后缀名数据库连接信息必不可少因为Flyway升级过程中需要初始化自己的表 4.4.3 准备升级sql脚本 在resource的目录下创建db文件目录在这个文件目录下创建升级相关的sql文件如下 V20230808.01__init.sql 文件中存放的是初始化安装相关的建表sql内容如下 -- 初始化建表sql use bank;-- tb_user表建表 CREATE TABLE tb_user (id int(12) NOT NULL,user_name varchar(32) DEFAULT NULL,password varchar(64) DEFAULT NULL,phone varchar(32) DEFAULT NULL,address varchar(64) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8;-- tb_role表建表 CREATE TABLE tb_role (id int(12) NOT NULL,role_name varchar(32) DEFAULT NULL,role_type varchar(64) DEFAULT NULL,code varchar(32) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8; V20230808.02__update.sql 文件中存放的是追加索引追加字段相关内容如下 -- 初始化建表sql use bank;-- 给tb_user表添加一个索引 alter table tb_user add index idx_name(user_name);-- 给tb_user表追加新字段 alter table tb_user add column woker varchar(32) default null; 4.4.4 测试效果一执行sql初始化建表 直接启动工程即可自动执行为了看到效果可以先将V20230808.02__update.sql内容注释掉第一次执行完成后再执行 第一次执行后从日志不难看出对应的sql脚本被执行了尽管另一个被注释了仍然输出了这也就是说只要放到被扫描的目录下就能被Flyway检测到 启动完成后检查数据库看到相关的表也被创建出来了 在上文中谈到Flyway是通过flyway_schema_history这个表来管理数据库版本升级的打开这张表可以看到记录了详细而完整的与升级sql文件相关的信息 4.4.5 测试效果二执行sql创建索引 放开V20230808.02__update.sql内容注释再次启动工程执行遗憾的是此时启动报错了这个错误大概是说检测到数据库中flyway_schema_history这张表的checksum的数据与当前准备执行的sql文件版本冲突了直接抛出错。 这也就是说其实上面的这种操作方式是有问题的因为一开始就把V20230808.02__update.sql这个文件放在这个目录下面了尽管里面没有要执行的sql但是Flyway仍然会在表中创建一个checksum的数据记录这个文件的元信息所以再次执行的时候我们再将注释放开的时候执行会报错解决的办法是 初始化sql使用V1版本再次发生sql变更的时候新建一个V2的文件记录变更的sql内容放在扫描的目录下即可 按照这个思路再次走一遍这个流程之后去数据库中确认下索引是否成功创建 再检查字段是否追加成功   在没有对上述两个sql文件做任何修改的情况下再次执行可以看到再次启动工程时就不会报错了 4.5 使用经验与问题总结 这里结合实际使用经验将实际开发中遇到的问题做一个总结避免后续踩坑 V开头的sql脚本执行后不可修改修改会导致启动项目时Flyway检测不通过导致项目启动失败一旦有sql变更建议增加版本号使用新的sql文件开发或测试阶段需修改已执行脚本可通过删除相关flyway_schema_history表元数据后重启项目脚本命名不规范导致脚本未被执行比如未使用V开头命名文件可参阅官网或资料认真了解命名规范脚本文件名版本和描述之间没有使用双下滑线‘__’分割导致执行失败新增脚本版本号没有大于已有版本号导致执行失败各sql语句要以分号‘;’结尾忘记用分号会导致sql执行失败语法格式牢记于心sql语句不满足可重复执行新环境执行sql导致失败 五、Liquibase 实现数据库版本自动管理 与Flyway相同的是通过项目集成都可以完成对sql脚本的自动化管理但是Liquibase提供了更丰富的操作管理配置管理以及插件化管理。 5.1 Liquibase简介 Liquibase是一个数据库变更的版本控制工具。项目中通过Liquibase解析用户编写的Liquibase的配置文件生成sql语句并执行和记录执行是根据记录确定sql语句是否曾经执行过和配置文件里的预判断语句确定sql是否执行。官网地址liquibase官网地址Liquibase开发文档liquibase文档 5.2 Liquibase 优势 下面总结了Liquibase的在做sql版本变更时的优势 可以用上下文控制sql在何时何地如何执行根据配置文件自动生成sql语句用于预览配置文件支持SQL、XML、JSON 或者 YAML可重复执行迁移可回滚且支持插件化配置拓展兼容14中主流数据库如oraclemysqlpg等支持平滑迁移版本控制按序执行支持schmea的变更与springboot整合简单上手简单支持schema方式的多租户(multi-tenant) 5.3 SpringBoot集成Liquibase 使用SpringBoot与Liquibase进行整合的方式有很多网上的参考资料也比较丰富下面演示一种最简单的方式方便开发中直接拿来使用。 5.3.1 导入依赖 dependencygroupIdorg.liquibase/groupIdartifactIdliquibase-core/artifactIdversion3.6.3/version/dependency 5.3.2 新建master.xml 使用该文件用于指定数据库初始化脚本的位置整个配置文件以及sql脚本目录结构如下 master.xml配置如下不难看出在xml文件中使用了changeSet 这个标签里面配置了sql脚本相关的信息 ?xml version1.0 encodingUTF-8? databaseChangeLog xmlnshttp://www.liquibase.org/xml/ns/dbchangelogxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsdchangeSet idv1.0.0-1 authorzcy contextmainsqlFile pathclasspath:/sql/v1.0.0/1.init.sql/sqlFile pathclasspath:/sql/v1.0.0/2.init-data.sql//changeSet/databaseChangeLog5.3.3 创建sql文件 sql文件中包括一个初始化表相关另一个为创建索引和追加字段与上文相同不再单独列举 5.3.4 自定义Liquibase配置类 自定义配置类通过该类在spring容器完成初始化时自动执行对sql的管理这个与上文jdbcTemplate是不是很相似原理差不多 import liquibase.integration.spring.SpringLiquibase; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.DefaultResourceLoader;import javax.annotation.Resource; import javax.sql.DataSource;Slf4j Configuration public class LiquibaseConfig {//指定liquibase版本Value(${dc.version:main})private String contexts;//是否开启liquibaseValue(${dc.liquibase.enable:true})private Boolean enable;//liquibase用到的两张表记录与管理升级sql相关private static final String DATABASE_CHANGE_LOG_TABLE lqb_changelog_demo;private static final String DATABASE_CHANGE_LOG_LOCK_TABLE lqb_lock_demo;Resourceprivate DataSource dataSource;/*** liquibase bean配置声明*/Beanpublic SpringLiquibase liquibase() {SpringLiquibase liquibase new SpringLiquibase();// Liquibase xml 文件路径liquibase.setChangeLog(classpath:sql/master.xml);liquibase.setDataSource(dataSource);if (StringUtils.isNotBlank(contexts)) {liquibase.setContexts(contexts);}liquibase.setShouldRun(enable);liquibase.setResourceLoader(new DefaultResourceLoader());// 覆盖Liquibase changelog表名liquibase.setDatabaseChangeLogTable(DATABASE_CHANGE_LOG_TABLE);liquibase.setDatabaseChangeLogLockTable(DATABASE_CHANGE_LOG_LOCK_TABLE);return liquibase;}}5.3.5 效果测试一 到这里主要的配置就完成了清空bank数据库然后启动工程 从控制台输出的日志可以看到使用Liquibase建表的详细过程 启动完成后检查数据库可以看到表成功创建出来同时用于管理版本相关的两张表也一起创建出来了 如果重新启动工程不会报错也不会再次重新创建说明可以自动进行sql数据库的版本管理了 5.3.6 效果测试二 再在xml中追加新的sql文件即v1.0.1的目录和相关的sql脚本 ?xml version1.0 encodingUTF-8? databaseChangeLog xmlnshttp://www.liquibase.org/xml/ns/dbchangelogxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsdchangeSet idv1.0.0-1 authorzcy contextmainsqlFile pathclasspath:/sql/v1.0.0/1.init.sql/sqlFile pathclasspath:/sql/v1.0.0/2.init-data.sql//changeSetchangeSet idv1.0.1-1 authorzcy contextmainsqlFile pathclasspath:/sql/v1.0.1/1.init.sql//changeSet/databaseChangeLog1.init.sql内容如下 -- 初始化建表sql use bank;-- tb_depart表建表 CREATE TABLE tb_depart (id int(12) NOT NULL,dept_name varchar(32) DEFAULT NULL,dept_type varchar(64) DEFAULT NULL,code varchar(32) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8; 重启工程之后控制台再次输出了sql中创建表的信息 检查数据库sql中的表也被创建出来了 关于使用Liquibase进行版本管理和命名规范的问题限于篇幅原因就不再详细展开了有兴趣的同学可参考相关资料和官方文档网上的资料很丰富。 5.4 Flyway与Liquibase的对比 从使用上来说Flyway与Liquibase都能与项目快速实现整合实现对数据库版本的管理成本较小但是两款数据库迁移工具其实定位上是差别的经验来说如果项目规模较小整体变动不大的话可以考虑用 Flyway 而大应用和企业应用用 Liquibase 更合适。 不过从入门到使用Liquibase 并未看到比 Flyway 带来多大优势反倒 Flyway 基于“约定大于配置”的思想在使用上更加便捷也更容易实现标准化。 六、自研数据库版本管理SDK工具 如果觉得引入外部或第三方组件仍觉得麻烦可以考虑自己开发一个SDK用来做数据库版本管理 比如像SAAS这样的平台应用可能存在非常多的微服务模块如果每个应用都引第三方组件一方面技术栈难以统一另一方面不同的应用开发人员存在不同的学习成本相反如果自研一套SDK工具只需要发布一下SDK其他应用按照SDK的规范统一接入使用即可。 6.1 实现思路 自研SDK其实也没有想象中那么难一方面可以借鉴上述几种外部组件的实现思路另一方面也是最核心的就是要制定标准规范即sql文件的内容必须按照一定的规范编写否则解析的时候会出现各种问题总结来说如下 统一技术栈建议SDK中尽可能少的依赖第三方包商讨并制定sql文件的格式规范下文会给出一个模板规范SDK对外暴露的信息尽可能少方便其他应用引用减少对接和学习成本 如下是接下来示例中即将要做的一个sql文件的内容 --version 1.0.0,build 2023-08-11 ,新建用户表 CREATE TABLE tb_user (id int(12) NOT NULL,user_name varchar(32) DEFAULT NULL,password varchar(64) DEFAULT NULL,phone varchar(32) DEFAULT NULL,address varchar(64) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8; 不难看出在该文件中以 --version 开头的内容就是规范的核心内容即 version 表示每次升级涉及到的sql的版本号至于是1.0.0或1.0等可以根据团队习惯来定需要注意的是每次发生了sql版本的变更程序在启动之后会执行sql的变更 build 即本次升级sql的时间也可以理解为一个标识位即发生sql变更的时间方便后续出现问题时进行sql的回滚或问题追溯 至于其他的信息比如后面的备注或者可以根据自身的需求再补充其他的内容因为这些信息将会作为一个记录变更sql信息的表进行数据存储说到这里相信有经验的同学应该知道大致的实现思路了下面用一张图来说明该方案的实现过程这也是程序中实现的思路 6.2 核心代码 结合上面的实现业务流程下面给出核心的实现逻辑最终可以将该核心实现做成一个SDK外部服务可以直接应用通过调用核心的入口方法传入相关的基础参数即可同时为了最小化依赖这里操作数据库使用的是jdbc的方式 import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.sql.*; import java.util.LinkedList; import java.util.List; import java.util.regex.Pattern;/*** 操作sysversion表相关*/ public class SysVersionUtils {public static void main(String[] args){boolean sysVersionTableExist SysVersionConfig.isSysVersionTableExist();if(!sysVersionTableExist){//如果没有创建表执行表的创建SysVersionConfig.checkAndCreateSysVersion();}//到这一步说明已经创建完成了接下来开始解析sql文件进行脚本的初始化String sqlFilePath classpath:sql/table_init.sql;parseAndStoreVersionInfo(sqlFilePath);}public static void parseAndStoreVersionInfo(String sqlFilePat) {File databaseFile null;try {databaseFile ResourceUtils.getFile(sqlFilePat);} catch (FileNotFoundException e) {System.out.println(获取或解析sql文件失败);e.printStackTrace();return;}ListString readLines null;try {readLines FileUtils.readLines(databaseFile, UTF-8);} catch (IOException e) {e.printStackTrace();System.out.println(解析sql文件失败);return;}if (CollectionUtils.isEmpty(readLines)) {return;}//如果解析数据时发现不合理的数据行是直接抛出异常不再执行还是忽略可以自行扩展这里暂时跳过记录日志ListString versionInfoList new LinkedList();for (String readLine : readLines) {if (!isValidVersionTagLine(readLine)) {continue;}versionInfoList.add(readLine);}//检查数据库中sys_version数据是否存在Connection connection JdbcUtils.getConnection();VersionRecord dbVersionRecord getDbVersionRecord(connection);if (dbVersionRecord null) {System.out.println(初次执行sql变更);String upgradeSql getSqlTextFromLines(readLines);System.out.println(to execute sql : upgradeSql);//解析得到最后一行的version数据String lastVersionLine ((LinkedListString) versionInfoList).getLast();TemLineRecord temLineRecord ParseUtils.getFromLine(lastVersionLine);System.out.println(本次变更的sql版本号信息 temLineRecord.getVersionNum() : temLineRecord.getBuildDate());//TODO 执行sql建表相关的创建initSysVersion(connection,app,temLineRecord.getVersionNum(),temLineRecord.getBuildDate());//执行sql升级doUpgradeSql(connection,upgradeSql);System.out.println(完成sql的初始化执行);return;}/*** FIXME 1、如果sys_version已经有数据了说明之前做过升级了只需要解析最新的版本变更;* FIXME 2、可以重复执行如果没有版本变更直接忽略不用解析*///拿到最后一行的version信息下面使用String lastVersionLine ((LinkedListString) versionInfoList).getLast();TemLineRecord lineRecord ParseUtils.getFromLine(lastVersionLine);if(lineRecord.getBuildDate().equals(dbVersionRecord.getBuildDate()) lineRecord.getVersionNum().equals(dbVersionRecord.getVersionNum())){System.out.println(已经是最新版本号无需升级);return;}//从哪一行开始的数据才是本次需要执行的sql呢那就是数据库中的两个字段和 --version 以及时间正好能够对的上的那一行开始getTargetSqlLines(readLines, versionInfoList, dbVersionRecord);String sqlText getSqlTextFromLines(readLines);System.out.println(待执行的sql sqlText);//执行本次sql的更新doUpgradeSql(connection,sqlText);//升级版本号updateSysVersion(lineRecord,dbVersionRecord.getId());}private static void getTargetSqlLines(ListString readLines, ListString versionInfoList, VersionRecord dbVersionRecord) {String targetLine null;int lineNum 0;for (int i 0; i versionInfoList.size() - 1; i) {if (versionInfoList.get(i).contains(dbVersionRecord.getVersionNum()) versionInfoList.get(i).contains(dbVersionRecord.getBuildDate())) {lineNum i;break;}}targetLine versionInfoList.get(lineNum 1);int targetNum 0;for (int i 0; i readLines.size(); i) {if (readLines.get(i).equalsIgnoreCase(targetLine)) {//找到这一行的行号targetNum i;break;}}//接下来移除targetNum之前的所有元素readLines.subList(0, targetNum1).clear();}/*** 将读取到的数据行组装成sql* param allLines* return*/public static String getSqlTextFromLines(ListString allLines){if(CollectionUtils.isEmpty(allLines)){System.out.println(数据行为空);return null;}StringBuilder stringBuilder new StringBuilder();for(String text : allLines){if(text.startsWith(--)){continue;}else if(StringUtils.isEmpty(text)){continue;}stringBuilder.append(text);stringBuilder.append(\n);}return stringBuilder.toString();}/*** 更新版本号* param lineRecord* param id*/public static void updateSysVersion(TemLineRecord lineRecord,String id){Connection connection JdbcUtils.getConnection();PreparedStatement psnull;String sqlUPDATE sys_version SET BUILD_DATE?, VERSION_NUM ? WHERE id?;try {psconnection.prepareStatement(sql);ps.setString(1, lineRecord.getBuildDate());ps.setString(2, lineRecord.getVersionNum());ps.setString(3, id);ps.execute();} catch (SQLException e) {e.printStackTrace();}finally {try {connection.close();ps.close();} catch (SQLException e) {e.printStackTrace();}}}/*** 初次新增版本号* param connection* param id* param versionNum* param dateStr*/public static void initSysVersion(Connection connection,String id,String versionNum,String dateStr){PreparedStatement psnull;String sql insert into sys_version values( id,versionNum,dateStr);try {ps connection.prepareStatement(sql);ps.execute(sql);System.out.println(初始化插入sys_version版本号成功);} catch (SQLException e) {e.printStackTrace();System.out.println(初始化插入sys_version版本号失败);}}/*** 执行升级sql* param connection* param sql* throws SQLException*/private static void doUpgradeSql(Connection connection, String sql) {if(StringUtils.isEmpty(sql)){System.out.println(带升级的sql不存在);return;}Statement statement null;try {statement connection.createStatement();String[] sqlCommands sql.split(;);for (String sqlCommand : sqlCommands) {if (!sqlCommand.trim().isEmpty()) {try {statement.executeUpdate(sqlCommand);} catch (SQLException e) {e.printStackTrace();System.out.println(执行升级sql失败);}}}} catch (SQLException e) {e.printStackTrace();}finally {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}}/*** 获取数据库的最新版本号* param connection* return*/public static VersionRecord getDbVersionRecord(Connection connection) {String sql select * from sys_version;PreparedStatement ps null;ResultSet rs null;VersionRecord versionRecord null;try {ps connection.prepareStatement(sql);rs ps.executeQuery();if (rs.next()) {versionRecord new VersionRecord();versionRecord.setId(rs.getString(id));versionRecord.setBuildDate(rs.getString(BUILD_DATE));versionRecord.setVersionNum(rs.getString(VERSION_NUM));}} catch (SQLException e) {e.printStackTrace();System.out.println(获取最新版本号失败);}return versionRecord;}private static Pattern oldVersionTagPattern Pattern.compile(^--version\\s[0-9\\.],build\\s.*);public static boolean isValidVersionTagLine(String tagLine) {return oldVersionTagPattern.matcher(tagLine).matches();}}关于此处的代码如果拿来生产实现还有一些细节点值得推敲和完善比如日志的输出参数的健壮性处理上毕竟是SDK需要考虑的更加细致一点。接下来测试下效果吧在resources目录下有如下待执行的sql文件 第一次执行时version表不存在结合控制台日志可以看到表创建成功 第二次执行在sql中追加如下内容 --version 1.0.0,build 2023-08-13 ,创建索引 alter table tb_user add index idx_name(user_name); 然后再次执行 执行完成后在数据库确认下索引是否创建成功 七、写在文末 本篇通过较大的篇幅详细总结了几种常用的实现数据库版本自动管理的方式每一种都有合适的应用场景希望对看到的小伙伴们有用本篇到此结束感谢观看
文章转载自:
http://www.morning.pjjkz.cn.gov.cn.pjjkz.cn
http://www.morning.qsyyp.cn.gov.cn.qsyyp.cn
http://www.morning.cpqwb.cn.gov.cn.cpqwb.cn
http://www.morning.ychrn.cn.gov.cn.ychrn.cn
http://www.morning.nwqyq.cn.gov.cn.nwqyq.cn
http://www.morning.dxhnm.cn.gov.cn.dxhnm.cn
http://www.morning.mngh.cn.gov.cn.mngh.cn
http://www.morning.reababy.com.gov.cn.reababy.com
http://www.morning.jhswp.cn.gov.cn.jhswp.cn
http://www.morning.lrjtx.cn.gov.cn.lrjtx.cn
http://www.morning.bwjws.cn.gov.cn.bwjws.cn
http://www.morning.xjnjb.cn.gov.cn.xjnjb.cn
http://www.morning.wqrdx.cn.gov.cn.wqrdx.cn
http://www.morning.dmsxd.cn.gov.cn.dmsxd.cn
http://www.morning.wsyq.cn.gov.cn.wsyq.cn
http://www.morning.dkbsq.cn.gov.cn.dkbsq.cn
http://www.morning.pynzj.cn.gov.cn.pynzj.cn
http://www.morning.pqwrg.cn.gov.cn.pqwrg.cn
http://www.morning.mmqng.cn.gov.cn.mmqng.cn
http://www.morning.zhnpj.cn.gov.cn.zhnpj.cn
http://www.morning.wfpmt.cn.gov.cn.wfpmt.cn
http://www.morning.ylmxs.cn.gov.cn.ylmxs.cn
http://www.morning.tqgx.cn.gov.cn.tqgx.cn
http://www.morning.fmrd.cn.gov.cn.fmrd.cn
http://www.morning.nlcw.cn.gov.cn.nlcw.cn
http://www.morning.lpcpb.cn.gov.cn.lpcpb.cn
http://www.morning.mcwgn.cn.gov.cn.mcwgn.cn
http://www.morning.hjsrl.cn.gov.cn.hjsrl.cn
http://www.morning.rwcw.cn.gov.cn.rwcw.cn
http://www.morning.dlrsjc.com.gov.cn.dlrsjc.com
http://www.morning.ppgdp.cn.gov.cn.ppgdp.cn
http://www.morning.xlndf.cn.gov.cn.xlndf.cn
http://www.morning.bdqpl.cn.gov.cn.bdqpl.cn
http://www.morning.bfybb.cn.gov.cn.bfybb.cn
http://www.morning.gzzxlp.com.gov.cn.gzzxlp.com
http://www.morning.rshkh.cn.gov.cn.rshkh.cn
http://www.morning.jgnjl.cn.gov.cn.jgnjl.cn
http://www.morning.gwdmj.cn.gov.cn.gwdmj.cn
http://www.morning.rlhjg.cn.gov.cn.rlhjg.cn
http://www.morning.fhykt.cn.gov.cn.fhykt.cn
http://www.morning.kyzja.com.gov.cn.kyzja.com
http://www.morning.wfzlt.cn.gov.cn.wfzlt.cn
http://www.morning.tgmfg.cn.gov.cn.tgmfg.cn
http://www.morning.qggxt.cn.gov.cn.qggxt.cn
http://www.morning.kqzrt.cn.gov.cn.kqzrt.cn
http://www.morning.cftkz.cn.gov.cn.cftkz.cn
http://www.morning.trmpj.cn.gov.cn.trmpj.cn
http://www.morning.mlntx.cn.gov.cn.mlntx.cn
http://www.morning.jjxnp.cn.gov.cn.jjxnp.cn
http://www.morning.rgpy.cn.gov.cn.rgpy.cn
http://www.morning.wdpbq.cn.gov.cn.wdpbq.cn
http://www.morning.dfhkh.cn.gov.cn.dfhkh.cn
http://www.morning.xjqrn.cn.gov.cn.xjqrn.cn
http://www.morning.hchrb.cn.gov.cn.hchrb.cn
http://www.morning.fjshyc.com.gov.cn.fjshyc.com
http://www.morning.vjwkb.cn.gov.cn.vjwkb.cn
http://www.morning.yongkangyiyuan-pfk.com.gov.cn.yongkangyiyuan-pfk.com
http://www.morning.yfmlj.cn.gov.cn.yfmlj.cn
http://www.morning.fhghy.cn.gov.cn.fhghy.cn
http://www.morning.c7627.cn.gov.cn.c7627.cn
http://www.morning.djlxz.cn.gov.cn.djlxz.cn
http://www.morning.rtbj.cn.gov.cn.rtbj.cn
http://www.morning.sprbs.cn.gov.cn.sprbs.cn
http://www.morning.rjbb.cn.gov.cn.rjbb.cn
http://www.morning.wfzlt.cn.gov.cn.wfzlt.cn
http://www.morning.rgrys.cn.gov.cn.rgrys.cn
http://www.morning.hxbps.cn.gov.cn.hxbps.cn
http://www.morning.hqrkq.cn.gov.cn.hqrkq.cn
http://www.morning.yqlrq.cn.gov.cn.yqlrq.cn
http://www.morning.mgkb.cn.gov.cn.mgkb.cn
http://www.morning.gyjld.cn.gov.cn.gyjld.cn
http://www.morning.gbfuy28.cn.gov.cn.gbfuy28.cn
http://www.morning.xxzjb.cn.gov.cn.xxzjb.cn
http://www.morning.jsdntd.com.gov.cn.jsdntd.com
http://www.morning.rmqlf.cn.gov.cn.rmqlf.cn
http://www.morning.tsrg.cn.gov.cn.tsrg.cn
http://www.morning.tpps.cn.gov.cn.tpps.cn
http://www.morning.hmnhp.cn.gov.cn.hmnhp.cn
http://www.morning.gbxxh.cn.gov.cn.gbxxh.cn
http://www.morning.glxdk.cn.gov.cn.glxdk.cn
http://www.tj-hxxt.cn/news/240781.html

相关文章:

  • 电子商务网站的建设步骤有广州网站设计开发招聘
  • 湖州网站建设公司哪家好做网站优化最快的方式
  • 南宁seo站内关键词优化家庭网络组建方案
  • 邯郸网站设计应搜韦欣cidun8上词一款游戏的制作过程
  • 查询企业营业执照怎么查百度seo通科
  • 适合0基础网站开发软件个人网站制作成品图片
  • 创网网站后台管理系统seo优化心得
  • 电影下载网站如何做wordpress 帐号共用
  • 龙华做网站联系电话常州按天优化代理
  • 网站进度条源代码juqery-ui商城网站的建设费用
  • 中国城乡建设网站收费网站素材
  • 柳州网站建设价格深圳网站建设代理
  • 河北省建设集团有限公司网站做的网站怎样更新
  • 三合一网站有必要吗离婚在线律师
  • 合肥高端网站建设公司哪家好网站备案 取消接入
  • 简单的购物网站制作昆明seo怎么做
  • 友情链接站长平台制作宣传片视频
  • 网站开发进阶实训报告直接在原备案号下增加新网站
  • 做布料的著名网站广州地铁官网
  • 网站开发googlevue cdn做的网站
  • 制作个人网站教程怎么分析网页的布局
  • 要建立网站和账号违法违规行为数据库和什么黑名单怎么在虚拟空间做两个网站
  • 学校网站开发的背景网站建设代码题
  • 网站怎么做登陆17网站一起做网店潮汕
  • 烟台怎么做网站建设网站花多少钱
  • 哪家房屋设计公司网站新北做网站
  • 焦作网站建设哪家便宜外链工具在线
  • 百度云做网站空间做AMC12的题的网站
  • 如何制作收费网站响应式网页设计名词解释
  • 天津做网站的费用东港网站建设