如何在 liquibase OracleDatabase 中添加保留关键字?

Posted

技术标签:

【中文标题】如何在 liquibase OracleDatabase 中添加保留关键字?【英文标题】:How to add reserved keywords in liquibase OracleDatabase? 【发布时间】:2021-11-27 06:52:45 【问题描述】:

试图使我的 Spring Boot JPA 应用程序与 Oracle DB 兼容,已经在 mysql 和 H2 上运行。 不幸的是,liquibase 生成的数据使用了一些 Oracle 的保留关键字作为表名或列名。

好消息是 hibernate 和 liquibase 实现可以检测这些关键字并在查询数据库时“引用”它们(使用 objectQuotingStrategy="QUOTE_ONLY_RESERVED_KEYWORDS" 用于 liquibase,spring.jpa.properties.hibernate.auto_quote_keyword: true 用于休眠)。 坏消息是 hibernate 和 liquibase 不共享相同的 Oracle 保留关键字列表。

例如,value 不被 liquibase 识别为保留关键字,但被 hibernate(使用 ANSI SQL:2003 关键字)识别。 我的一个 liquibase changeSets 创建了一个带有小写 value 列的表,因此 Liquibase 创建了一个带有不带引号的小写 value 列的表,Oracle DB 自动将其转换为大写案例 VALUE 列。现在当休眠尝试获取 该列,它识别 value 并引用它(`SELECT "value" from ...),这使其区分大小写,因此找不到该列 (ORA-00904)。

我想我通过扩展 SpringLiquibase 并添加我的自定义关键字找到了解决方法,如下所述:https://liquibase.jira.com/browse/CORE-3324。问题是这似乎不适用于 OracleDatabase 实现,它覆盖了 SpringLiquibase 的一组保留关键字(当然,isReservedWord() 方法使用 OracleDatabase 的一组)。

现在,我将对 liquibase 和 hibernate.globally_quoted_identifiers 使用 QUOTE_ALL_OBJECTS 引用策略。

但是,出于好奇,我想知道是否可以附加 liquibase 用于 Oracle 的保留关键字集。

spring boot 版本:2.3.9.RELEASE。 hibernate-core 版本(spring boot 依赖):5.4.28 liquibase-core 版本(spring boot 依赖):3.8.9

【问题讨论】:

【参考方案1】:

嗯,如果是 Oracle,您有关键字和保留字。

保留字不能用作标识符 关键字可以用作标识符,但不推荐使用。

您可以直接从数据库中获取它们的列表:

select KEYWORD, RESERVED from v$reserved_words;
...
1864 rows selected

在源代码中到处使用大写名称怎么样?

看起来 Liqubase 在功能上依赖于某些 JDBC 驱动程序 - 这不起作用。

OracleDatabase.java:

public void setConnection(DatabaseConnection conn) 
    //noinspection HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,HardCodedStringLiteral,
    // HardCodedStringLiteral
    reservedWords.addAll(Arrays.asList("GROUP", "USER", "SESSION", "PASSWORD", "RESOURCE", "START", "SIZE", "UID", "DESC", "ORDER")); //more reserved words not returned by driver

    Connection sqlConn = null;
    if (!(conn instanceof OfflineConnection)) 
        try 
            /*
             * Don't try to call getWrappedConnection if the conn instance is
             * is not a JdbcConnection. This happens for OfflineConnection.
             * see https://liquibase.jira.com/browse/CORE-2192
             */
            if (conn instanceof JdbcConnection) 
                sqlConn = ((JdbcConnection) conn).getWrappedConnection();
            
         catch (Exception e) 
            throw new UnexpectedLiquibaseException(e);
        

        if (sqlConn != null) 
            tryProxySession(conn.getURL(), sqlConn);

            try 
                //noinspection HardCodedStringLiteral
                reservedWords.addAll(Arrays.asList(sqlConn.getMetaData().getSQLKeywords().toUpperCase().split(",\\s*")));
             catch (SQLException e) 
                //noinspection HardCodedStringLiteral
                Scope.getCurrentScope().getLog(getClass()).info("Could get sql keywords on OracleDatabase: " + e.getMessage());
                //can not get keywords. Continue on
            

如果 Liquibase 调用 sqlConn.getMetaData().getSQLKeywords() 并且这没有返回正确的输出,那么你的机会是有限的。这可能是 JDBC 驱动程序中的错误,或者您的应用程序没有 SELECT_CATALOG_ROLE 权限并且看不到 v$reserved_words 视图(如果 JDBC 在内部查询)。

【讨论】:

感谢您的建议。我对 Oracle DB 还很陌生,所以我认为向我的数据源用户授予所有权限会授予他 SELECT_CATALOG_ROLE。好像没有,所以我加了。但不幸的是,保留关键字列表保持不变。 所以最后,我按照你最初的建议做了,到处都使用大写的名字。为此,我必须重命名所有使用关键字的表和列。但最终,这样更干净。谢谢。

以上是关于如何在 liquibase OracleDatabase 中添加保留关键字?的主要内容,如果未能解决你的问题,请参考以下文章

如何在liquibase中设置当月第一个日期和下个月的默认值?

如何修复未清除的 Liquibase 数据库锁

Liquibase:如何在 MySQL 数据库表上设置 Charset UTF-8?

如何使用 Liquibase 强制列顺序?

如何在 liquibase OracleDatabase 中添加保留关键字?

如何在 SpringBootApplication 中的 MyBatis 映射之前启动 Liquibase 迁移?