关键背景

国家战略与安全

“卡脖子”风险倒闭技术自主

“自主可控”成为国家战略

数据要素化与合规要求

数据作为核心生产要素的地位提升

合规性压力增大

技术成熟度提升

技术成熟度提升

国产技术从可用到好用

信创产业生态的构建

降低供应链风险

长期成本优势

从单数据库到多数据库的发展路程

单库的维护方式

以文件的方式,配合代码仓库进行统一管理。脚本包括:创建表、创建视图、初始化数据、存储过程(已废弃)、新增列等。

示例目录结果入下:

  DDL
    ├─v1.0
        ├─table_{table_name}_create.sql
        ├─table_{table_name}_init_data.sql
        ├─sequence_{sequence_name}.sql
        └─view_{view_name}_create.sql
    ├─v1.1
        ├─table_{table_name}_create.sql
        ├─table_{table_name}_add_column.sql
        ├─sequence_{sequence_name}.sql
        └─view_{view_name}_change.sql
    ├─v1.2
        ├─table_{table_name}_create.sql
        ├─table_{table_name}_add_column.sql
        ├─sequence_{sequence_name}.sql
        └─view_{view_name}_create.sql

多数据库适配后初步方案

├─DDL
    ├─tidb
        ├─v1.0  
        └─v1.1
    ├─dm
        ├─v1.0  
        └─v1.1
    ├─gaussdb
        ├─v1.0  
        └─v1.1
    ├─kingbase
        ├─v1.0  
        └─v1.1

随之而来的问题

大量的重复的工作:建一张表需要写多个数据库的脚本,增加列字段需要修改多个数据库下的脚本。改动需手动在数据库中验证。重复且分散的操作极易出错:给gaussDB修改了,忘记同步处理金仓数据库。进而为线上增加隐患。 多数据库适配进一步的解决方案

目标

解耦数据表的定义与具体DDL实现,并实现工程化。

实现路径

1.创建不同数据库的 beetl 模板。 2.java程序自动化(可集成至spring boot项目) 3.未来的扩展仅需添加对应数据库的 beetl 模板即可。

项目结构

java依赖包

├─libs
│      antlr4-runtime-4.9.3.jar
│      beetl-3.20.1.RELEASE.jar
│      beetl-core-3.20.1.RELEASE.jar
│      beetl-default-antlr4.9-support-3.20.1.RELEASE.jar
│      beetl-ext-3.20.1.RELEASE.jar

java实体类

├─domain
│      ColumnBuilder.java 
│      ColumnDefinition.java
│      TableBuilder.java
│      TypeEnum.java
│      DefaultEnum.java
│      TableDefinition.java
│      Main.java

*Builder.java 类为构建对象的build模式。按照个人习惯构建,代码较长,就不展示啦。

其他代码如下:

TableDefinition.java

public class TableDefinition {
    private String database;
    private String tableName;
    private String comment;
    private String tablespace;
    private List<ColumnDefinition> columns;
}

ColumnDefinition.java

public class ColumnDefinition {
    private String name;
    private TypeEnum type;
    private int length;
    private int precision;
    private int scale;
    private String comment;
    private DefaultEnum defaultValueEnum = DefaultEnum.NONE;
    private String defaultValue;
}

TypeEnum.java

public enum TypeEnum {
    STRING,
    NUMBER,
    CLOB,
    DATE
}

DefaultEnum.java

public enum DefaultEnum {
    CURRENT_DATE,  // 当前时间 
    SYS_ID,        // 数据库ID 
    NONE,          // 无默认值
    FIX_VALUE      // 指定默认值
}

Main.java

public class Main {
    public static void main(String[] args){
        new Main().check();
    }
    public void check() {

        TableDefinition poc_project_baseinfo = TableDefinition.builder()
                .name("test_table")
                .database("test_user")
                .comment("测试表")
                .column(ColumnDefinition.builder()
                .string()
                .name("id")
                .length(50)
                .comment("ID")
                .build())
                .column(ColumnDefinition.builder()
                .string()
                .name("project_name")
                .comment("名称").build())
                .build();

        List<TableDefinition> tables = Arrays.asList(poc_project_baseinfo);
        
        // 篇幅限制,自动化部分下次分享
       
        GroupTemplate gt1 = getGroupTemplate("gaussdb");

        Template t1 = gt1.getTemplate("beetl/base/create_table.btl");
        t1.binding("tables", tables);
        String r1 = t1.render();

        // 仅打印 create table ddl
        System.out.println(r1);

        GroupTemplate gt2 = getGroupTemplate("dm");

        Template t2 = gt.getTemplate("beetl/base/create_table.btl");
        t2.binding("tables", tables);
        String r2 = t2.render();
        // 仅打印 create table ddl
        System.out.println(r2);
    }

