# Conscript **Repository Path**: gcc1566/conscript ## Basic Information - **Project Name**: Conscript - **Description**: java数据库自动初始化 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2023-05-25 - **Last Updated**: 2024-10-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: SpringBoot, SpringBoot扩展, 数据库开发包, 国产数据库, Java ## README # Conscript 数据库初始化工具 > Conscript基于Spring-boot-starter的方式提供数据库初始化功能,可以随项目初次启动,自动创建相关数据库,后续系统升级所引发的数据库变化及升级也可由此完成,拆箱即用,随系统程序代码运行,省去额外的数据库升级操作, > > `支持数据库种类可扩展,目前支持通用mysql、postgreSQL、 国产数据库达梦和金仓` > > `支持执行失败回滚机制` ## 1.支持数据库类型 | 数据库类型 | 是否支持 | db-driver参数 | | ---------- | ---- | ----------- | | mysql | 是 | mysql | | mariadb | 是 | mariadb | | PostgreSQL | 是 | pgsql | | 达梦数据库 | 是 | dm | | 人大金仓8 | 是 | kingbase8 | ## 2.实现设计 核心代码结构: ```shell +---action #基础功能包 | +---mysql #mysql数据库实现 ----initDataBase #数据库初始化接口 ----DataBaseInitorFactory #数据库初始器工厂 ----AbstractInitDataBase #数据库初始器基类 ``` | 类 | 介绍 | | --------------------- | -------------- | | initDataBase | 初始化接口 | | AbstractInitDataBase | 初始化基类,定义核心逻辑 | | MySqlDataBase | Mysql实现,具体执行方式 | | DataBaseInitorFactory | 工厂类 | ```mermaid classDiagram direction TD class initDataBase{ <> isInitEd() startCoreJob() createConnection() databaseIsExitd() getCurrenDbVersion() excuteSQL(Map sqlcontent) close() } class AbstractInitDataBase { <> +void : startCoreJob() } class MySqlDataBase { +void : createConnection() +boolean:databaseIsExitd() +Float:getCurrenDbVersion() +void:excuteSQL(Map sqlcontent) } class DataBaseInitorFactory { +InitDataBase : createInitiator(DbConConfiguration config) +String:getClassUrlValue(DbType dbType) } class DbConConfiguration { +DbConConfiguration : builder() +void:set() +Object:get() } initDataBase <-- AbstractInitDataBase : AbstractInitDataBase <|-- MySqlDataBase DbConConfiguration <.. DataBaseInitorFactory initDataBase -- DataBaseInitorFactory ``` > **扩展其他类型数据库:在action目录下新建相应的数据库类型目录,然后继承AbstractInitDataBase类,实现对应的几个数据库操作方法,在DatabaseProperties的getDbDeriver()中新增对应的映射** ## 3.使用方式 可直接集成至SpringBoot项目中使用 | SpringBoot版本要求 | | -------------------------------------- | | `SpringBoot version` >=  2.3.6.RELEASE | ### 1.引入Conscript 1.1.使用jar引入 > 本地编译源码后,install到本地的maven私仓后,直接引入项目 ```xml com.gcc.airbase Conscript 1.0 ``` ### 2.配置yml文件 ```yml spring: datasource: url: jdbc:mysql://127.0.0.1:3306/test?user=root&password=1234 username: root password: 1234 driver-class-name: com.mysql.cj.jdbc.Driver db-config-file-url: sql/mysql/dbconfig.json db-driver: mysql db-name: test ``` 配置内容遵循标准的`spring.datasource`配置,并进行了增强 | 字段 | 含义 | 是否必填 | | ------------------ | ------------------------------------------------ | ---- | | url | 必选字段,遵循标准datasource即可,若使用mybaits,则按照mybaits标准填写 | 是 | | username | 必选字段,标准datasource即可,填写数据库账号 | 是 | | password | 必选字段,标准datasource即可,填写数据库连接密码 | 是 | | driver-class-name | 必选字段,标准datasource即可,填写数据库连接驱动类 | 是 | | db-config-file-url | 必选字段,指定静态SQL文件存放位置 | 是 | | db-driver | 必选字段,指定数据库类型,扩展时,可填写类路径,默认填写mysql,详细见扩展用法章节 | 是 | | dbport | 非必选字段,数据库服务端口,若url填写,可省略该字段 | 否 | | db-name | 非必选字段,需要连接的数据库名称,若url中填写,可省略该字段 | 否 | | schema | 非必须字段,若url中填写,则可省略,主要用于对部分数据库的模式配置 | 否 | | root-user | 非必选字段,**若存在对数据库权限分离有要求的需求**,可以在此填写专门用于建库的高权限账号 | 否 | | root-pass | 非必选字段,**若存在对数据库权限分离有要求的需求**,可以在此填写专门用于建库的高权限账号密码 | 否 | 此处配置相较 `spring.datasource`的基础的 `url、username、password、driver-class-name`外,增加了独有的`db-driver、db-config-file-url`,原则上只需要满足此6项配置即可使用Conscript,但建议将配置补全使用 `使用该组件,必须确保拥有较高的数据库角色权限,例如mysql的root、pgsql的postgres,否则无法使用创建数据库的功能,如需清晰划分权限,则可以通过配置项中的root-user,root-pass来将系统数据库的使用区分开来` ### 3.建立配置文件及SQL文件 以标准maven项目为例 ​ 1.根据 yml中`spring.datasource.db-config-file-url`配置的值,在resource目录下新建相应路径json文件 XX.json文件样例如下: ```json [ { "version": "1.0", "sqlfile": "a.sql", "desc": "基础数据库结构" }, { "version": "1.1", "sqlfile": "ddd.sql", "desc": "第一版升级数据库" } ] ``` | 字段 | 含义 | | ------- | --------------------------------------------------------------------------- | | version | 数据库的版本,**数字类型** | | sqlfile | 数据库升级的sql文件,叠加式追加,**注:这里要写清楚sql文件的路径(基于resource目录为基准),此处的配置决定后续sql文件的存放位置** | | desc | 维护使用的描述信息 | ​ 2.根据XX.json配置文件中的sqlfile配置项,新建相应目录追加需要预制的SQL文件 ### 4.提供一个标准的用例 yml配置文件如下: ``` spring: datasource: url: jdbc:mysql://127.0.0.1:3306/test?user=root&password=1234?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=GMT%2B8 username: root password: 1234 driver-class-name: com.mysql.cj.jdbc.Driver db-dirver: mysql db-config-file-url: sql/mysql-dbconfig.json db-name: test ``` mysql-dbconfig.json配置如下: ```json [ { "version": "1.0", "sqlfile": "sql/a.sql", "desc": "基础数据库结构" }, { "version": "1.1", "sqlfile": "sql/ddd.sql", "desc": "第一版升级数据库" } ] ``` 目录结构如下: ```shell +-java #java源码 +-resource #资源文件 | +---sql #sql初始化配置内容 |+---mysql-dbconfig.json #sql初始化配置内容 |+---a.sql #sql初始化配置内容 |+---ddd.sql #sql初始化配置内容 ``` > yml配置中的`db-config-file-url`配置决定着json配置文件的存放,如填写的路径为:sql/mysql/a.json,则需要在resource目录下新建sql文件夹,并在sql文件夹下新建mysql文件夹,然后在mysql文件夹中按照样例创建json文件;json配置文件中的sqlfile属性决定着需要执行的sql文件存放位置; > > 为了方便,建议json文件中的sql文件存放位置和json文件在同级目录 ### 5.其他说明 使用过程中获取建库结果,需要在代码中获取数据库的升级结果以便后续操作,可直接获取bean `DbIsExist` 的值来进行判定: ```java @Service public class AfterStartProcess{ @Qualifier("DbIsExid") @Autowired Boolean dbIsOk; public void test(){ if(dbIsOk){ System.out.print("数据库升级完成!") } } } ``` 默认数据库版本表名称修改,需要新增配置 `conscript.table-name` 配置项: ```yml conscript: table-Name: xxxx ``` ## 4.扩展 若支持的数据库类型不满足要求,需要自行进行扩展,项目引入后,可新建类继承 `AbstractInitDataBase`类,仅实现对应的 `initCommConn() initDbConn() databaseIsExitd() ` `createDataBase() `方法,并将新建的类路径配置到db-driver中即可 ### method:initCommConn() > 创建数据库基础JDBC连接, > > `AbstractInitDataBase`类中定义了 `commConn` 、` dbConn` 两个**Connection** 变量,该方法主要用于对 `commConn`变量进行初始化,建立基础的数据库连接,用于测试数据库联通以及执行数据库建库语句,例如MySQL中,需要建立连接mysql数据库的JDBC ```java @Override public void initCommConn() { try { Class.forName(dataBaseProperties.getDriverClassName()); String jdbc_url = "jdbc:mysql://" + dataBaseProperties.getHost() + ":" + dataBaseProperties.getDbport() + "/mysql?characterEncoding=utf8&serverTimezone=GMT%2B8"; commConn = DriverManager.getConnection(jdbc_url, dataBaseProperties.getUsername(), dataBaseProperties.getPassword()); }catch (Exception e){ log.error("【Conscript】Database initialization :data base is not connection....."); } } ``` ### method: initDbConn() > 创建数据库基础JDBC连接, > > `AbstractInitDataBase`类中定义了 `commConn` 、` dbConn` 两个**Connection** 变量,该方法主要用于对 `dbConn`变量进行初始化,建立对目标数据库(配置中指定的数据库)实例的JDBC连接,用于执行SQL语句 ```java @Override public void initDbConn() { try{ String url = "jdbc:mysql://"+ dataBaseProperties.getHost()+":"+ dataBaseProperties.getDbport()+"/"+ dataBaseProperties.getDbName()+"?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=GMT%2B8"; dbConn = DriverManager.getConnection(url, dataBaseProperties.getUsername(), dataBaseProperties.getPassword()); }catch (Exception e){ log.warn(""); } } ``` ### method:databaseIsExitd(Connection connection) > 确定需要连接的目标数据库实例((配置中指定的数据库))是否存在,使用`commConn`进行确认,不同的数据库确认实例的方式不同,可根据具体要扩展的数据库类型自行进行定义 ```java @Override public boolean databaseIsExitd(Connection connection){ try { Statement stmt = connection.createStatement(); ResultSet res = stmt.executeQuery("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name= \""+ dataBaseProperties.getDbName()+"\""); if(res.next() && res.getInt(1) == 0){ stmt.close(); return false; } return true; }catch (Exception e){ log.error("【Conscript】Database initialization :database base query is error"); } return false; } ``` ### method:createDataBaseSQL() > 创建指定的数据库的建库语句,仅返回对应的建库语句即可 ```java @Override public String createDataBaseSQL() { return "CREATE DATABASE IF NOT EXISTS "+ dataBaseProperties.getDbName()+" DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci"; } ``` ## 5.回滚保护机制 为防止用户自定义的sql文件中存在错误的语法,导致升级建库过程因部分sql错误而失败,但正常SQL却已经执行,污染了原始数据库;Conscript进行了加强,可以将升级过程停止于错误sql文件之前,保证存在错误语法的sql文件不会被执行,从而保证不会污染原始数据库 ```mermaid graph LR; sql文件 --> 语法校验; 语法校验 --> 数据事务操作保护 ``` 保护机制为两层防护,第一层为语法保护,保证执行的sql语法都是正确的,能够被数据库执行的,其次进行一次数据操作的回滚,当正确的SQL满足了语法要求,但是如果存在语义错误(主键冲突、字段和值不匹配等造成的数据操作失败),则进行数据 `增删改`的回滚机制,保证数据不被污染 > 注:因开启回滚保护机制,需要对sql文件进行语法校验,sql文件数量较大时会有较高的性能损耗,可根据实际情况使用,默认是不开启状态 ### 使用方法 yml文件中新增`conscript.parse-enable`属性,取值范围为布尔,不配置时默认为 false ```yml conscript: parse-enable: true ``` ## 6.其他问题 6.1 与Mybatis-plus一起使用时,会出现Conscript在Mybatis-Plus之后加载的情况,此时会因为未创建数据库,导致Mybatis-plus的SqlSession创建失败,解决方案需要在启动类上使用注解@DependsOn,手动干预starter加载顺序,先加载Conscript的 `DbIsExist` bean,如下: ```java @SpringBootApplication @DependsOn("DbIsExist") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ```