编程修炼之Hibernate--- springboot启动初始化ddl过程与如何自定义修改 table 字段长度

news2024/11/22 11:47:34

文章目录

  • springboot启动初始化ddl过程
  • 如何自定义修改 table

springboot启动初始化ddl过程

跟踪Springboot整合hibernate的启动代码:
在这里插入图片描述
SessionFactoryImpl 的初始化里做了非常多的事情,初始化各种资源,并调用 SchemaManagementToolCoordinator.process 先执行脚本任务再执行 Db任务:

		performScriptAction( actions.getScriptAction(), metadata, tool, serviceRegistry, executionOptions, configService );
		performDatabaseAction( actions.getDatabaseAction(), metadata, tool, serviceRegistry, executionOptions );

在这里插入图片描述
在这里就开始进行 spring.jpa.hibernate.ddl-auto 配置的项,进行处理了。
在这里插入图片描述
在 doMigration 时,就获取到 tables 的元数据:

public class GroupedSchemaMigratorImpl extends AbstractSchemaMigrator {
    public GroupedSchemaMigratorImpl(HibernateSchemaManagementTool tool, SchemaFilter schemaFilter) {
        super(tool, schemaFilter);
    }

    protected NameSpaceTablesInformation performTablesMigration(Metadata metadata, DatabaseInformation existingDatabase, ExecutionOptions options, Dialect dialect, Formatter formatter, Set<String> exportIdentifiers, boolean tryToCreateCatalogs, boolean tryToCreateSchemas, Set<Identifier> exportedCatalogs, Namespace namespace, GenerationTarget[] targets) {
        NameSpaceTablesInformation tablesInformation = new NameSpaceTablesInformation(metadata.getDatabase().getJdbcEnvironment().getIdentifierHelper());
        if (this.schemaFilter.includeNamespace(namespace)) {
            this.createSchemaAndCatalog(existingDatabase, options, dialect, formatter, tryToCreateCatalogs, tryToCreateSchemas, exportedCatalogs, namespace, targets);
            NameSpaceTablesInformation tables = existingDatabase.getTablesInformation(namespace);
            Iterator var14 = namespace.getTables().iterator();

            Table table;
            TableInformation tableInformation;
            while(var14.hasNext()) {
                table = (Table)var14.next();
                if (this.schemaFilter.includeTable(table) && table.isPhysicalTable()) {
                    this.checkExportIdentifier(table, exportIdentifiers);
                    tableInformation = tables.getTableInformation(table);
                    if (tableInformation == null) {
                        this.createTable(table, dialect, metadata, formatter, options, targets);
                    } else if (tableInformation != null && tableInformation.isPhysicalTable()) {
                        tablesInformation.addTableInformation(tableInformation);
                        this.migrateTable(table, tableInformation, dialect, metadata, formatter, options, targets);
                    }
                }
            }

            var14 = namespace.getTables().iterator();

            while(true) {
                do {
                    do {
                        do {
                            if (!var14.hasNext()) {
                                return tablesInformation;
                            }

                            table = (Table)var14.next();
                        } while(!this.schemaFilter.includeTable(table));
                    } while(!table.isPhysicalTable());

                    tableInformation = tablesInformation.getTableInformation(table);
                } while(tableInformation != null && (tableInformation == null || !tableInformation.isPhysicalTable()));

                this.applyIndexes(table, tableInformation, dialect, metadata, formatter, options, targets);
                this.applyUniqueKeys(table, tableInformation, dialect, metadata, formatter, options, targets);
            }
        } else {
            return tablesInformation;
        }
    }
}

如何自定义修改 table

再往下的过程,到 HibernateSchemaManagementTool 这个核心类:
在这里插入图片描述
ddl-auto 语义的实现,就在这里。
那要自定义修改 table ,最常见的需要就是 hibernate 只能增加,对于修改字段长度这一类的涉及不到。当然这种修改只限于开发技术交流,生产环境慎用。
回归正题,要想修改字段长度有这么几个关键数据:

  • table 的元数据
  • Entity 的元数据
    hibernate 本身的功能,如 Migrator 既然能识别到字段缺失,那肯定也能获取到这2个元数据。也就是2个参数:registry 和 metadata。
    了解了需要的数据,就有了思路:
public void migrateDB(DataSourceProperties dataSource, EntityManager entityManager, String ddlAuto, String dialectStr) {
        log.info("schema 生成:{}", dataSource.getUrl());
        Set<EntityType<?>> entities = entityManager.getMetamodel().getEntities();
        Properties p = new Properties();
        // 数据库方言,最终输出的方言
        p.put(AvailableSettings.DIALECT, dialectStr);
        // 自动执行的动作
        p.put(AvailableSettings.HBM2DDL_AUTO, ddlAuto);
        // 分隔符,默认为空
        p.put(AvailableSettings.HBM2DDL_DELIMITER, ";");
        // 是否展示SQL
        p.put(AvailableSettings.SHOW_SQL, true);
        p.put("hibernate.connection.driver_class", dataSource.getDriverClassName());
        p.put("hibernate.connection.url", dataSource.getUrl());
        p.put("hibernate.connection.username", dataSource.getUsername());
        p.put("hibernate.connection.password", dataSource.getPassword());
        // 是否使用默认的jdbc元数据,默认为true,读取项目自身的元数据
        p.put("hibernate.temp.use_jdbc_metadata_defaults", true);
//                p.put("hibernate.temp.use_jdbc_metadata_defaults", false);
        ConfigurationHelper.resolvePlaceHolders(p);
        try (ServiceRegistry registry = new StandardServiceRegistryBuilder().applySettings(p).build()) {
            Map settings = registry.getService(ConfigurationService.class).getSettings();
            MetadataSources metadataSources = new MetadataSources(registry);
            entities.forEach(entityType -> metadataSources.addAnnotatedClass(entityType.getJavaType()));
            Metadata metadata = metadataSources.buildMetadata();
            HashMap properties = new HashMap<>();
            properties.putAll(registry.getService(ConfigurationService.class).getSettings());
            log.info("开始migration");
            SchemaManagementToolCoordinator.process(metadata, registry, settings, null);
            log.info("开始alter migration");
            SchemaAlterTool schemaAlterTool = new SchemaAlterTool(registry);
            schemaAlterTool.performTablesAlter(registry, metadata);
        }
    }

    public void migrateDB(String jdbcUrl, DataSourceProperties dataSource, Set<EntityType<?>> entities, String ddlAuto) {
        log.info("schema 生成:{}", jdbcUrl);
        Properties p = new Properties();
        // 数据库方言,最终输出的方言
        p.put(AvailableSettings.DIALECT, MySQL5InnoDBDialect.class.getName());
        // 自动执行的动作
        p.put(AvailableSettings.HBM2DDL_AUTO, ddlAuto);
        // 分隔符,默认为空
        p.put(AvailableSettings.HBM2DDL_DELIMITER, ";");
        // 是否展示SQL
        p.put(AvailableSettings.SHOW_SQL, true);
        p.put("hibernate.connection.driver_class", dataSource.getDriverClassName());
        p.put("hibernate.connection.url", jdbcUrl);
        p.put("hibernate.connection.username", dataSource.getUsername());
        p.put("hibernate.connection.password", dataSource.getPassword());
        // 是否使用默认的jdbc元数据,默认为true,读取项目自身的元数据
        p.put("hibernate.temp.use_jdbc_metadata_defaults", true);
//                p.put("hibernate.temp.use_jdbc_metadata_defaults", false);
        ConfigurationHelper.resolvePlaceHolders(p);
        try (ServiceRegistry registry = new StandardServiceRegistryBuilder().applySettings(p).build()) {
            Map settings = registry.getService(ConfigurationService.class).getSettings();
            MetadataSources metadataSources = new MetadataSources(registry);
            entities.forEach(entityType -> metadataSources.addAnnotatedClass(entityType.getJavaType()));
            Metadata metadata = metadataSources.buildMetadata();
            HashMap properties = new HashMap<>();
            properties.putAll(registry.getService(ConfigurationService.class).getSettings());
            log.info("开始migration");
            SchemaManagementToolCoordinator.process(metadata, registry, settings, null);
            log.info("开始alter migration");
            SchemaAlterTool schemaAlterTool = new SchemaAlterTool(registry);
            schemaAlterTool.performTablesAlter(registry, metadata);
        }
    }

自定义 hibernate 的entity 关联 table:

public class SchemaAlterTool {
    private Logger log = LoggerFactory.getLogger(SchemaAlterTool.class);
    protected SchemaFilter schemaFilter;
    ServiceRegistry registry;
    private HibernateSchemaManagementTool tool;
    private JdbcContext jdbcContext;

    public SchemaAlterTool(ServiceRegistry registry) {
        this.registry = registry;
        SchemaFilterProvider schemaFilterProvider = registry.getService(StrategySelector.class).resolveDefaultableStrategy(
                SchemaFilterProvider.class,
                null,
                DefaultSchemaFilterProvider.INSTANCE
        );
        this.schemaFilter = schemaFilterProvider.getMigrateFilter();
    }


    protected void validateColumnType(Table table, Column column, ColumnInformation columnInformation, Metadata metadata, Dialect dialect) {
        boolean typesMatch = column.getSqlTypeCode(metadata) == columnInformation.getTypeCode() || column.getSqlType(dialect, metadata).toLowerCase(Locale.ROOT).startsWith(columnInformation.getTypeName().toLowerCase(Locale.ROOT));
        if (!typesMatch) {
            throw new SchemaManagementException(String.format("Schema-alter: wrong column type encountered in column [%s] in table [%s]; found [%s (Types#%s)], but expecting [%s (Types#%s)]", column.getName(), table.getQualifiedTableName(), columnInformation.getTypeName().toLowerCase(Locale.ROOT), JdbcTypeNameMapper.getTypeName(columnInformation.getTypeCode()), column.getSqlType().toLowerCase(Locale.ROOT), JdbcTypeNameMapper.getTypeName(column.getSqlTypeCode(metadata))));
        }
    }

    /**
     * @param column            模型信息
     * @param columnInformation 数据库信息
     * @return
     */
    protected boolean validateColumnLength(Column column, ColumnInformation columnInformation, Metadata metadata, Dialect dialect) {
        Value value = column.getValue();
        String name = value.getType().getName();
        if (!"string".equals(name)) {
            return true;
        }
//        255 默认值忽略
        if (column.getLength() == 255) {
            return true;
        }
//        boolean typesMatch = column.getSqlTypeCode(metadata) == columnInformation.getTypeCode() || column.getSqlType(dialect, metadata).toLowerCase(Locale.ROOT).startsWith(columnInformation.getTypeName().toLowerCase(Locale.ROOT));
//        if (!typesMatch) {
//            return true;
//        }
        if (column.getSqlType(dialect, metadata).toLowerCase(Locale.ROOT).startsWith("clob")) {
            return true;
        }
//        columnInformation.getTypeName()
//        有大佬定义 columnDefination
        String sqlType = column.getSqlType();
        if (!StringUtils.isEmpty(sqlType) && sqlType.length() > 24) {
            return true;
        }
        if (!(column.getLength() == columnInformation.getColumnSize())) {
            log.info("column {} not match length {}", column.getName(), column.getLength());
            return false;
        }
        return true;
    }


    public NameSpaceTablesInformation performTablesAlter(ServiceRegistry registry, Metadata metadata) {

        tool = (HibernateSchemaManagementTool) registry.getService(SchemaManagementTool.class);

        Map configurationValues = registry.getService(ConfigurationService.class).getSettings();

        ExecutionOptions options = new ExecutionOptions() {
            @Override
            public boolean shouldManageNamespaces() {
                return true;
            }

            @Override
            public Map getConfigurationValues() {
                return configurationValues;
            }

            @Override
            public ExceptionHandler getExceptionHandler() {
                return ExceptionHandlerLoggedImpl.INSTANCE;
            }
        };

        NameSpaceTablesInformation tablesInformation = new NameSpaceTablesInformation(metadata.getDatabase().getJdbcEnvironment().getIdentifierHelper());
        jdbcContext = tool.resolveJdbcContext(options.getConfigurationValues());

        final DdlTransactionIsolator isolator = tool.getDdlTransactionIsolator(jdbcContext);

        final DatabaseInformation databaseInformation = Helper.buildDatabaseInformation(
                tool.getServiceRegistry(),
                isolator,
                metadata.getDatabase().getDefaultNamespace().getName()
        );

        try {
            for (Namespace namespace : metadata.getDatabase().getNamespaces()) {
                if (schemaFilter.includeNamespace(namespace)) {
                    alterTables(metadata, databaseInformation, options, jdbcContext.getDialect(), namespace);
                }
            }
        } finally {
            try {
                databaseInformation.cleanup();
            } catch (Exception e) {
                log.debug("Problem releasing DatabaseInformation : " + e.getMessage());
            }

            isolator.release();
        }

        return tablesInformation;
    }

    protected void alterTables(Metadata metadata, DatabaseInformation databaseInformation, ExecutionOptions options, Dialect dialect, Namespace namespace) {
        NameSpaceTablesInformation tables = databaseInformation.getTablesInformation(namespace);
        Iterator tableIter = namespace.getTables().iterator();

        while (tableIter.hasNext()) {
            Table table = (Table) tableIter.next();
            if (this.schemaFilter.includeTable(table) && table.isPhysicalTable()) {
                this.alterTable(table, tables.getTableInformation(table), metadata, options, dialect);
            }
        }

    }

    protected void alterTable(Table table, TableInformation tableInformation, Metadata metadata, ExecutionOptions options, Dialect dialect) {
        if (tableInformation == null) {
            throw new SchemaManagementException(String.format("Schema-alter: missing table [%s]", table.getQualifiedTableName().toString()));
        } else {
            Iterator selectableItr = table.getColumnIterator();

            while (selectableItr.hasNext()) {
                Selectable selectable = (Selectable) selectableItr.next();
                if (Column.class.isInstance(selectable)) {
                    Column column = (Column) selectable;
                    ColumnInformation existingColumn = tableInformation.getColumn(Identifier.toIdentifier(column.getQuotedName()));
                    if (existingColumn == null) {
                        throw new SchemaManagementException(String.format("Schema-alter: missing column [%s] in table [%s]", column.getName(), table.getQualifiedTableName()));
                    }
//                    this.validateColumnType(table, column, existingColumn, metadata, dialect);
                    if (!validateColumnLength(column, existingColumn, metadata, dialect)) {
                        log.info("table alter:{}", table.getQualifiedTableName());
                        boolean format = Helper.interpretFormattingEnabled(options.getConfigurationValues());
                        Formatter formatter = format ? FormatStyle.DDL.getFormatter() : FormatStyle.NONE.getFormatter();
                        final GenerationTarget[] targets = new GenerationTarget[]{new GenerationTargetToDatabase(tool.getDdlTransactionIsolator(jdbcContext), true)};

                        migrateTable(table, column, tableInformation, dialect, metadata, formatter, options, targets);
                    }
                }
            }

        }
    }

    protected void migrateTable(Table table, Column column, TableInformation tableInformation, Dialect dialect, Metadata metadata, Formatter formatter, ExecutionOptions options, GenerationTarget... targets) {
        Database database = metadata.getDatabase();
        AlterTable alterTable = new AlterTable(table);
        applySqlStrings(false, alterTable.sqlAlterStrings(dialect, metadata, tableInformation,
                        column,
                        database.getDefaultNamespace().getPhysicalName().getCatalog(),
                        database.getDefaultNamespace().getPhysicalName().getSchema()),
                formatter,
                options,
                targets);
        for (int i = 0; i < targets.length; i++) {
            targets[i].release();
        }

    }

    void applySqlStrings(boolean quiet, Iterator sqlStrings, Formatter formatter, ExecutionOptions options, GenerationTarget... targets) {
        if (sqlStrings != null) {
            while (sqlStrings.hasNext()) {
                String sqlString = (String) sqlStrings.next();
                applySqlString(quiet, sqlString, formatter, options, targets);
            }
        }
    }

