ImprovedNamingStrategy no longer working in Hibernate 5
Solution 1
Just figured out the issue, the configuration is absolutely fine when using a Hibernate version < 5.0 but not for Hibernate >= 5.0.
I was using Hibernate 5.0.0.Final with Spring 4.2.0.RELEASE. I guess the Hibernate 5 is not fully compatible with Spring 4.2 . I just downgraded Hibernate to 4.2.1.Final and things started working fine.
Hibernate's NamingStrategy
class is deprecated in Hibernate 5.
Solution 2
Thanks for posting your own solution. It helps me so much to set Hibernate 5 naming strategy!
The hibernate.ejb.naming_strategy
property of pre-Hibernate 5.0 seems split into two parts:
hibernate.physical_naming_strategy
hibernate.implicit_naming_strategy
The values of these properties do not implement the NamingStrategy
interface as did hibernate.ejb.naming_strategy
. There are two new interfaces for these purposes:
org.hibernate.boot.model.naming.PhysicalNamingStrategy
org.hibernate.boot.model.naming.ImplicitNamingStrategy
Hibernate 5 provides only one implementation of PhysicalNamingStrategy
(PhysicalNamingStrategyStandardImpl
) that assumes physical identifier names are the same as logical ones.
There are several implementations of ImplicitNamingStrategy
but I found none equivalent to the old ImprovedNamingStrategy
. (See: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
)
So, I implemented my own PhysicalNamingStrategy
which is very simple:
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);
}
}
Note that the addUnderscores()
method is from the original org.hibernate.cfg.ImprovedNamingStrategy
.
Then, I set this physical strategy into the persistence.xml file :
<property name="hibernate.physical_naming_strategy" value="my.package.PhysicalNamingStrategyImpl" />
It is a trap to set Hibernate 5 naming strategy as previous version settings.
Solution 3
Thanks and +1 to Samuel Andrés for the very helpful answer, however it's probably a good idea to avoid the hand-written snake-casing logic. Here is the same solution, using Guava.
It assumes your entity names are written in the StandardJavaClassFormat
and column names in the standardJavaFieldFormat
Hopefully this will save some people coming here in future some googling :-)
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()
);
}
}
Solution 4
Instead of
jpaProperties.put("hibernate.ejb.naming_strategy",
"org.hibernate.cfg.ImprovedNamingStrategy");
change to the new physical naming strategy and the new implementation CamelCaseToUnderscoresNamingStrategy
, which behaves as the old ImprovedNamingStrategy
:
jpaProperties.put("hibernate.physical_naming_strategy",
"org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy");
Available in Hibernate 5.6.1.FINAL
Solution 5
Every answer posts solution by implementing PhysicalNamingStrategy
, but all you need (and should do) is to implement ImplicitNamingStrategy
.
When an entity does not explicitly name the database table that it maps to, we need to implicitly determine that table name. Or when a particular attribute does not explicitly name the database column that it maps to, we need to implicitly determine that column name. There are examples of the role of the org.hibernate.boot.model.naming.ImplicitNamingStrategy contract to determine a logical name when the mapping did not provide an explicit name.
And the code can be as easy as this (using the original addUnderscores
as in other answers):
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);
}
}
Related videos on Youtube
Anup
I am a Software Developer by profession. A keen Learner :)
Updated on December 27, 2021Comments
-
Anup over 2 years
I have simple spring-jpa configuration where I have configured Hibernate's
ImprovedNamingStrategy
. This means if my entity class has a variableuserName
, then Hibernate should convert it touser_name
for querying the database. But this naming conversion stopped working after I upgraded to Hibernate 5. I am getting the error:ERROR: Unknown column 'user0_.userName' in 'field list'
This is my Hibernate config:
@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(); } }
This is my Entity class:
@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; }
I don't want to explicitly name my database fields within the @Column annotations. I want my configuration which can implicitly convert camel case to underscore.
Please guide.
-
T.D. Smith over 8 yearsI don't understand why you don't want to use @Column
-
Anup over 8 years@Tyler its just for ease of coding, adding "@Column" for each of variable is just annoying, instead i can configure Naming-Strategy which will map the variable name to db column names and i can avoid writing so many Column annotation
-
-
Glenn over 8 yearsAlso, along with the custom physical naming strategy, the property
hibernate.implicit_naming_strategy
can be set toorg.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
-
Anup over 8 yearsThanks @Samuel for explaining this and providing example for implementing ur own Naming_Strategy. Its helpful and even I am looking to implement similar Naming_Strategy
-
iberbeu about 8 yearsgreat answer Samuel, it deserves to be the correct one and not the other. Does anyone know why they changed this behavior? It makes to me no sense at all
-
Jagger almost 8 yearsWhy do you add
implements Serializable
while theorg.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
already implements that? -
Kostas Filios almost 8 yearsSpring Boot 1.4 update: You can now use Spring's SpringPhysicalNamingStrategy like this (yml):
spring.jpa.properties.hibernate.physical_naming_strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
-
xathien almost 8 yearsAwesome, though has some of its own flaws. For example, Hibernate's default discriminator column name is "DTYPE" which will get converted to "d_t_y_p_e".
-
davnicwil almost 8 years@xathien thanks, was not aware of that. I guess a static mapping of special cases could be added to deal with things like this in a clear way
-
Marcel Stör over 7 yearsI don't think that's a clever idea.
ImprovedNamingStrategy
implements a deprecated interface. While only the interfaceNamingStrategy
is explicitly marked deprecated I'd assume once that one is removed all implementing classes will be removed as well. -
Arjan Mels about 7 yearsI don't think it is needed to touch the physical naming strategy, which will also override explicitly set column names. Just changing the implicit strategy (which is only applied when no explicit overrides are set) should be sufficient. There is a default implicit naming strategy available, which prefixes embedded classes:
hibernate.implicit_naming_strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl
See also the hibernate documentation for more details: docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/… -
Rich about 7 years@MarcelStör -- I think that's a feature of this approach, not a bug! If and when Hibernate get around to replacing the old "ImprovedNamingStrategy", they will hopefully remove that deprecated class, and this code will break nosily, giving us a prompt to switch over to the new official implementation and to remove this workaround.
-
ajkush almost 5 yearsI don't understand why this answer is accepted and it has many negative votes!
-
Prashant about 4 yearsThat is because OP answered own question. It might have worked for the OP but the community thinks that either the approach taken by the OP is not correct or the answer provided by the OP is not a generic one.
-
DavidR over 2 yearsHopefully people make it down this far! This is by far the cleanest solution and should be the accepted answer.