Spring Boot + JPA : Column name annotation ignored - java

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

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

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")

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.

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 :))

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)

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;

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.

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;

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

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

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

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.

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 :)

Related

HibernateException: Missing column: Wrong name

In my Java code, I have a field named isNegative with a similar column existing in database. But Hibernate insists the name should be is_negative, even with forcing the name with #Column.
#Column(name="isNegative")
private boolean isNegative;
Error:
Caused by: org.hibernate.HibernateException: Missing column:
is_negative in datasource.item
Application.properties:
#JPA
spring.data.jpa.repositories.enabled=false
spring.jpa.database=mysql
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.jpa.generate-ddl=true
spring.jpa.open-in-view=true
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.hibernate.use-new-id-generator-mappings=false
spring.jpa.properties.hibernate.event.merge.entity_copy_observer=allow
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
That's due to your configuration, because you are setting spring.jpa.hibernate.naming.physical-strategy to PhysicalNamingStrategyStandardImpl which will use underscores for the names.
If you check the Configure Hibernate Naming Strategy section of Spring Docs, you can see that:
Hibernate uses two different naming strategies to map names from the object model to the corresponding database names. The fully qualified class name of the physical and the implicit strategy implementations can be configured by setting the spring.jpa.hibernate.naming.physical-strategy and spring.jpa.hibernate.naming.implicit-strategy properties, respectively. Alternatively, if ImplicitNamingStrategy or PhysicalNamingStrategy beans are available in the application context, Hibernate will be automatically configured to use them.
By default, Spring Boot configures the physical naming strategy with
SpringPhysicalNamingStrategy. This implementation provides the same
table structure as Hibernate 4: all dots are replaced by underscores
and camel casing is replaced by underscores as well. By default, all
table names are generated in lower case, but it is possible to
override that flag if your schema requires it.
To solve that you need to remove this property and use the default naming strategy instead:
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultNamingStrategy
You would need spring.jpa.hibernate.naming.physical-strategy and spring.jpa.hibernate.naming.implicit-strategy
Adding following
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
to application.properties could help. This solution would work from hibernate 5.
Hope it helps.
Please find below my analysis:
If you don't want your naming strategy to add an underscore to the column name or class name, then the strategy that you need to use would look like: spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl. The things that you provide in annotations #Table and #Column’s name attribute would remain as it is. E.g. firstName attribute in entity will get a column name as firstName i.e. No change.
If you don't want to provide annotations and want to manually handle the table name and column names, you should extend the class org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl and override the required methods. If you still use annotations for some of the cases here, remember the overridden methods will apply on the names written in those annotations. spring.jpa.hibernate.naming.physical-strategy=example.CustomStrategy

SQLServerException: Invalid column name [duplicate]

#Column(name="DateOfBirth")
private Date dateOfBirth;
I specifically need the above code to create a column named "DateOfBirth," instead Hibernate gives me a column named date_of_birth. How can I change this? Is there a web.xml property? I came across DefaultNamingStrategy and ImprovedNamingStrategy, but not sure how to specify one or the other.
Try putting this in
application.properties
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
FYI: The reason for the insertion of underscores is probably because you're using an ImprovedNamingStrategy. It's set on your Configuration object. See here for an example...
If you don't want the underscores you can just not set the naming strategy, or set it to the DefaultNamingStrategy you discovered earlier.
Here is a possible workaround: if you name it dateofbirth the column in the DB would be named like that, but the attribute name should be the same.
Hibernate takes the camel case format to create/read database columns.
I've had this problem before. I worked with a legacy columns where there was no space in the column names "employeename", "employeerole", "departmentlocation". I hate it because all my beans properties had to be without camel case.
Database columns separated by "_" will be used to properly camelCase as you have just seen.
add below property in the case of spring boot.
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
Put the #Column annotation on the getter:
#Column(name="DateOfBirth")
public Date getDateOfBirth() {
...
}
The workaround proposed was to use #Column(name="dateofbirth"), which worked for my purposes.
ImprovedNamingStrategy has method addUnderscores() which is called from tableName() and columnName()
you can implement your own naming strategy class and override these as per your choice
public class MyOwnNamingStrategy extends ImprovedNamingStrategy {
#Override
public String tableName(String tableName) {
//return addUnderscores(columnName); // skip this
return columnName; // if you want column name variable name same
//return changeAsYouWant(columnName); // as name sames
}
}
You can annotate either fields or getter methods, it doesn't make a difference. Can you post your full hibernate.cfg.xml or persistence.xml file?
I'm not 100% sure, but don't you need to annotate the get method and not the private variable?
I had a similar problem and adding the following two properties to my application.properties solved my issue: spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl .

How can I use capitalized property in a hibernate entity