    private static void applySqlString(boolean quiet, String sqlString, Formatter formatter, ExecutionOptions options, GenerationTarget... targets) {
        if (!StringHelper.isEmpty(sqlString)) {
            String sqlStringFormatted = formatter.format(sqlString);
            GenerationTarget[] exeProcessor = targets;
            int excCount = targets.length;

            for (int i = 0; i < excCount; ++i) {
                GenerationTarget target = exeProcessor[i];

                try {
                    target.accept(sqlStringFormatted);
                } catch (CommandAcceptanceException var11) {
                    if (!quiet) {
                        options.getExceptionHandler().handleException(var11);
                    }
                }
            }
        }

    }
}

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

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

相关文章

c语言基础--------字符串指针

在 C 语言中&#xff0c;字符串指针是一个指向字符类型的指针&#xff0c;通常用于指向字符串的第一个字符。字符串在 C 语言中通常表示为字符数组&#xff0c;而字符串指针则是用来存储这种字符数组首地址的变量。 定义字符串指针 字符串指针的定义方式如下&#xff1a; ch…

Android更改包名和签名

一、更改包名 1、包名——鼠标右键——Refactor——Rename 修改自己想更改的包名和选择更改范围后点击Refactor就可以了 2.手动修改app的build.gradle文件中的applicationId&#xff08;改成和我们之前修改的包名相同&#xff09; 3.修改AndroidManifest.xml文件中的packag…

“AI+Security”系列第2期(三):面向LLM(大语言模型)的漏洞挖掘与对齐防御研究

近日&#xff0c;由安全极客、Wisemodel 社区和 InForSec 网络安全研究国际学术论坛联合主办的“AISecurity”系列第 2 期——对抗&#xff01;大模型自身安全的攻防博弈线上活动如期举行。 在此次活动中&#xff0c;前阿里云高级安全专家郑瀚带来了以《通往LLM安全对齐的道路…

网易云音乐故障 2 小时,这次到底谁背锅?(今天记得领补偿)

大家好&#xff0c;我是程序员鱼皮&#xff0c;8 月 19 日下午&#xff0c;网易云音乐突发严重故障&#xff0c;并登顶微博热搜&#xff0c;跟黑神话悟空抢了热度。 根据用户的反馈&#xff0c;故障的具体表现为&#xff1a;用户无法登录、歌单加载失败、播放信息获取失败、无法…

SQL— DML语句学习【后端 10】

数据库操作-DML 详解 在数据库管理系统中&#xff0c;DML&#xff08;Data Manipulation Language&#xff0c;数据操作语言&#xff09;扮演着至关重要的角色&#xff0c;它负责对数据库中的数据进行增、删、改操作。掌握DML操作对于数据库的日常维护和管理至关重要。本文将详…

CSP 2023 普及组第一轮 - CSP/S 2023初试题 完善程序第二题解析

一、题目阅读 &#xff08;编辑距离&#xff09;给定两个字符串&#xff0c;每次操作可以选择删除&#xff08;Delete&#xff09;、插入&#xff08;Insert&#xff09;、替换&#xff08;Replace&#xff09;&#xff0c;一个字符&#xff0c;求将第一个字符串转换为第二个字…

时序预测|基于贝叶斯BO-卷积-双向门控单元-注意力机制的单变量时间序列预测模型BO-CNN-BiGRU-Attention

时序预测|基于贝叶斯BO-卷积-双向门控单元-注意力机制的单变量时间序列预测模型BO-CNN-BiGRU-Attention 文章目录 前言时序预测|基于贝叶斯BO-卷积-双向门控单元-注意力机制的单变量时间序列预测模型BO-CNN-BiGRU-Attention 一、BO-CNN-BiGRU-Attention模型1. 贝叶斯优化&#…

【C++ 第十二章】二叉搜索树

1.1 二叉搜索树概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 左边小&#xff1a;若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值右边大&#xff1a;若它的右子树不为空&#xff0c;则右子树上…

并网式光伏气象站——科技百科

并网式光伏气象站的工作原理简洁而充满智慧&#xff0c;并网式光伏气象站巧妙地通过太阳能电池板将太阳能转化为电能&#xff0c;利用先进的气象监测设备&#xff0c;‌对风速、‌风向、‌温度、‌湿度、‌光照等关键气象要素进行实时监测和记录&#xff0c;不仅充分利用了太阳…

