Spring - конфигурация стратегии именования hibernate 5

Я пишу приложение, используя базу данных postgresql и spring + hibernate frameworks.

Я обновил фреймворк spring с версии 4.1.5.RELEASE до версии 4.2.0.RELEASE и обновил фреймворк hibernate с версии 4.3.7.Final до 5.0.0.Final.

После обновления у меня проблемы с NamingStrategy. В базе данных postgresql имена столбцов таблицы находятся в нижнем регистре, разделенных символом подчеркивания, в прикладном уровне свойства bean находятся в camelcase.

Это рабочий файл конфигурации spring для более старой версии:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="fms" />

    <bean id="microFmsDataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="***" />
        <property name="username" value="***" />
        <property name="password" value="***" />

        <property name="testOnBorrow" value="true" />
        <property name="testOnReturn" value="true" />
        <property name="testWhileIdle" value="true" />
        <property name="validationQuery" value="select 1" />
        <property name="initialSize" value="5" />
        <property name="minIdle" value="10" />
        <property name="maxIdle" value="100" />
        <property name="maxActive" value="100" />
        <property name="removeAbandoned" value="true" />
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="microFmsDataSource"/>

        <property name="packagesToScan">
            <list>
                <value>fms</value>
            </list>
        </property>

        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>

        <property name="jpaPropertyMap">
            <map>
                <entry key="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider" />
                <entry key="hibernate.hbm2ddl.auto" value="validate" />
                <entry key="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
                <entry key="hibernate.show_sql" value="true" />
                <entry key="hibernate.format_sql" value="true" />
                <entry key="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />
            </map>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

После обновления я изменил конфигурацию NamingStrategy:

<entry key="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />

вот так:

<entry key="hibernate.implicit_naming_strategy" value="***" />

и попробовал все варианты опций, перечисленных в hibernate javadoc: https://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/cfg/AvailableSettings.html#IMPLICIT_NAMING_STRATEGY

но без успеха.

Можете ли вы рассказать мне, что является альтернативой ImprovedNamingStrategy в Hibernate 5 и предоставить пример рабочей конфигурации?

Ответы

Ответ 1

Я думаю, что нашел решение.

Для достижения моей цели я использовал hibernate.physical_naming_strategy конфигурацию вместо hibernate.implicit_naming_strategy.

Я создал реализацию интерфейса PhysicalNamingStrategy, который имитирует часть функциональности исходного класса ImprovedNamingStrategy:

package fms.util.hibernate;

import org.apache.commons.lang.StringUtils;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

public class ImprovedNamingStrategy implements PhysicalNamingStrategy {

    @Override
    public Identifier toPhysicalCatalogName(Identifier identifier, JdbcEnvironment jdbcEnv) {
        return convert(identifier);
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier identifier, JdbcEnvironment jdbcEnv) {
        return convert(identifier);
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier identifier, JdbcEnvironment jdbcEnv) {
        return convert(identifier);
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier identifier, JdbcEnvironment jdbcEnv) {
        return convert(identifier);
    }

    @Override
    public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnv) {
        return convert(identifier);
    }

    private Identifier convert(Identifier identifier) {
        if (identifier == null || StringUtils.isBlank(identifier.getText())) {
            return identifier;
        }

        String regex = "([a-z])([A-Z])";
        String replacement = "$1_$2";
        String newName = identifier.getText().replaceAll(regex, replacement).toLowerCase();
        return Identifier.toIdentifier(newName);
    }
}

После того как я создал этот класс, я изменил свою конфигурацию на:

<entry key="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy" />

:

<entry key="hibernate.physical_naming_strategy" value="fms.util.hibernate.ImprovedNamingStrategy" />

и теперь все работает правильно.

Это решение охватывает только малую часть ImprovedNamingStrategy. В моем проекте для сопоставления таблиц и сопоставления соединений я всегда указываю имя таблицы или столбца соединения явно. Я полагаюсь на неявное преобразование имен только для имен столбцов. Поэтому это простое решение было приемлемым для меня.

Это полный пример моего конфигурационного файла Spring:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="fms" />

    <bean id="microFmsDataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="***" />
        <property name="username" value="***" />
        <property name="password" value="***" />

        <property name="testOnBorrow" value="true" />
        <property name="testOnReturn" value="true" />
        <property name="testWhileIdle" value="true" />
        <property name="validationQuery" value="select 1" />
        <property name="initialSize" value="5" />
        <property name="minIdle" value="10" />
        <property name="maxIdle" value="100" />
        <property name="maxActive" value="100" />
        <property name="removeAbandoned" value="true" />
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="microFmsDataSource"/>

        <property name="packagesToScan">
            <list>
                <value>fms</value>
            </list>
        </property>

        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>

        <property name="jpaPropertyMap">
            <map>
                <entry key="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider" />
                <entry key="hibernate.hbm2ddl.auto" value="validate" />
                <entry key="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
                <entry key="hibernate.show_sql" value="true" />
                <entry key="hibernate.format_sql" value="true" />
                <entry key="hibernate.physical_naming_strategy" value="fms.util.hibernate.ImprovedNamingStrategy" />
            </map>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