In our product we use auto generated hibernate entities to be able to link a customizable Database scheme to our server software. The entity names and property names are taken from the data base. Especially, the property names can usually not be changed as they also are used in user code unrelated to the hibernate data layer (e.g. python scripts)
Some of these property names are capitalized, which seems to cause some problems. HQL statements using those property names fail with an Exception, e.g.:
org.hibernate.QueryException: could not resolve property List_id
at org.hibernate.QueryException.generateQueryException(QueryException.java:137)
at org.hibernate.QueryException.wrapWithQueryString(QueryException.java:120)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:234)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:158)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:126)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:88)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:190)
Some code snippet for the example Exception:
#Entity(name = "ListItem")
#Table(name = "LIST_ITEM")
public class ListItem
extends HibernatePojoClass
{
private String List_id = "";
#Column(name = "`LIST_ID`", length = 8)
public String getList_id() {
return List_id;
}
public void setList_id(String List_id) {
this.List_id = List_id;
}
...
and the HQL statement:
select li.id, li.List_id from ListItem li
The exception occurs when hibernate tries to transform the hql statement to a sql statement.
Why does this happen?
It seems that when I use li.list_id in the hql statement, the property is resolved (while this leads to another error); can I prevent this implicit "capitalization change" somehow?
if you use
#Column(name = "`LIST_ID`", length = 8)
public String getList_id() {
return List_id;
}
you should refer that property as list_id in HQL, of course.
Hibernate can use a naming strategy to generate column names. ImprovedNamingStrategy from Hibernate 4 will convert column name to the lower case, even if you specify it. I am not sure about the quotes, but for this:
#Column(name = "LIST_ID", length = 8)
public String getList_id() {
return List_id;
}
using ImprovedNamingStrategy you will have list_id column name.
You can try to use your own naming strategy to generate correct column names.
JPA has 2 basic access modes: property access and field access.
Property access requires you to adhere to the Java Beans convention which means you need field name that starts with a lower case character and a corresponding getter/setter which has the same character in upper case, i.e. field listId would require a getter getListId().
Thus you'd need to use field access in order to have Hibernate use the field name as it is. Another advantage of using field access on an entity's id would be that you'd not need to do any lazy loading in order to just get the id - which wouldn't be possible with property access in Hibernate.
For more information have a look at sections 2.2 and 2.3 of the JPA specification.
A final word of advice though: as already stated multiple times in my comments you should try and stick with the Java code conventions. Some advantages of doing so:
It'll be easier to communicate with others such as people here on SO (e.g. a name starting with a capital letter normally is assumed to be a class name).
You'll have less problems with libraries in the Java eco system since most of them use the same conventions or are based on them (e.g. JavaBeans, JavaEL, etc.)
It'll be easier to spot errors, e.g. when using a class rather than a field or variable etc.
You'll be less dependent on IDE features like code coloring, error highlighting etc.

All my fields have a prefix. How can I tell that to hibernate?

E.g. I have:
#Column(name = "username")
private String m_username;
Note that the #Column annotation only affects the database column name.
Hibernate still thinks the name of the property is 'm_username'.
How can I tell Hibernate that the property name is just 'username'?
Please tell me there is a way to do this...
Edit: I removed the #AccessType annotation in my code example, as it is not relevant for this question.
Update: After switching everything to field access, this exception happens:
org.hibernate.QueryException: could not resolve property: username of: mypackage.model.User
It happens here:
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.eq("username", username));
User result = (User) criteria.uniqueResult();
And the reason is most likely that hibernate only 'knows' of a property called 'm_username', while I think of it and program against a property named 'username'. Also note that my getters/setters are called: "getUsername()" and "setUsername(String value)" (automatically generated).
why do you use AccessType.PROPERTY?
remove it and it is accessed by 'field'.
do not mix field and property annotations. stick to one or the other.
As per my knowledge we can't* .
There is still one issue with the way hibernate looks up the methods
for a given property name.
Suppose you have a property with a name like "mProperty" (first
lowercase, second uppercase, rest doesn't matter). Not the accessor
methods in the source code will be getMProperty and setMProperty.
The way BasicPropertyAccessor.getterMethod is implemented in that way .
I found one lead here: https://forum.hibernate.org/viewtopic.php?f=1&t=943110
But the thread is 7 years old and I don't know how to apply this to annotation based configuration (I don't have an xml configuration file).

Why Hibernates ignores the name attribute of the #Column annotation?

Using Hibernate 3.3.1 and Hibernate Annotations 3.4, the database is DB2/400 V6R1, running that on WebSphere 7.0.0.9
I have the following class
#Entity
public class Ciinvhd implements Serializable {
#Id
private String ihinse;
#Id
#Column(name="IHINV#")
private BigDecimal ihinv;
....
}
For reasons I can't figure, Hibernate ignores the specified column name and uses 'ihinv' to generate the SQL:
select
ciinvhd0_.ihinse as ihinse13_,
ciinvhd0_.ihinv as ihinv13_,
...
Which of course gives me the following error:
Column IHINV not in table CIINVHD
Edit: I switched the log level of hibernate to DEBUG, and I see that it does not process the column annotation for that field. Tried several random things it just doesn't work.
Did anyone had this problem before? I have other entities that are very alike in the way that they are using # in their database field names and that are part of the PK and I don't have this problem with them.
You could try some kind of quoting:
For example:
#Column(name="`IHINV#`")
or
#Column(name="'IHINV#'")
Another option would be to dig in to source code Hibernate dialect for DB2 and see if it contains anything helpful.
Of course, the easiest way would be to remove the hash from column name if possible.
I suspect that the problem is the hash in the column name. A similar question on the hibernate forums suggests that backticks can be useful here.

Categories

Resources