基于Spring包扫描工具和MybatisPlus逆向工程组件的数据表自动同步机制

公司产品产出的项目较多。同步数据库表结构工作很麻烦。一个alter语句要跑到N个客户机上执行脚本。超级费时麻烦。介于此,原有方案是把增量脚本放到一resource包下,项目启动时执行逐行执行一次。但由于模块开发人员较多,总有那么一两个机灵鬼漏写脚本。加上项目分支的原因,导致更新客户表结构后埋雷,炸雷。投诉不断。因此本人开发了一款可靠、自动的表结构更新构件。

原理说明:
1、由于全部表OR映射实体采用了MyBatisPlus的@TableName,TableField注解,所以利用组件(https://blog.csdn.net/qq_37148232/article/details/131821497?spm=1001.2014.3001.5501),扫描出项目中所有带@TableName的实体类。然后解析出表名、字段以及自定义注解上的建表类型,建表长度,注释,默认值等。得到最新的表结构。
2、利用从MyBatisPlus逆向工程中扒出来的表结构查询工具。稍做封装,得到项目数据库当前的表结构。
3、对比最新的表结构和当前表结构。得到缺哪些表、缺哪些字段、哪些字段更新了、
哪些字段删了。
4、将对比结果生成可执行的alter、ceate、drop、modify语句。更新数据库。为保险起见,drop和modify默认不启用。
5、开机自动运行生成的语句集。更新表结构。

代码结构如下:
在这里插入图片描述
各类说明:
ColumnInfo:包扫描实体类后得到的字段信息封装。
CreateTableHandler:自动建表处理器。
DBJDBCType:java-jdbcType映射接口。
MySqlJDBCType:Mysql系列数据库类型映射支持
OtherJDBCTYpe:Oracle系列数据库映射支持。
JDBCSupport:类型映射支持
SchemaAnalyserAutoConfiguration:主配置类
SchemaAnalyserExecutor:自动建表执行入口
SchemaDefinition:扫描后得到的表和字段信息
SchemaDefinitionLoader:包扫描及全面目字段信息加载类
SchemaExistedDefinitionLoader:项目库表结构加载类
UpdateTableHandler:Alter语句处理类

主要类代码:

public class ColumnInfo {

    private Boolean isPrimary;

    private String columnName;

    private String type;

    public String toCreateColumn() {

        if (isPrimary) {
            return columnName + " " + type + " " + "primary key";
        } else {
            return columnName + " " + type;
        }
    }
}
public class CreateTableHandler {

    @Autowired
    private JDBCSupport jdbcSupport;

    public void createTable(SchemaDefinition schemaDefinition) {
        if (!FrameworkProperties.enableAutoAlterTableCreate) {
            return;
        }
        List<String> columns = schemaDefinition.getColumns();

        Map<String, ColumnName> columnContainer = schemaDefinition.getColumnContainer();
        Map<String, Field> propContainer = schemaDefinition.getPropContainer();

        List<ColumnInfo> columnInfos = new ArrayList<>();
        TableId tableId = schemaDefinition.getTableId();
        if (null != tableId) {
            ColumnInfo columnInfo = new ColumnInfo();
            columnInfo.setColumnName(tableId.value());
            columnInfo.setIsPrimary(true);
            columnInfo.setType(jdbcSupport.decideJDBCPrimaryType());
            columnInfos.add(columnInfo);
        }

        for (String column : columns) {
            ColumnName columnName = columnContainer.get(column);
            Field field = propContainer.get(column);
            String jdbcType = jdbcSupport.decideJDBCType(column, field, columnName);
            ColumnInfo columnInfo = new ColumnInfo();
            columnInfo.setType(jdbcType);
            columnInfo.setIsPrimary(false);
            columnInfo.setColumnName(column);
            columnInfos.add(columnInfo);
        }
        jdbcSupport.createTable(schemaDefinition.getTableName(), columnInfos);
    }
}
public interface DBJDBCType {

    static DBJDBCType matchJDBCType(String databaseType) {
        return databaseType.toLowerCase().contains("mysql") ? new MySqlJDBCType() : new OtherJDBCType();
    }

    String varcharType(int length);

    String intType();

    String longType();

    String booleanType();

    String dateType();

    String textType();
}

public class JDBCSupport implements InitializingBean {

    private final static String SQL_ADD_MYSQL = "alter table %s add %s %s %s comment '%s';";

    private final static String SQL_ADD_ORACLE = "alter table %s add %s %s %s;";
    private final static String SQL_COMMENT_ORACLE = "comment on table %s.%s is '%s';";

    private final static String SQL_MODIFY = "alter table %s modify %s %s;";
    private final static String SQL_CREATE = "create table %s (%s);";

    private DataSource dataSource;

    private String databaseProductName;
    private boolean isMysql;

    public JDBCSupport(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void createTable(String tableName, List<ColumnInfo> columnInfos) {
        List<String> createColumns = ListUtils.list2list(columnInfos, ColumnInfo::toCreateColumn);
        String columnSqlItems = StrUtils.join(createColumns);
        String createSql = String.format(SQL_CREATE, tableName, columnSqlItems);
        executeSql(createSql);
    }

    public void addColumn(String tableName, String columnName, String type, Object defaultValue, String comment) {
        String sql;
        // 默认值
        String defaultValueSegment = "";
        if (StrUtils.isNotNull(defaultValue)) {
            if (defaultValue instanceof String) {
                defaultValueSegment = "default '" + defaultValue + "'";
            } else {
                if (!"-999".equals(String.valueOf(defaultValue))) {
                    defaultValueSegment = "default " + defaultValue;
                }
            }
        }
        // 注释
        comment = StrUtils.isNull(comment) ? "" : comment;
        if (isMysql) {
            sql = String.format(SQL_ADD_MYSQL, tableName, columnName, type, defaultValueSegment, comment);
            executeSql(sql);
        } else {
            sql = String.format(SQL_ADD_ORACLE, tableName, columnName, type, defaultValueSegment);
            String commentSql = String.format(SQL_COMMENT_ORACLE, tableName, columnName, comment);
            executeSql(sql);
            executeSql(commentSql);
        }


    }


    public void modifyColumn(String tableName, String columnName, String type) {
        String sql = String.format(SQL_MODIFY, tableName, columnName, type);
        executeSql(sql);
    }

    public String decideJDBCPrimaryType() {
        DBJDBCType dbjdbcType = DBJDBCType.matchJDBCType(databaseProductName);
        // 主键用60位字符
        return dbjdbcType.varcharType(60);
    }

    public String decideJDBCType(String columnName, Field field, ColumnName definition) {
        DBJDBCType dbjdbcType = DBJDBCType.matchJDBCType(databaseProductName);
        if (null != definition) {
            return chooseByColumnDefinition(definition, dbjdbcType);
        } else {
            return chooseByField(columnName, field, dbjdbcType);
        }
    }

    @Override
    public void afterPropertiesSet() {
        try (Connection connection = dataSource.getConnection()) {
            this.databaseProductName = connection.getMetaData().getDatabaseProductName();
            this.isMysql = "MySQL".equals(databaseProductName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String chooseByField(String columnName, Field field, DBJDBCType dbjdbcType) {
        if (null == field) {
            return dbjdbcType.varcharType(200);
        }
        String name = field.getName();
        Class<?> fieldType = field.getType();
        if (String.class.isAssignableFrom(fieldType)) {
            // 基于经验的一些合理猜测判断
            if (columnName.endsWith("_id")) {
                return dbjdbcType.varcharType(60);
            } else if (columnName.endsWith("_ids")) {
                return dbjdbcType.varcharType(500);
            } else if (name.equals("content")) {
                return dbjdbcType.varcharType(500);
            } else if (name.equals("createBy")) {
                return dbjdbcType.varcharType(60);
            } else if (name.equals("updateBy")) {
                return dbjdbcType.varcharType(60);
            } else if (name.equals("areaId")) {
                return dbjdbcType.varcharType(60);
            } else {
                return dbjdbcType.varcharType(200);
            }
        }
        if (Integer.class.isAssignableFrom(fieldType)) {
            // 基于经验的一些合理猜测判断
            if (columnName.startsWith("is_") || columnName.startsWith("has_")) {
                return dbjdbcType.booleanType();
            } else {
                return dbjdbcType.intType();
            }
        }

        if (Long.class.isAssignableFrom(fieldType)) {
            return dbjdbcType.longType();
        }

        if (Date.class.isAssignableFrom(fieldType)) {
            return dbjdbcType.dateType();
        }
        return dbjdbcType.varcharType(200);
    }

    private String chooseByColumnDefinition(ColumnName definition, DBJDBCType dbjdbcType) {
        if (definition.varcharColumn()) {
            return dbjdbcType.varcharType(definition.varcharLength());
        } else if (definition.booleanColumn()) {
            return dbjdbcType.booleanType();
        } else if (definition.intColumn()) {
            return dbjdbcType.intType();
        } else if (definition.longColumn()) {
            return dbjdbcType.longType();
        } else if (definition.dateColumn()) {
            return dbjdbcType.dateType();
        } else if (definition.textColumn()) {
            return dbjdbcType.textType();
        } else {
            return dbjdbcType.varcharType(definition.varcharLength());
        }
    }

    private void executeSql(String sql) {
        try (Connection connection = dataSource.getConnection();
             PreparedStatement preparedStatement = connection.prepareStatement(sql)
        ) {
            preparedStatement.execute();
        } catch (Exception e) {
            log.warn("sql[{}]执行异常", sql);
        }
    }
}
public class MySqlJDBCType implements DBJDBCType {
    @Override
    public String varcharType(int length) {
        return "varchar(" + length + ")";
    }

    @Override
    public String intType() {
        return "int";
    }

    @Override
    public String longType() {
        return "bigint";
    }

    @Override
    public String booleanType() {
        return "tinyint";
    }

    @Override
    public String dateType() {
        return "date";
    }

    @Override
    public String textType() {
        return "text";
    }
}
public class OtherJDBCType implements DBJDBCType {
    @Override
    public String varcharType(int length) {
        return "varchar2(" + (length * 2) + ")";
    }

    @Override
    public String intType() {
        return "number(10)";
    }

    @Override
    public String longType() {
        return "number(19)";
    }

    @Override
    public String booleanType() {
        return "number(1)";
    }

    @Override
    public String dateType() {
        return "date";
    }

    @Override
    public String textType() {
        return "text";
    }
}
@Configuration
@ConditionalOnProperty(prefix = "com.xxx.framework", name = "enable-auto-alter-table", havingValue = "true")
public class SchemaAnalyserAutoConfiguration {

    @Bean
    public JDBCSupport jdbcSupport(DataSource dataSource) {
        return new JDBCSupport(dataSource);
    }

    @Bean
    public CreateTableHandler createTableHandler() {
        return new CreateTableHandler();
    }

    @Bean
    public UpdateTableHandler updateTableHandler() {
        return new UpdateTableHandler();
    }

    @Bean
    public SchemaAnalyserExecutor schemaAnalyserRunner() {
        return new SchemaAnalyserExecutor();
    }

    @Bean
    @DependsOn("frameworkProperties") // 主要是读取系统类型用于一些判断,所以要依赖
    public SchemaDefinitionLoader schemaDefinitionLoader() {
        return new SchemaDefinitionLoader();
    }

    @Bean
    public SchemaExistedDefinitionLoader schemaExistedDefinitionLoader() {
        return new SchemaExistedDefinitionLoader();
    }
}

public class SchemaAnalyserExecutor implements EasySpringListener {

    @Autowired
    private CreateTableHandler createTableHandler;
    @Autowired
    private UpdateTableHandler updateTableHandler;
    @Autowired
    private SchemaDefinitionLoader schemaDefinitionLoader;
    @Autowired
    private SchemaExistedDefinitionLoader schemaExistedDefinitionLoader;

    @Override
    public void doBusiness(ApplicationContext applicationContext) {
        List<SchemaDefinition> projectSchemaDefinition = schemaDefinitionLoader.getProjectSchemaDefinition();
        Map<String, TableInfo> tableContainer = schemaExistedDefinitionLoader.findExistedTableInfo();

        generateDelete(tableContainer);
        // 对比已存在的表和字段,更新字段或新建表
        for (SchemaDefinition schemaDefinition : projectSchemaDefinition) {
            // 看表里存不存在该表的定义信息
            TableInfo tableInfo = tableContainer.get(schemaDefinition.getTableName());
            if (null != tableInfo) {
                try {
                    updateTableHandler.updateTable(schemaDefinition, tableInfo);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                try {
                    createTableHandler.createTable(schemaDefinition);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void generateDelete(Map<String, TableInfo> tableContainer) {
        if (FrameworkProperties.enableGenerateDeleteScript) {
            Set<String> strings = tableContainer.keySet();
            List<String> tableNames = ZYListUtils.set2list(strings);
            tableNames.sort(Comparator.comparing(a -> a));
            List<String> deleteSqls = new ArrayList<>();
            tableNames.forEach(tableName -> {
                deleteSqls.add("delete from " + tableName + " ;");
            });

            FileUtils.writeLines(deleteSqls, "D://clear_" + ZYDateUtils.formart(new Date(), "yyyy-MM-dd-HH-mm-ss") + ".sql", "utf-8", true);
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

public class SchemaDefinition {

    private String tableName;

    private String tableComment;
    // 表格信息
    private TableName tableNameAnnotation;
    // 主键信息
    private TableId tableId;

    private List<String> columns = new ArrayList<>();
    // 字段属性定义
    private Map<String, Field> propContainer = new HashMap<>();
    // 字段描述信息
    private Map<String, ColumnName> columnContainer = new HashMap<>();
    // 字段定义信息
    private Map<String, TableField> fieldContainer = new HashMap<>();

    public void joinSchemaDefinition(SchemaDefinition schemaDefinition) {
        List<String> targetColumns = schemaDefinition.getColumns();
        Map<String, ColumnName> targetColumnContainer = schemaDefinition.getColumnContainer();
        Map<String, TableField> targetFieldContainer = schemaDefinition.getFieldContainer();
        for (String targetColumn : targetColumns) {
            if (!columns.contains(targetColumn)) {
                ColumnName columnName = targetColumnContainer.get(targetColumn);
                TableField tableField = targetFieldContainer.get(targetColumn);
                if (null != columnName && null != tableField) {
                    columns.add(targetColumn);
                    columnContainer.put(targetColumn, columnName);
                    fieldContainer.put(targetColumn, tableField);
                }
            }
        }


    }

    public SchemaDefinition(Class<?> aClass) {
        this.tableNameAnnotation = aClass.getAnnotation(TableName.class);
        // 表名
        this.tableName = tableNameAnnotation.value().toLowerCase();

        // 收集字段定义信息
        Field[] fields = ZYReflectUtils.getFields(aClass);
        for (Field field : fields) {
            field.setAccessible(true);
            // 主键字段
            TableId tableId = field.getAnnotation(TableId.class);
            if (null != tableId) {
                this.tableId = tableId;
                continue;
            }

            // 普通字段
            TableField tableField = field.getAnnotation(TableField.class);
            if (null == tableField) {
                continue;
            }
            if (!tableField.exist()) {
                continue;
            }
            String column = tableField.value().toLowerCase();
            // 字段集合
            columns.add(column);
            // 表格定义
            fieldContainer.put(column, tableField);
            // 字段反射属性
            propContainer.put(column, field);
            // 建表描述
            ColumnName columnName = field.getAnnotation(ColumnName.class);
            if (null != columnName) {
                columnContainer.put(column, columnName);
            }
        }
    }
}
public class SchemaDefinitionLoader implements InterestedClassAware {

    private List<SchemaDefinition> schemaDefinitions = new ArrayList<>();

    public List<SchemaDefinition> getProjectSchemaDefinition() {
        return schemaDefinitions;
    }

    @Override
    public boolean match(AnnotationMetadata annotationMetadata) {
        return annotationMetadata.hasAnnotation(TableName.class.getName());
    }

    @Override
    public void setClasses(Set<Class<?>> classes) {
        List<SchemaDefinition> definitions = new ArrayList<>();

        Map<String, TableCondition> tableConditionCache = new HashMap<>();
        for (Class<?> aClass : classes) {
            TableExplain tableExplain = aClass.getAnnotation(TableExplain.class);
            if (isNecessary(tableConditionCache, tableExplain, aClass)) {
                SchemaDefinition schemaDefinition = new SchemaDefinition(aClass);
                if (null != tableExplain) {
                    // 表的注释
                    schemaDefinition.setTableComment(tableExplain.value());
                }

                definitions.add(schemaDefinition);
            }
        }

        Map<String, List<SchemaDefinition>> schemaContainer = ZYListUtils.groupList(definitions, SchemaDefinition::getTableName);
        schemaContainer.forEach((schemaName, schemas) -> {
            if (schemas.size() == 1) {
                schemaDefinitions.add(schemas.get(GlobalConstant.FIRST));
            } else if (schemas.size() > 1) {
                SchemaDefinition schemaDefinition = schemas.get(GlobalConstant.FIRST);
                // 合并集合
                for (int i = 1; i < schemas.size(); i++) {
                    schemaDefinition.joinSchemaDefinition(schemas.get(i));
                }
                schemaDefinitions.add(schemaDefinition);
            }
        });
    }

    private boolean isNecessary(Map<String, TableCondition> tableConditionCache, TableExplain tableExplain, Class<?> aClass) {
        if (null == tableExplain) {
            return true;
        }

        if (tableExplain.exclude()) {
            return false;
        }

        Class<? extends TableCondition> condition = tableExplain.condition();
        String name = condition.getName();
        TableCondition tableCondition = tableConditionCache.get(name);
        if (null == tableCondition) {
            tableCondition = ReflectUtils.newInstance(condition);
            tableConditionCache.put(name, tableCondition);
        }
        return tableCondition.isNecessary(aClass);

    }
}

public class SchemaExistedDefinitionLoader {

    @Autowired
    private DataSourceProperties dataSourceProperties;
    @Autowired
    private DataSource dataSource;

    @SneakyThrows
    public Map<String, TableInfo> findExistedTableInfo() {
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setDriverName(dataSourceProperties.getDriverClassName());
        dataSourceConfig.setPassword(dataSourceProperties.getPassword());
        dataSourceConfig.setUsername(dataSourceProperties.getUsername());
        String url = dataSourceProperties.getUrl();
        dataSourceConfig.setUrl(url);

        this.connection = dataSourceConfig.getConn();
        dataSourceConfig.setSchemaName(this.connection.getSchema());
        this.dataSourceConfig = dataSourceConfig;
        if (url.contains("kingbase8")) {
            this.dbQuery = new OracleQuery();
        } else {
            this.dbQuery = dataSourceConfig.getDbQuery();
        }

        this.strategyConfig = new StrategyConfig();
        this.globalConfig = new GlobalConfig();
        List<TableInfo> tablesInfo = getTablesInfo();
        return ZYListUtils.groupModel(tablesInfo, TableInfo::getName);
        // 表名全改成小写

    }


    private IDbQuery dbQuery;

    private DataSourceConfig dataSourceConfig;

    private Connection connection;

    private GlobalConfig globalConfig;

    private StrategyConfig strategyConfig;

    private List<TableInfo> getTablesInfo() {
        //所有的表信息
        List<TableInfo> tableList = new ArrayList<>();
        //不存在的表名
        PreparedStatement preparedStatement = null;
        try {
            String tablesSql = dbQuery.tablesSql();
            if (DbType.POSTGRE_SQL == dbQuery.dbType()) {
                String schema = dataSourceConfig.getSchemaName();
                if (schema == null) {
                    //pg默认schema=public
                    schema = "public";
                    dataSourceConfig.setSchemaName(schema);
                }
                tablesSql = String.format(tablesSql, schema);
            }
            //oracle数据库表太多,出现最大游标错误
            else if (DbType.ORACLE == dbQuery.dbType()) {
                String schema = dataSourceConfig.getSchemaName();
                //oracle默认用户的schema=username
                if (schema == null) {
                    schema = dataSourceConfig.getUsername().toUpperCase();
                    dataSourceConfig.setSchemaName(schema);
                }
                tablesSql = String.format(tablesSql, schema);
            }
            preparedStatement = connection.prepareStatement(tablesSql);
            ResultSet results = preparedStatement.executeQuery();
            TableInfo tableInfo;
            while (results.next()) {
                String tableName = results.getString(dbQuery.tableName());
                if (StringUtils.isNotEmpty(tableName)) {
                    String tableComment = results.getString(dbQuery.tableComment());
                    if ("VIEW".equalsIgnoreCase(tableComment)) {
                        // 跳过视图
                        continue;
                    }
                    tableInfo = new TableInfo();
                    tableInfo.setName(tableName.toLowerCase());
                    tableInfo.setComment(tableComment);
                    tableList.add(tableInfo);
                } else {
                    System.err.println("当前数据库为空!!!");
                }
            }
            tableList.forEach(ti -> convertTableFields(ti, strategyConfig.getColumnNaming()));
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            try {
                if (preparedStatement != null) {
                    preparedStatement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return tableList;
    }

    private TableInfo convertTableFields(TableInfo tableInfo, NamingStrategy strategy) {
        boolean haveId = false;
        List<TableField> fieldList = new ArrayList<>();
        try {
            String tableFieldsSql = dbQuery.tableFieldsSql();
            if (DbType.POSTGRE_SQL == dbQuery.dbType()) {
                tableFieldsSql = String.format(tableFieldsSql, dataSourceConfig.getSchemaName(), tableInfo.getName());
            } else if (DbType.ORACLE == dbQuery.dbType()) {
                tableFieldsSql = String.format(tableFieldsSql.replace("#schema", dataSourceConfig.getSchemaName()), tableInfo.getName());
            } else {
                tableFieldsSql = String.format(tableFieldsSql, tableInfo.getName());
            }
            PreparedStatement preparedStatement = connection.prepareStatement(tableFieldsSql);
            ResultSet results = preparedStatement.executeQuery();
            while (results.next()) {
                TableField field = new TableField();
                field.setName(results.getString(dbQuery.fieldName().toLowerCase()));
                field.setType(results.getString(dbQuery.fieldType()));
                field.setColumnType(dataSourceConfig.getTypeConvert().processTypeConvert(globalConfig, field.getType()));
                field.setComment(results.getString(dbQuery.fieldComment()));
                fieldList.add(field);
            }
        } catch (SQLException e) {
            System.err.println("SQL Exception:" + e.getMessage());
        }
        tableInfo.setFields(fieldList);
        return tableInfo;
    }
}
public class UpdateTableHandler {

    @Autowired
    private JDBCSupport jdbcSupport;

    public void updateTable(SchemaDefinition schemaDefinition, TableInfo tableInfo) {
        if (!FrameworkProperties.enableAutoAlterTableAddColumn && !FrameworkProperties.enableAutoAlterTableModifyColumn) {
            return;
        }

        List<String> columns = schemaDefinition.getColumns();
        List<TableField> commonFields = tableInfo.getFields();

        Map<String, TableField> existsColumnContainer = ZYListUtils.groupModel(commonFields, TableField::getName);
        // 列的定义
        Map<String, ColumnName> columnContainer = schemaDefinition.getColumnContainer();
        // 列的字段描述
        Map<String, Field> propContainer = schemaDefinition.getPropContainer();

        String tableName = tableInfo.getName();
        for (String column : columns) {
            // 列的定义注解
            ColumnName columnName = columnContainer.get(column);
            // 列属性的反射类型
            Field field = propContainer.get(column);
            // 决定jdbc现有的类型
            String jdbcType = jdbcSupport.decideJDBCType(column, field, columnName);
            if (!existsColumnContainer.containsKey(column)) {
                // 添加字段
                if (FrameworkProperties.enableAutoAlterTableAddColumn) {
                    Object defaultValue = null;
                    String comment = "";
                    if (null != columnName && null != field) {
                        boolean isVarchar = String.class.isAssignableFrom(field.getType());
                        defaultValue = isVarchar ? columnName.varcharDefaultValue() : columnName.intDefaultValue();
                        comment = columnName.value();
                    }
                    jdbcSupport.addColumn(tableName, column, jdbcType, defaultValue, comment);
                }
            } else {
                // 更新字段
                TableField existsTableField = existsColumnContainer.get(column);
                if (compareAndNecessaryModify(column, columnName, field, existsTableField)) {
                    if (FrameworkProperties.enableAutoAlterTableModifyColumn) {
                        jdbcSupport.modifyColumn(tableName, column, jdbcType);
                    }
                }
            }
        }

    }

    // 比对下新旧数据库,看字段是否需要modify
    private boolean compareAndNecessaryModify(String column, ColumnName columnName, Field field, TableField existsTableField) {
        // 主要是字段类型跟长度
        String type = existsTableField.getType();
        String jdbcType = jdbcSupport.decideJDBCType(column, field, columnName);
        return !type.equals(jdbcType);
    }
}

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

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

相关文章

【运维】DevOps全流程笔记(未完成)

运维笔记 DevOps基本流程Code阶段工具&#xff08;gitlab安装&#xff09;Build阶段工具&#xff08;Maven安装&#xff09;Integrate阶段工具JenkinsJenkins介绍Jenkins安装Jenkins入门配置 CI/CD操作集成Sonar Qube集成HarborJenkins流水线Kubernetes编排工具 DevOps全流程笔…

1400*C. Strong Password

Example input 5 88005553535123456 2 50 56 123412341234 3 111 444 1234 4 4321 4321 459 2 49 59 00010 2 10 11output YES NO YES NO YES解析&#xff1a; 题目要求有一种密码不在数据库中即可&#xff0c;所以枚举每一位的所有可能的数字&#xff0c;记录这一位数字在数…

C++-----list

本期我们来讲解list&#xff0c;有了string和vector的基础&#xff0c;我们学习起来会快很多 目录 list介绍 ​编辑 list常用接口 insert erase reverse sort merge unique remove splice 模拟实现 基础框架 构造函数 push_back 迭代器 常见问题 const迭代器 …

vue3 实现排序按钮

需求背景解决效果index.vue 需求背景 需要实现一个复用性&#xff0c;是提供表单顺倒排序的按钮 解决效果 index.vue <!--/*** author: liuk* date: 2023/7/25* describe: 排序按钮*/--> <template><div class"sort-fn"><span :class"[…

C++ 名字空间namespace

在C中支持三种域&#xff1a;局部域、名字空间域和类域。 名字空间域是随标准C而引入的。它相当于一个更加灵活的文件域&#xff08;全局域&#xff09;&#xff0c;可以用花括号把文件的一部分括起来&#xff0c;并以关键字namespace开头给它起一个名字&#xff1a; namespac…

STM32读写内部Flash

内存映射 stm32的flash起始地址为0x0800 0000&#xff0c;结束地址为0x0800 0000加上芯片实际的Flash大小&#xff0c;不同芯片Flash大小不同&#xff0c;RAM同理。 对于STM32F103RCT6&#xff0c;Flash256KB&#xff0c;所以结束地址为0x0803 ffff。 Flash中的内容一般用来存…

WEB:easyphp

背景知识 php弱类型比较 MD5碰撞 题目 进行代码审计 <?php highlight_file(__FILE__); $key1 0;//值赋值 $key2 0;$a $_GET[a];//get方法获取值 $b $_GET[b];if(isset($a) && intval($a) > 6000000 && strlen($a) < 3){ //a的值需要大于 60000…

zabbix钉钉报警

登录钉钉客户端,创建一个群,把需要收到报警信息的人员都拉到这个群内. 然后点击群右上角 的"群机器人"->"添加机器人"->"自定义", 记录该机器人的webhook值。 添加机器人 在钉钉群中&#xff0c;找到只能群助手 添加机器人 选择自定义机…

STM32 互补PWM 带死区 HAL

1、设置PWM波频率100KHz&#xff0c;占空比50%&#xff0c;死区时间1us 2、 while 循环之前启动PWM HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); //启动TIM1_CH1 PWM输出 HAL_TIMEx_PWMN_Start(&htim1,TIM_CHANNEL_1);//启动TIM1_CH1N PWM输出 3、死区计算 DT_time…

Linux三剑客与正则

目录 正则表达式 text测试文件 正则符号分类 基础正则符号 正则表达式的贪婪性 扩展正则符号 linux三剑客 三剑客特点及应用场景 grep sed sed命令执行过程 sed查找script sed删除script sed增加script 具体功能 具体script sed替换script 后向引用 awk aw…

SQLite Studio 连接 SQLite数据库

1、在SQLite中创建数据库和表 1.1、按WINR&#xff0c;打开控制台&#xff0c;然后把指引到我们的SQLite的安装路径&#xff0c;输入D:&#xff0c;切换到D盘&#xff0c;cd 地址&#xff0c;切换到具体文件夹&#xff0c;输入“sqlite3”&#xff0c;启动服务 1.2、创建数据库…

Sip IP网络对讲广播模块,sip网络寻呼话筒音频模块

Sip IP网络对讲广播模块&#xff0c;sip网络寻呼话筒音频模块 模块介绍 SV-2401VP和SV-2403VPIP网络对讲广播模块是一款通用的独立SIP音频功能模块&#xff0c;可以轻松地嵌入到OEM产品中。该模块对来自网络的SIP协议及RTP音频流进行编解码。 该模块支持多种网络协议和音频编…

vue 快速自定义分页el-pagination

vue 快速自定义分页el-pagination template <div style"text-align: center"><el-paginationbackground:current-page"pageObj.currentPage":page-size"pageObj.page":page-sizes"pageObj.pageSize"layout"total,prev,…

Nginx最佳实践优化(动静分离、资源压缩、负载均衡、黑白名单等等)

一、前言 Nginx是目前负载均衡技术中的主流方案&#xff0c;几乎绝大部分项目都会使用它&#xff0c;Nginx是一个轻量级的高性能HTTP反向代理服务器&#xff0c;同时它也是一个通用类型的代理服务器&#xff0c;支持绝大部分协议&#xff0c;如TCP、UDP、SMTP、HTTPS等。 二、…

“云上新气象”,VDI+IDV混合部署,麒麟信安云正式上线某市气象局!

阴晴冷暖&#xff0c;风云变幻&#xff0c;气象与人们的生活密切相关&#xff0c;气象局信息系统的智慧高效运营对于提升灾害防御能力、城市气象观测等方面具有重要作用&#xff0c;随着气象业务范围的不断扩展&#xff0c;气象局的信息化建设与数字化转型也亟需提上日程。 走…

【计算机网络 02】物理层基本概念 传输媒体 传输方式 编码与调制 信道极限容量 章节小结

第二章 -- 物理层 2.1 物理层基本概念2.2 物理层下的传输媒体2.3 传输方式2.4 编码与调制2.5 信道极限容量2.6 章节小结 2.1 物理层基本概念 2.2 物理层下的传输媒体 传输媒体也称为传输介质或传输媒介&#xff0c;他就是数据传输系统中在发送器和接收器之间的物理通路 传输媒…

微信小程序quickstartFunctions中云函数的应用

1、在quickstartFunctions文件中新建文件夹和文件 2、index.js 文件书写 const cloud require(wx-server-sdk);cloud.init({env: cloud.DYNAMIC_CURRENT_ENV }); const db cloud.database();// 链表查询试卷和对应的题库 exports.main async (event, context) > {retu…

矩阵置零(力扣)思维 JAVA

给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]] 输入&#xff1a;matrix [[0,1,2,0],[3,4,5,2],[…

架空线接地故障测试仪

一、凯迪正大架空线路接地故障定位仪产品概述 KDJK-10A只能在线路发生故障停运后进行故障定位&#xff0c;由发射机向故障线路施加高压将故障复现&#xff0c;超低频电流由发射机流向故障点&#xff0c;经过渡电阻进入大地并流回发射机&#xff1b;在线路沿线&#xff0c;将传…

算法的时间复杂度与空间复杂度

文章目录 1.算法效率 2.时间复杂度 3.空间复杂度 4.复杂度oj题目 文章内容 1.算法效率 1.1 如何衡量一个算法的好坏 一辆车的好坏我们可以从价格&#xff0c;油耗...... 方面来衡量&#xff0c;但衡量一个算法的好坏我们该从哪一个方面入手呢&#xff1f;比如斐波那契数…