我正在使用postgresql数据库和spring + hibernate框架编写应用程序。
我将spring框架从4.1.5.RELEASE升级到4.2.0.RELEASE版本,并将hibernate框架从4.3.7.Final升级到5.0.0.Final版本。
升级后我遇到NamingStrategy问题。在postgresql数据库中,表列名称以小写字母用下划线分隔,在应用程序层中,bean属性在camelcase中。
这是旧版本的弹簧配置文件:
<?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
但没有成功。
你能告诉我Hibernate 5中ImprovedNamingStrategy的替代方案是什么,并提供一个有效的配置示例吗?
答案 0 :(得分:28)
我想我找到了解决方案。
为了实现我的目标,我使用了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>
我希望这个解决方案对某些人有用。 :)
答案 1 :(得分:5)
<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>
答案 2 :(得分:2)
我遇到了完全相同的问题。我用Spring boot的默认实现修复了它:
<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" />
答案 3 :(得分:0)
试试这个:
@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 {***
答案 4 :(得分:0)
请在我命名策略工作中发现以下3点:
如果您在实体类中提供@Table
和@Column
批注,且其名称带有下划线,即 user_id 即@Column(name="user_id")
,则它将以列名为 user_id ;如果您将其指定为 userid ,则如果您不使用任何策略或隐式策略(特别是org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
),它将更改为 user_id 。因此,如果您想要一个策略,其中实体属性名称更改为带下划线和小写字母的策略,即从 userId 到 user_id 的某一项,则应使用隐式或不使用策略(实际上使用隐式策略)。
如果您不希望您的命名策略在列名或类名上添加下划线,则您需要使用的策略如下所示:
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
。您在注释@Table
和@Column’s
name属性中提供的内容将保持原样。
如果您不想提供批注并且想要手动处理表名和列名,则应该扩展类org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
并覆盖所需的方法。如果您在某些情况下仍使用注释,请记住,覆盖的方法将应用于这些注释中写的名称。