Установить имя таблицы в Spring JPA

Я думаю, что я пытаюсь сделать что-то действительно простой. Использование Spring Boot (1.3.3 .RELEASE) с JPA я хочу установить имя таблицы.

@Entity
@Table(name = "MyTable_name")
public class MyTableData {
  ...
}

то, что я ожидаю в своей базе данных, - это таблица с "MyTable_name". Мне кажется, это вполне разумно. Но этого не происходит. Я получаю таблицу с именем " MY_TABLE_NAME "(H2 backend) или" my_table_name " (Postgre backend). С этого момента я буду придерживаться Postgre, так как моя цель-прочитать существующую БД, где я не контролирую таблицу имена.

после некоторых исследований я нахожу сообщения, которые говорят, что я должен использовать весну.jpa.зимовать.naming-свойство стратегии. Это не очень помогает. Настройка наиболее часто рекомендуемой организации.зимовать.контекстно-свободная грамматика.ImprovedNamingStrategy производит то же самое поведение: "my_table_name". Установка для орг.зимовать.контекстно-свободная грамматика.EJB3NamingStrategy производит "mytable_name". Установка для орг.зимовать.контекстно-свободная грамматика.DefaultNamingStrategy вызывает ошибки контекста приложения во внутренностях Spring.

ушел в письменной форме мой собственный, я начал смотреть на орг.зимовать.контекстно-свободная грамматика.ImprovedNamingStrategy. Я обнаружил, что он использовал устаревшую организацию.зимовать.контекстно-свободная грамматика.NamingStrategy. Это предполагает использование NamingStrategyDelegator вместо этого. Я посмотрел на его Java docs но не уверен, как применять. Я нашел этот пост. Как бы я ни ценил это объяснение, то, что там пытаются сделать, гораздо сложнее того, что мне нужно, и я с трудом применил его.

мой вопрос в том, как я могу получить весну JPA просто использовать имя, которое я указываю? Есть ли новое свойство для использования NamingStrategyDelegator? Нужно ли мне писать собственную стратегию?

=========== обновление ==========================

Я думаю, что я приближаюсь к ответу. Я создал простое приложение Spring startup (отдельно от моего производственного проекта). Я использую H2 для бэкэнд-БД.

эта дискуссия on Hiberate 5 Имен очень полезная. С его помощью я понял, как установите стратегии именования в Hibernate 5 следующим образом (в приложении.свойства.)

hibernate.implicit_naming_strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

Я создал физическую стратегию именования, которая прошла через имя (например, org.зимовать.сапог.модель.называющий.PhysicalNamingStrategyStandardImpl делает) и распечатывает значения. Из этого я вижу, что имена таблиц-это то, что я хочу через физический уровень именования.

затем я устанавливаю спящий режим.show_sql=true для отображения generate SQL. В сгенерированном SQL имена также правильный.

Я изучаю имена таблиц с помощью DatabaseMetaData.

private void showTables() throws SQLException {
    DatabaseMetaData dbMetadata = getConnection().getMetaData();
    ResultSet result = dbMetadata.getTables(null, null, null, new String[] { "TABLE" });
    if (result != null) {
        boolean haveTable = false;
        while (result.next()) {
            haveTable = true;
            getLogger().info("Found table {}", result.getString("TABLE_NAME"));
        }
        if (!haveTable) {
            getLogger().info("No tables found");
        }

    }
}

Я все еще вижу имена таблиц во всех шапках, когда я использую вышеуказанный код. Это заставляет меня полагать, что DatabaseMetaData по какой-то причине показывает все шапки, но остальная часть кода использует правильные имена. [редактировать: этот вывод неверен. Я просто была сбита с толку всем остальным, что происходило. Позднее тестирование показывает DatabaseMetaData показывает имена таблиц с правильными случай.]

это еще не полный ответ, потому что в моем производственном коде все еще есть какая-то странность, которую мне нужно исследовать. Но это близко, и я хотел опубликовать обновление, чтобы потенциальные читатели не тратили время.

вот мой проход через стратегию физического именования в случае, если кто-то заинтересован. Я знаю, что это может помочь увидеть, что сделали другие, особенно при попытке найти классы и пакеты в весеннем лабиринте.

package my.domain.eric;

