Как добавить зарезервированные ключевые слова в базу данных liquibase OracleDatabase?

#oracle #spring-boot #hibernate #liquibase

Вопрос:

Пытаюсь сделать мое приложение 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). Плохая новость заключается в том, что hibernate и liquibase не используют один и тот же список зарезервированных ключевых слов для Oracle.

Например, значение не распознается как зарезервированное ключевое слово liquibase, а определяется hibernate (который использует ключевые слова ANSI SQL:2003). Один из моих наборов изменений liquibase создает таблицу со столбцом значений в нижнем регистре, поэтому Liquibase создает таблицу со столбцом значений в нижнем регистре без кавычек, а Oracle DB автоматически переводит ее в столбец ЗНАЧЕНИЙ в верхнем регистре. Теперь, когда hibernate пытается извлечь этот столбец, он распознает значение и заключает его в кавычки («ВЫБЕРИТЕ» значение » из…), что делает его чувствительным к регистру, поэтому столбец не найден (ORA-00904).

Я думал, что нашел обходной путь для этого, расширив SpringLiquibase и добавив свои собственные ключевые слова, как описано здесь : https://liquibase.jira.com/browse/CORE-3324. Проблема в том, что это, похоже, не работает с реализацией OracleDatabase, которая перезаписывает набор зарезервированных ключевых слов SpringLiquibase (и, конечно, isReservedWord() метод использует набор OracleDatabase).

На данный момент я буду использовать QUOTE_ALL_OBJECTS стратегию цитирования для liquibase и hibernate.globally_quoted_identifiers .

Но просто из любопытства я хотел знать, можно ли добавить набор зарезервированных ключевых слов, используемых liquibase для Oracle.

  • версия весенней загрузки: 2.3.9.РЕЛИЗ.
  • версия hibernate-core (зависимость от загрузки весной): 5.4.28
  • версия ядра liquibase (зависимость от загрузки spring): 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 запрашивает это внутренне).

Комментарии:

1. Спасибо за предложение. Я довольно новичок в Oracle DB, поэтому я подумал, что предоставление всех привилегий моему пользователю источника данных предоставит ему роль SELECT_CATALOG_. Очевидно, нет, поэтому я добавил его. Но, к сожалению, список зарезервированных ключевых слов остается прежним.

2. Поэтому, в конце концов, я сделал то, что вы изначально предложили, везде используя имена в верхнем регистре. Для этого мне пришлось переименовать все таблицы и столбцы, в которых использовались ключевые слова. Но, в конце концов, так чище. Спасибо.