    public GroupTemplate getGroupTemplate(String database) {
        try {
            ClasspathResourceLoader loader = new ClasspathResourceLoader();
            Configuration conf = Configuration.defaultConfiguration();
            System.out.println(conf.getTagMap());
            GroupTemplate gt = new GroupTemplate(loader, conf);

            Map<String, Object> shareVars = new HashMap<>();
            shareVars.put("StringEnum", TypeEnum.STRING);
            shareVars.put("NumberEnum", TypeEnum.NUMBER);
            shareVars.put("ClobEnum", TypeEnum.CLOB);
            shareVars.put("DateEnum", TypeEnum.DATE);
            shareVars.put("CURRENT_DATE", DefaultEnum.CURRENT_DATE);
            shareVars.put("SYS_ID", DefaultEnum.SYS_ID);
            shareVars.put("NONE", DefaultEnum.NONE);
            shareVars.put("FIX_VALUE", DefaultEnum.FIX_VALUE);
            shareVars.put("database", database);

            gt.setSharedVars(shareVars);
            return gt;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

beetl模板

├─beetl
    ├─base                           
        ├─add_column.btl
        ├─check_ds.btl
        ├─check_table_column.btl
        └─create_table.btl
    ├─gaussdb
        ├─check_ds.btl
        ├─check_table_column.btl
        ├─create_column.btl
        └─tablespace.btl
    └─dm
        ├─check_ds.btl
        ├─check_table_column.btl
        ├─create_column.btl
        └─tablespace.btl

base目录

定义通用脚本格式,将数据库差异部分交由对应数据库下的脚本执行。利用beetl中的 include 标签实现。

base/check_ds.btl 检查基础环境。

可根据基建情况自定义,如数据库名、用户名、对应的tablespace是否存在等等。

# 使用include标签将由对应数据库实现
<% include("../" + database + "/check_ds.btl"){} %>

base/check_table_column.btl 检查表和列是否已经存在

# 使用include标签将由对应数据库实现
<% include("../" + database + "/check_table_column.btl"){} %>

base/create_table.btl 基础建表语句

<%
for(table in tables) {
%>
create table ${table.database}.${table.tableName} (
    <%
    for(column in table.columns) {
        var end ="";
        if(!columnLP.last){
            end = ",";
        }
        # 列信息差异交由各自数据库的 create_column.btl 模板实现
        include("../" + database + "/create_column.btl",{column:column,end:end}){}
    }
    %>
) <% include("../" + database + "/tablespace.btl",{table:table}){} %>;

COMMENT ON TABLE ${table.database}.${table.tableName} IS '${table.comment}';
    <%
    for(column in table.columns) {
    %>
COMMENT ON COLUMN ${table.database}.${table.tableName}.${column.name} IS '${column.comment}';
    <%
    }
    %>
<%
}
%>

gaussDB 目录

gaussdb/check_ds.btl

SELECT user FROM dual;test_user;check failed. user must be test_user
select spcname from pg_tablespace where spcname='test_user_ts';test_user_ts;check failed. cannot find tablespace test_user_ts

gaussdb/check_table_column.btl

select table_name,column_name from information_schema.columns where table_schema='test_user' and table_name in  (
<% for(table in tables) {  %>
    '${table.tableName}'<% if(!tableLP.last) {%>,<% } %>
<% } %>
)

gaussdb/create_column.btl

<% if(column.type == StringEnum ){ %>
    ${column.name}  VARCHAR2(${column.length}) <% if(column.defaultValueEnum == FIX_VALUE){ %>default '${column.defaultValue}'<% } %> <% if(column.defaultValueEnum == SYS_ID){ %>default sys_guid()<% } %> ${end}
<% } else if (column.type == NumberEnum) { %>
    ${column.name}  NUMBER(${column.precision},${column.scale}) <% if(column.defaultValueEnum == FIX_VALUE){ %>default ${column.defaultValue}<% } %> ${end}
<% } else if (column.type == ClobEnum) { %>
    ${column.name}  CLOB ${end}
<% } else if (column.type == DateEnum) { %>
    ${column.name}  DATE <% if(column.defaultValueEnum == CURRENT_DATE){ %>default sysdate<% } %> ${end}
<% } %>

gaussdb/tablespace.btl

 tablespace ${table.tablespace}
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com