Issue
I am migrating a legacy application from Hibernate 3.6.10 to 5.3.8 After some adaptations it now compiles fine, but during startup the application crashes.
I am getting this error:
2021-07-23 11:23:50,740 [ERROR] [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean] Failed to initialize JPA EntityManagerFactory: @org.hibernate.annotations.Table references an unknown table: history_entry
2021-07-23 11:23:50,740 [WARN ] [org.springframework.context.support.GenericApplicationContext] Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in file [D:\projects\long_path_omitted_here\hibernate-config.xml]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: @org.hibernate.annotations.Table references an unknown table: history_entry
After quite some investigation I found that Hibernate seems to expect the Entity's classname as argument of the appliesTo
parameter. So, as an experiment I changed the argument of appliesTo
to the Java classname as shown in the snippet below and then the application initialization indeed continues (it crashes with a different issue later, but that's a different story).
@Entity
@SequenceGenerator(name = "keyid_generator", sequenceName = "history_entry_sequence")
@org.hibernate.annotations.Table(
appliesTo = "HistoryEntry", // was and should be: "history_entry",
indexes = {
... <index details omitted for brevity>
}
)
public class HistoryEntry {
... <remainder of class omitted for brevity>
}
What I don't understand here: if the name attribute to be provided here is actually the Entity class' name (here: HistoryEntry
), how is Hibernate supposed to figure out the actual table's name (which is history_entry
- with an underscore between the two words)?
I always thought that this annotation and its name attribute is exactly there to provide this mapping. But If I can't provide a table name here that's different from the Entity's classname, how can Hibernate then map these entity names to the actual DB names?
Is there a completely different mechanism now in Hibernate 5.x to provide this mapping (compared to 3.x)? I googled for quite a while but found no indication that this has changed. What am I missing here? And how can I teach hibernate, what the actual DB name for this entity is?
Solution
- Please note that @org.hibernate.annotations.Index annotation was deprecated even in hibernate 5.2. So, I would suggest you to correct your mapping in the following way:
@Entity
@SequenceGenerator(name = "keyid_generator", sequenceName = "history_entry_sequence")
@javax.persistence.Table(
name = "history_entry",
indexes = {
@javax.persistence.Index(...)
...
}
)
public class HistoryEntry {
// ...
}
and it should work without additional custom PhysicalNamingStrategy definition/registration.
-
What I don't understand here: if the name attribute to be provided here is actually the Entity class' name (here:
HistoryEntry
), how is Hibernate supposed to figure out the actual table's name (which ishistory_entry
- with an underscore between the two words)?
You should understand that hibernate use two phases process for resolving table/column names by appropriate entity/entity's field names. At the first phase hibernate checks: does an entity class have an @javax.persistence.Table
annotation with an explicit name and if no, the implicit-naming strategy implementation is asked what the table name should be. The default strategy is ImplicitNamingStrategyJpaCompliantImpl
. Then, at the second phase, hibernate applies transformation defined in the PhysicalNamingStrategy
to all entity/column names (explicitly or implicitly named). It can be useful to override this strategy for example in the following case. Suppose that all table names in your application should follow the pattern APP_<table name>
. One solution is to manually specify an @Table
annotation on all entity classes. This approach is time-consuming and easily forgotten. Instead, you can implement Hibernate’s PhysicalNamingStrategy
interface or override an existing implementation, like this:
public class YourAppNamingStrategy extends org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl {
@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
return new Identifier("APP_" + name.getText(), name.isQuoted());
}
}
and then define your naming-strategy implementation in persistence.xml
:
<property name="hibernate.physical_naming_strategy"
value="org.your.app.YourAppNamingStrategy"/>
It's difficult to say for sure, but it looks like somewhere in this chain the algorithm was changed between hibernate 3.6.10 and 5.3.8 or at least, some default values for settings that customize this process were changed and this leads to your problem.
-
I found that Hibernate seems to expect the Entity's classname as argument of the
appliesTo
parameter.
Actually, hibernate expects to obtain a physical table name here, but as you do not specify the name via the @javax.persistence.Table
annotation by default hibernate requires the entity's class name. As an additional example, assuming that you use physical naming strategy from the n.2, the following mapping would be valid:
@Entity
@javax.persistence.Table(name = "history_entry")
@org.hibernate.annotations.Table(
appliesTo = "APP_history_entry",
indexes = { ... }
)
public class HistoryEntry {
// ...
}
Answered By - SternK