#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).
Но просто из любопытства я хотел знать, можно ли добавить набор зарезервированных ключевых слов, используемых liquibase для Oracle.
- версия весенней загрузки: 2.3.9.РЕЛИЗ.
- версия hibernate-core (зависимость от загрузки весной): 5.4.28
- версия ядра liquibase (зависимость от загрузки spring): 3.8.9
Ответ №1:
- зарезервированные слова не могут использоваться в качестве идентификаторов
- ключевые слова можно использовать в качестве идентификаторов, но это не рекомендуется.
Вы можете получить их список непосредственно из базы данных:
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. Поэтому, в конце концов, я сделал то, что вы изначально предложили, везде используя имена в верхнем регистре. Для этого мне пришлось переименовать все таблицы и столбцы, в которых использовались ключевые слова. Но, в конце концов, так чище. Спасибо.