【初阶数据结构题目】32. 希尔排序

文章目录 希尔排序希尔排序的时间复杂度计算 希尔排序 希尔排序法又称缩小增量法。希尔排序法的基本思想是&#xff1a;先选定一个整数&#xff08;通常是gap n/31&#xff09;&#xff0c;把待排序文件所有记录分成各组&#xff0c;所有的距离相等的记录分在同一组内&#x…

全国10米分辨率逐年植被覆盖度(FVC)数据集

本数据集包括2017至2023年间&#xff0c;全国植被覆盖度数据&#xff0c;FVC范围值为0-1&#xff0c;数据为浮点型&#xff0c;GeoTIFF格式。GeoTIFF文件均可用ArcGIS软件和GDAL读取和打开。 植被覆盖度是指植被&#xff08;包括叶、茎、枝&#xff09;在地面的垂直投影面积占统…

系统编程-进程初步2

进程初步2 目录 进程初步2 1、进程等待清理函数&#xff08;wait&#xff09; 2、等待指定的子进程&#xff08;waitpid&#xff09; 3、新的开辟进程的函数&#xff08;vfork&#xff09; 4、在程序中运行系统下的指令&#xff08;system&#xff09; 5、exec 函数族 …

初识指针4の学习笔记

目录 1>>前言 2>>字符指针变量 3>>数组指针变量 4>>函数指针变量 5>>函数指针数组 6>>回调函数是什么&#xff1f; 7>>结语 1>>前言 今天我会继续分享一些我做的笔记&#xff0c;以及我对指针的理解&#xff0c; 后续会…

查看会议所属CCF级别(A/B/C类会议)

步骤&#xff1a; 1、打开中国计算机学会官网&#xff1a;中国计算机学会 (ccf.org.cn)。 2、搜索框中输入会议名称&#xff0c;例如&#xff1a;SIGKDD。 3、点击打开如图所示来源是“学术评价”的网页。 4、进入如下页面。 可以看到&#xff0c;SIGKDD是CCF A类会议。 参考…

低代码: 关于Test Driven Development - 测试驱动开发组件与测试示例

TDD 的开发方式 Test Driven Development - 测试驱动开发这是一种非常有意思的开发方式,我们进入一个实际场景,拿需要自研的colorpicker表单组件来说表单中的很多属性,都需要进行一个颜色的选择,如背景颜色,字体颜色等等我们来看一下相关设计交互在这里我们分两个图,点击…

python的导入包飘红

本文是mac的解决方法 1、点击pycharm-preferences-paython interpreter 直接搜索你要导入的包名&#xff0c;选择版本号点击 install 2、第二种方法是命令 pip3 install pandas2.2.2

学习文件IO,让你从操作系统内核的角度去理解输入和输出(理论篇)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

day06——前后端交互

一、计算属性 计算属性就是基于现有的数据推算出来的新属性&#xff0c;只要依赖的数据变化&#xff0c;新属性就会自动更新&#xff0c;而且计算属性多次调用的情况下只会计算一次&#xff0c;效率非常高 简化写法 const app new Vue({ el: #app, data: {}, methods: {//跟da…

易境通海运系统:打造海运专线稳定合作的强力引擎

对于海运专线而言&#xff0c;稳定的合作伙伴至关重要。在激烈的市场竞争中&#xff0c;如何持续保持客户的高满意度&#xff0c;不断强化自身优势&#xff0c;并提升用户粘性&#xff0c;是众多海运专线必须面对并解决的问题。而拥有一套可靠的系统&#xff0c;提供完备的服务…

会“坐”电梯,能避障碍,AGV无人搬运车进入各行各业

AGV 近年来&#xff0c;自动导引车&#xff08;Automated Guided Vehicle&#xff0c;简称AGV&#xff09;作为一种先进的物流设备&#xff0c;在制造业中广泛应用。AGV是一种能够自主行驶的无人驾驶车辆&#xff0c;通过内置的导航系统和传感器&#xff0c;实现对环境的感知与…