ImprovedNamingStrategy больше не работает в Hibernate 5

У меня есть простая конфигурация Spring-JPA, где я настроил Hibernate ImprovedNamingStrategy, Это означает, что если мой класс сущности имеет переменную userNameто Hibernate должен преобразовать его в user_name для запроса базы данных. Но это преобразование имен перестало работать после обновления до Hibernate 5. Я получаю сообщение об ошибке:

ОШИБКА: неизвестный столбец 'user0_.userName' в 'списке полей'

Это мой конфиг Hibernate:

@Configuration
@EnableJpaRepositories("com.springJpa.repository")
@EnableTransactionManagement
public class DataConfig {

    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/test");
        ds.setUsername("root");
        ds.setPassword("admin");
        return ds;
    }


    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(){ 

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setShowSql(Boolean.TRUE);
        vendorAdapter.setDatabase(Database.MYSQL);

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        factory.setDataSource(dataSource());
        factory.setPackagesToScan("com.springJpa.entity");


        Properties jpaProperties = new Properties();

        jpaProperties.put("hibernate.ejb.naming_strategy","org.hibernate.cfg.ImprovedNamingStrategy");
        jpaProperties.put("hibernate.dialect","org.hibernate.dialect.MySQL5InnoDBDialect");

        factory.setJpaProperties(jpaProperties);
        factory.afterPropertiesSet();
        return factory;
    }

    @Bean
    public SharedEntityManagerBean entityManager() {
        SharedEntityManagerBean entityManager = new SharedEntityManagerBean();
        entityManager.setEntityManagerFactory(entityManagerFactory().getObject());
        return entityManager;
    }



    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory().getObject());
        return txManager;
    }

    @Bean
    public ImprovedNamingStrategy namingStrategy(){
        return new ImprovedNamingStrategy();
    }
}

Это мой класс Entity:

@Getter
@Setter
@Entity
@Table(name="user")
public class User{

    @Id
    @GeneratedValue
    private Long id;

    private String userName;
    private String email;
    private String password;
    private String role;

}

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

Пожалуйста, руководство.

8 ответов

Решение

Только что выяснили проблему, конфигурация абсолютно подходит при использовании версии Hibernate < 5.0, но не для Hibernate >= 5.0.

Я использовал Hibernate 5.0.0.Final с Spring 4.2.0.RELEASE. Я думаю, что Hibernate 5 не полностью совместим с Spring 4.2. Я только что понизил Hibernate до 4.2.1. Наконец-то все заработало нормально.

в Hibernate NamingStrategy класс устарел в Hibernate 5.

Спасибо за размещение вашего собственного решения. Мне очень помогает установить стратегию именования Hibernate 5!

hibernate.ejb.naming_strategy свойство pre-Hibernate 5.0 кажется разделенным на две части:

  • hibernate.physical_naming_strategy
  • hibernate.implicit_naming_strategy

Значения этих свойств не реализуют NamingStrategy интерфейс как сделал hibernate.ejb.naming_strategy, Для этих целей есть два новых интерфейса:

  • org.hibernate.boot.model.naming.PhysicalNamingStrategy
  • org.hibernate.boot.model.naming.ImplicitNamingStrategy

Hibernate 5 обеспечивает только одну реализацию PhysicalNamingStrategy (PhysicalNamingStrategyStandardImpl), который предполагает, что физические имена идентификаторов совпадают с логическими.

Есть несколько реализаций ImplicitNamingStrategy но я не нашел ничего эквивалентного старому ImprovedNamingStrategy, (Увидеть: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl)

Итак, я реализовал свой собственный PhysicalNamingStrategy что очень просто:

public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl implements Serializable {

 public static final PhysicalNamingStrategyImpl INSTANCE = new PhysicalNamingStrategyImpl();

 @Override
 public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
     return new Identifier(addUnderscores(name.getText()), name.isQuoted());
 }

 @Override
 public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
     return new Identifier(addUnderscores(name.getText()), name.isQuoted());
 }


 protected static String addUnderscores(String name) {
     final StringBuilder buf = new StringBuilder( name.replace('.', '_') );
     for (int i=1; i<buf.length()-1; i++) {
        if (
             Character.isLowerCase( buf.charAt(i-1) ) &&
             Character.isUpperCase( buf.charAt(i) ) &&
             Character.isLowerCase( buf.charAt(i+1) )
         ) {
             buf.insert(i++, '_');
         }
     }
     return buf.toString().toLowerCase(Locale.ROOT);
 }
}

Обратите внимание, что addUnderscores() метод из оригинала org.hibernate.cfg.ImprovedNamingStrategy,

Затем я установил эту физическую стратегию в файл persistence.xml:

  <property name="hibernate.physical_naming_strategy" value="my.package.PhysicalNamingStrategyImpl" />

Это ловушка для установки стратегии именования Hibernate 5 в качестве настроек предыдущей версии.

Вместо

      jpaProperties.put("hibernate.ejb.naming_strategy",
                  "org.hibernate.cfg.ImprovedNamingStrategy");

