Spring Boot + JPA : Column name annotation ignored
JavaHibernateJpaSpring BootJava Problem Overview
I have a Spring Boot application with dependency spring-boot-starter-data-jpa
. My entity class has a column annotation with a column name. For example:
@Column(name="TestName")
private String testName;
SQL generated by this created test_name
as the columns name. After looking for a solution I have found that spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy
solved the problem (column name is taken from column annotation).
Still, my question is why without naming_strategy set to EJB3NamingStrategy
JPA is ignoring column annotation? Maybe hibernate dialect has something to do with it? I'm connecting to MS SQL 2014 Express and my logs contain:
Unknown Microsoft SQL Server major version [12] using SQL Server 2000 dialect
Using dialect: org.hibernate.dialect.SQLServerDialect
Java Solutions
Solution 1 - Java
For Hibernate 5, I solved this issue by adding the following lines in my application.properties file:
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
Solution 2 - Java
By default Spring uses org.springframework.boot.orm.jpa.SpringNamingStrategy
to generate table names. This is a very thin extension of org.hibernate.cfg.ImprovedNamingStrategy
. The tableName
method in that class is passed a source String
value but it is unaware if it comes from a @Column.name
attribute or if it has been implicitly generated from the field name.
The ImprovedNamingStrategy
will convert CamelCase
to SNAKE_CASE
where as the EJB3NamingStrategy
just uses the table name unchanged.
If you don't want to change the naming strategy you could always just specify your column name in lowercase:
@Column(name="testname")
Solution 3 - Java
It seems that
> @Column(name="..")
is completely ignored unless there is
> spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy
specified, so to me this is a bug.
I spent a few hours trying to figure out why @Column(name="..") was ignored.
Solution 4 - Java
The default strategy for @Column(name="TestName")
will be test_name
, this is correct behavior!
If you have a column named TestName
in your database you should change Column annotation to @Column(name="testname")
.
This works because database does not care if you name your column TestName or testname (column names are case insensitive!!).
But beware, the same does not apply for database name and table names, that are case sensitive on Unix systems but case in sensitive on Windows systems (the fact that probably kept a lot of people awake at night, working on windows but deploying on linux :))
Solution 5 - Java
The only solution that worked for me was the one posted by teteArg above. I'm on Spring Boot 1.4.2 w/Hibernate 5. Namely
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
For additional insight I'm posting the call trace so that its clear what calls Spring is making into Hibernate to setup the naming strategy.
at org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl.toPhysicalColumnName(PhysicalNamingStrategyStandardImpl.java:46)
at org.hibernate.cfg.Ejb3Column.redefineColumnName(Ejb3Column.java:309)
at org.hibernate.cfg.Ejb3Column.initMappingColumn(Ejb3Column.java:234)
at org.hibernate.cfg.Ejb3Column.bind(Ejb3Column.java:206)
at org.hibernate.cfg.Ejb3DiscriminatorColumn.buildDiscriminatorColumn(Ejb3DiscriminatorColumn.java:82)
at org.hibernate.cfg.AnnotationBinder.processSingleTableDiscriminatorProperties(AnnotationBinder.java:797)
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:561)
at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:245)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:222)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:265)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:847)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:874)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:353)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:373)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:362)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1642)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1579)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
- locked <0x1687> (a java.util.concurrent.ConcurrentHashMap)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1081)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:856)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
- locked <0x1688> (a java.lang.Object)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:761)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:371)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1186)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1175)
Solution 6 - Java
teteArg, thank you so much. Just an added information so, everyone bumping into this question will be able to understand why.
What teteArg said is indicated on the Spring Boot Common Properties: http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
Apparently, spring.jpa.hibernate.naming.strategy is not a supported property for Spring JPA implementation using Hibernate 5.
Solution 7 - Java
Turns out that I just have to convert @column
name testName to all small letters, since it was initially in camel case.
Although I was not able to use the official answer, the question was able to help me solve my problem by letting me know what to investigate.
Change:
@Column(name="testName")
private String testName;
To:
@Column(name="testname")
private String testName;
Solution 8 - Java
you have to follow some naming strategy when you work with spring jpa. The column name should be in lowercase or uppercase.
@Column(name="TESTNAME")
private String testName;
or
@Column(name="testname")
private String testName;
keep in mind that, if you have your column name "test_name" format in the database then you have to follow the following way
@Column(name="TestName")
private String testName;
or
@Column(name="TEST_NAME")
private String testName;
or
@Column(name="test_name")
private String testName;
Solution 9 - Java
If you want to use @Column(...), then use small-case letters always even though your actual DB column is in camel-case.
Example: If your actual DB column name is TestName
then use:
@Column(name="testname") //all small-case
If you don't like that, then simply change the actual DB column name into: test_name
Solution 10 - Java
I also tried all the above and nothing works. I got field called "gunName" in DB and i couldn't handle this, till i used example below:
@Column(name="\"gunName\"")
public String gunName;
with properties:
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
also see this: https://stackoverflow.com/a/35708531
Solution 11 - Java
In my case, the annotation was on the getter() method instead of the field itself (ported from a legacy application).
Spring ignores the annotation in this case as well but doesn't complain. The solution was to move it to the field instead of the getter.
Solution 12 - Java
I tried all the above and it didn't work. This worked for me:
@Column(name="TestName")
public String getTestName(){//.........
Annotate the getter instead of the variable
Solution 13 - Java
Using maven 3.8.3, I found funny case where the column name of the table is following the get/set method naming. Even when I added a new field in the entity, it didn't create a new column in the table if I didn't specify the get/set method.
But, if I remove all get/set methods from the entity class, the naming of columns in the table follows the naming of fields in the entity class.
(I'm a rookie, it might be a feature which ensures logical correctness :)