import java.io.Serializable;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NamingStrategyPhysicalLeaveAlone implements PhysicalNamingStrategy, Serializable {
    private static final long serialVersionUID = -5937286882099274612L;

    private static final Logger LOGGER = LoggerFactory.getLogger(NamingStrategyPhysicalLeaveAlone.class);

    protected Logger getLogger() {
        return LOGGER;
    }

    @Override
    public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment context) {
        String nameText = name == null ? "" : name.getText();
        getLogger().info("toPhysicalCatalogName name: {}", nameText);
        return name;
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment context) {
        String nameText = name == null ? "" : name.getText();
        getLogger().info("toPhysicalSchemaName name: {}", nameText);
        return name;
    }

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        String nameText = name == null ? "" : name.getText();
        getLogger().info("toPhysicalTableName name: {}", nameText);
        return name;
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment context) {
        String nameText = name == null ? "" : name.getText();
        getLogger().info("toPhysicalSequenceName name: {}", nameText);
        return name;
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        String nameText = name == null ? "" : name.getText();
        getLogger().info("toPhysicalColumnName name: {}", nameText);
        return name;
    }
}

2 ответов


ответ на мой вопрос заключается в следующем.

  1. SQL нечувствителен к регистру, но это не совсем так просто. Цитируемые имена взяты буквально. Имена без кавычек могут быть интерпретированы. Например, PostgreSQL преобразует имена без кавычек в нижний регистр, а H2 - в верхний регистр. Таким образом, select * from MyTable_name в PostgreSQL ищет таблицу mytable_name. В H2 тот же запрос ищет MYTABLE_NAME. В моем случае таблица PostgreSQL была создана с использованием процитировал название "MyTable_name" так выбрать * из MyTable_name не удается, в то время как Выберите * от "MyTable_name" успешно.
  2. Spring JPA / Hibernate передает непереведенные имена в SQL.
  3. весной JPA / Hibernate есть три метода, которые можно использовать для передачи цитируемых имен
    1. явно Укажите имя: @Table (name = " \ " MyTable_name\"")
    2. реализуйте физическую стратегию именования, которая цитирует имена (подробности ниже)
    3. установить атрибут незарегистрированным цитата все имена таблиц и столбцов: весна.jpa.свойства.зимовать.globally_quoted_identifiers=true (см. комментарий). Это последнее, что я сделал, потому что у меня также есть имена столбцов, для которых мне нужна чувствительность к регистру.

другим источником путаницы для меня было то, что многие сайты ссылаются на старую переменную именования hibernate.EJB-компонента.naming_strategy или это весенний эквивалент. Для Hibernate 5 это устарело. Вместо этого, как я упоминаю в моем вопросе обновления, Hibernate 5 имеет неявные и физические стратегии именования.

кроме того, я был смущен, потому что есть свойства hibernate, а затем есть свойства Spring. Я использовал это очень полезный учебник. Однако он показывает ненужное прямое использование свойств hibernate (как я перечисляю в своем обновлении), а затем явную конфигурацию LocalContainerEntityManagerFactorybean и JpaTransactionManager. Гораздо проще использовать свойства Spring и иметь их автоматически подобранный. Для меня важны стратегии именования.

  1. весна.jpa.зимовать.называющий.неявная стратегия
  2. весна.jpa.зимовать.называющий.физическая стратегия

для реализации стратегии физического именования необходимо создать класс, реализующий org.зимовать.сапог.модель.называющий.PhysicalNamingStrategy, как я показываю в своем обновлении выше. Цитирование имен на самом деле очень просто, потому что класс идентификатора, переданный методу, управляет цитированием или нет. Таким образом следующий метод будет цитировать имена таблиц.

@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
    if (name == null) {
        return null;
    }
    return Identifier.quote(name);
}

другие вещи, которые я узнал, которые могут быть полезны для тех, кто пришел сюда в поисках ответов.

  1. С использованием Spring.JPA properties автоматически выбирает диалект SQL. С direct hibernate у меня были ошибки SQL, когда я переключился на Postgre.
  2. хотя сбои контекста приложения Spring очень распространены, тщательное чтение ошибок часто указывает на решения.
  3. DatabaseMetaData сообщает имена таблиц правильно, я просто был сбит с толку всем остальным.
  4. set весна.jpa.шоу-среде SQL=true, чтобы увидеть сгенерированный SQL-код. Очень полезно для отладки. Позволил мне увидеть, что используются правильные имена таблиц
  5. весна.jpa.зимовать.ddl-auto поддерживает по крайней мере следующие значения. create-drop: создание таблиц при входе, падение при выходе. create: создание таблиц при входе, но выход при выходе. нет: не создавайте и не отбрасывайте. Я видел, что люди используют "обновление" в качестве значения, но это не удалось для меня. (Например здесь.) Вот это обсуждение вариантов.
  6. у меня были проблемы в H2 с использованием цитируемых имен столбцов, но я не исследовал дальше.
  7. страница свойств весны полезно, но описания очень редки.

имя указывается в аннотации сущности

@Entity(name = "MyTable_name")
public class MyTableData {
  ...
}