переход на новую стратегию физического именования и новую реализацию CamelCaseToUnderscoresNamingStrategy, который ведет себя как старый ImprovedNamingStrategy:

      jpaProperties.put("hibernate.physical_naming_strategy",
                  "org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy");

Доступно в Hibernate 5.6.1.FINAL

Спасибо и +1 Самуэлю Андресу за очень полезный ответ, однако, вероятно, будет хорошей идеей избегать написанной от руки логики с изображением змеиной оболочки. Вот то же решение, использующее гуаву.

Предполагается, что имена вашей сущности записаны в StandardJavaClassFormat и имена столбцов в standardJavaFieldFormat

Надеюсь, это спасет некоторых людей, приходящих сюда в будущем, погуглить:-)

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import static com.google.common.base.CaseFormat.*;

public class SnakeCaseNamingStrategy extends PhysicalNamingStrategyStandardImpl {

  public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
    return new Identifier(
      UPPER_CAMEL.to(LOWER_UNDERSCORE, name.getText()),
      name.isQuoted()
    );
  }

  public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
    return new Identifier(
      LOWER_CAMEL.to(LOWER_UNDERSCORE, name.getText()),
      name.isQuoted()
    );
  }
}

Спасибо за этот пост. Немного раздражает, что обновление нарушает стратегию имен таблиц и столбцов. Вместо того, чтобы копировать логику из ImprovedNamingStrategy Вы также можете использовать делегирование.

public class TableNamingStrategy extends PhysicalNamingStrategyStandardImpl {
    private static final String TABLE_PREFIX = "APP_";
    private static final long serialVersionUID = 1L;
    private static final ImprovedNamingStrategy STRATEGY_INSTANCE = new ImprovedNamingStrategy();

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return new Identifier(classToTableName(name.getText()), name.isQuoted());
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        return new Identifier(STRATEGY_INSTANCE.classToTableName(name.getText()), name.isQuoted());
    }

    private String classToTableName(String className) {
        return STRATEGY_INSTANCE.classToTableName(TABLE_PREFIX + className);
    }
}

Каждый ответ публикует решение путем реализации PhysicalNamingStrategy, но все, что вам нужно (и нужно сделать), это реализовать ImplicitNamingStrategy,

Когда сущность не называет явно таблицу базы данных, с которой она сопоставляется, нам необходимо неявно определить это имя таблицы. Или когда конкретный атрибут не указывает явно столбец базы данных, в который он отображается, нам нужно неявно определить имя этого столбца. Существуют примеры роли контракта org.hibernate.boot.model.naming.ImplicitNamingStrategy для определения логического имени, когда сопоставление не предоставило явного имени.

И код может быть так просто, как это (используя оригинальный addUnderscores как и в других ответах):

public class ImplicitNamingStrategyImpl extends ImplicitNamingStrategyJpaCompliantImpl {

    @Override
    protected Identifier toIdentifier(String stringForm, MetadataBuildingContext buildingContext) {
        return super.toIdentifier(addUnderscores(stringForm), buildingContext);
    }

    protected static String addUnderscores(String name) {
        final StringBuilder buf = new StringBuilder(name.replace('.', '_'));
        for (int i = 1; i < buf.length() - 1; i++) {
            if (Character.isLowerCase(buf.charAt(i - 1))
                    && Character.isUpperCase(buf.charAt(i))
                    && Character.isLowerCase(buf.charAt(i + 1))) {
                buf.insert(i++, '_');
            }
        }
        return buf.toString().toLowerCase(Locale.ROOT);
    }
}

Надеюсь это поможет:

hibernate.implicit_naming_strategy =.... ImplicitNamingStrategy hibernate.physical_naming_strategy =.... PhysicalNamingStrategyImpl

и вот код (только что переназначенный из существующего кода):

import java.io.Serializable;
import java.util.Locale;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl implements Serializable {

    public static final PhysicalNamingStrategyImpl INSTANCE = new PhysicalNamingStrategyImpl();

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return new Identifier(addUnderscores(name.getText()), name.isQuoted());
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        return new Identifier(addUnderscores(name.getText()), name.isQuoted());
    }

    protected static String addUnderscores(String name) {
        final StringBuilder buf = new StringBuilder( name.replace('.', '_') );
        for (int i=1; i<buf.length()-1; i++) {
            if (
                Character.isLowerCase( buf.charAt(i-1) ) &&
                Character.isUpperCase( buf.charAt(i) ) &&
                Character.isLowerCase( buf.charAt(i+1) )
            ) {
                buf.insert(i++, '_');
            }
        }
        return buf.toString().toLowerCase(Locale.ROOT);
    }

}

Нет утилит Guava и Apache

public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl {

    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return context.getIdentifierHelper().toIdentifier(
                name.getText().replaceAll("((?!^)[^_])([A-Z])", "$1_$2").toLowerCase(),
                name.isQuoted()
        );
    }

    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        return context.getIdentifierHelper().toIdentifier(
                name.getText().replaceAll("((?!^)[^_])([A-Z])", "$1_$2").toLowerCase(),
                name.isQuoted()
        );
    }
}
Другие вопросы по тегам