首页 > 解决方案 > 如何在 liquibase OracleDatabase 中添加保留关键字?

问题描述

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

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

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

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

现在,我将使用QUOTE_ALL_OBJECTSliquibase 和hibernate.globally_quoted_identifiers.

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

标签: oraclespring-boothibernateliquibase

解决方案


嗯,在 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 在内部查询)。


推荐阅读