Я надеюсь, что это решение будет полезно для кого-то.:)

Ответ 2

Эта работа для меня из http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/orm/hibernate5/LocalSessionFactoryBean.html

<bean id="sessionFactory" 
    class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">

    <!-- convert aaBb to aa_bb -->
    <property name="PhysicalNamingStrategy">
        <bean class="fms.util.hibernate.ImprovedNamingStrategy" />
    </property>

    <!-- convert aa_bb to aaBb -->
    <property name="ImplicitNamingStrategy">
        <bean class="org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl" />
    </property>
...
</bean>   

Ответ 3

У меня была точно такая же проблема. Я исправил это с реализацией по умолчанию из весенней загрузки:

<property name="hibernate.implicit_naming_strategy" value="org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy" />
<property name="hibernate.physical_naming_strategy" value="org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy" />

Ответ 4

Для тех, кто ищет решение Java Config

Свойство hibernate.ejb.naming_strategy в hibernate 5.X кажется разделенным на две части:

hibernate.physical_naming_strategy

hibernate.implicit_naming_strategy

Spring предоставляет SpringImplicitNamingStrategy и SpringPhysicalNamingStrategy для этой цели.

Вот мой подход:

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@DependsOn("myDataSource")
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "myEntityManagerFactory",
        basePackages={"com.myapp.repo"},
        transactionManagerRef="myTransactionManager"
        )
public class MyJpaConfig {

    private Map<String, Object> properties;

    public MyJpaConfig() {
        properties = new HashMap<>();
        properties.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
        properties.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());

    }

    @Bean(name = "myEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
            @Qualifier("systemDataSource") DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean build = builder.dataSource(dataSource)
                .packages("com.myapp.entity")
                .properties(properties)
                .build();
        return build;

    }

    @Bean(name = "myTransactionManager")
    public PlatformTransactionManager myTransactionManager(
            @Qualifier("myEntityManagerFactory") EntityManagerFactory myEntityManagerFactory) {
        return new JpaTransactionManager(myEntityManagerFactory);
    }
}

Ответ 5

попробуйте следующее:

@Bean
public LocalSessionFactoryBean getSessionFactory() {
    LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
    localSessionFactoryBean.setDataSource(getDataSource());
    localSessionFactoryBean.setHibernateProperties(getHibernateProperties());
    localSessionFactoryBean.setPackagesToScan(new String[]{"com.xxx.pojo"});
    // -----important-----
    localSessionFactoryBean.setPhysicalNamingStrategy(new CustomNamingStrategy());
    return localSessionFactoryBean;
}

public class CustomNamingStrategy extends PhysicalNamingStrategyStandardImpl {***

Ответ 6

Ниже приведены 3 пункта, которые я обнаружил во время работы над стратегией именования:

  1. Если вы предоставляете аннотации @Table и @Column в ваших классах сущностей с именами, снабженными подчеркиванием, т.е. user_id, т.е. @Column(name="user_id"), имя столбца будет приниматься как user_id; если вы зададите его как ИД пользователя, он изменится на user_id, если вы не используете стратегию или неявную стратегию (в частности, org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl). Итак, если вам нужна стратегия, в которой имя атрибута сущности изменяется на имя с символами подчеркивания и строчными буквами, то есть с userId до user_id, вам следует использовать неявную или не использовать стратегию (которая на самом деле использует неявная стратегия).

  2. Если вы не хотите, чтобы ваша стратегия именования добавляла подчеркивание к имени столбца или имени класса, то стратегия, которую вам нужно использовать, будет выглядеть следующим образом: spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl. Вещи, которые вы предоставляете в аннотациях @Table и @Columns атрибут имени, останутся без изменений.

  3. Если вы не хотите предоставлять аннотации и хотите вручную обрабатывать имя таблицы и имена столбцов, вы должны расширить класс org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl и переопределить необходимые методы. Если вы все еще используете аннотации для некоторых случаев здесь, помните, что переопределенные методы будут применяться к именам, написанным в этих аннотациях.

Ответ 7

Полный Spring config, который работает для меня:       

    <property name="dataSource" ref="dataSource"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="database" value="${db.platform}"/>
            <property name="generateDdl" value="false"/>
        </bean>
    </property>
    <property name="jpaProperties">
        <props>
            <!--<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>-->
            <prop key="hibernate.implicit_naming_strategy">org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl</prop>
            <prop key="hibernate.format_sql">${hibernate.format_sql:false}</prop>
            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto:validate}</prop>
            <prop key="hibernate.show_sql">${hibernate.show_sql:false}</prop>
            <prop key="hibernate.use_sql_comments">${hibernate.use_sql_comments:false}</prop>
            <prop key="hibernate.id.new_generator_mappings">${hibernate.id.new_generator_mappings:true}</prop>
            <prop key="hibernate.enable_lazy_load_no_trans">${hibernate.enable_lazy_load_no_trans:true}</prop>
            <prop key="hibernate.max_fetch_depth">${hibernate.max_fetch_depth:1}</prop>
            <prop key="hibernate.default_batch_fetch_size">${hibernate.default_batch_fetch_size:16}</prop>
        </props>
    </property>
</bean>