I think I'm trying to do something really simple. Using Spring Boot (1.3.3.RELEASE) with JPA I want to set a table name.
@Entity
@Table(name = "MyTable_name")
public class MyTableData {
...
}
What I expect in my database is a table with "MyTable_name". Seems completely reasonable to me. But that doesn't happen. I get a table with name "MY_TABLE_NAME" (H2 backend) or "my_table_name" (Postgre backend). From here on I'll stick with Postgre since my goal is to read an existing DB where I don't control the table names.
After some research I find posts that say I should use the spring.jpa.hibernate.naming-strategy property. This doesn't help much. Setting to the most commonly recommended org.hibernate.cfg.ImprovedNamingStrategy produces the same behavior: "my_table_name". Setting to org.hibernate.cfg.EJB3NamingStrategy produces "mytable_name". Setting to org.hibernate.cfg.DefaultNamingStrategy causes application context errors in Spring's innards.
Resigned to writing my own, I started looking at org.hibernate.cfg.ImprovedNamingStrategy. I discovered it used the deprecated org.hibernate.cfg.NamingStrategy. That suggests using NamingStrategyDelegator instead. I looked at its Java docs but not sure how to apply. I found this post. As much as I appreciate the explanation, what is trying to be done there is more complex than what I need and I had trouble applying it.
My question then is how can I get Spring JPA to just use the name I specify? Is there a new property for NamingStrategyDelegator use? Do I need to write my own strategy?
=========== Update ==========================
I think I'm converging on an answer. I created a simple Spring startup application (separate from my production project). I use H2 for the backend DB.
This discussion on Hiberate 5 Naming is very helpful. With it I figured out how to set naming strategies in Hibernate 5 like the following (in application.properties).
hibernate.implicit_naming_strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
I created a physical naming strategy that passed through the name (like org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl does) and prints out values. From that I see that tables names are what I want through the physical naming layer.
I then set hibernate.show_sql=true to show generate SQL. In the generated SQL the names are also correct.
I am examining table names using DatabaseMetaData.
private void showTables() throws SQLException {
DatabaseMetaData dbMetadata = getConnection().getMetaData();
ResultSet result = dbMetadata.getTables(null, null, null, new String[] { "TABLE" });
if (result != null) {
boolean haveTable = false;
while (result.next()) {
haveTable = true;
getLogger().info("Found table {}", result.getString("TABLE_NAME"));
}
if (!haveTable) {
getLogger().info("No tables found");
}
}
}
I still see table names in ALL CAPS when I use the above code. This leads me to believe that DatabaseMetaData is showing all caps for some reason but the rest of the code uses the correct names. [EDIT: This conclusion is not correct. I was just confused by everything else that was happening. Later testing shows DatabaseMetaData shows table names with correct case.]
This is not yet a complete answer because there is still some strangeness in my production code that I need to investigate. But it's close and I wanted to post an update so potential readers don't waste time.
Here is my pass through physical naming strategy in case anyone is interested. I know it can help to see what others have done, especially when trying to find classes and packages in the Spring labyrinth.
package my.domain.eric;
import java.io.Serializable;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NamingStrategyPhysicalLeaveAlone implements PhysicalNamingStrategy, Serializable {
private static final long serialVersionUID = -5937286882099274612L;
private static final Logger LOGGER = LoggerFactory.getLogger(NamingStrategyPhysicalLeaveAlone.class);
protected Logger getLogger() {
return LOGGER;
}
@Override
public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment context) {
String nameText = name == null ? "" : name.getText();
getLogger().info("toPhysicalCatalogName name: {}", nameText);
return name;
}
@Override
public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment context) {
String nameText = name == null ? "" : name.getText();
getLogger().info("toPhysicalSchemaName name: {}", nameText);
return name;
}
@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
String nameText = name == null ? "" : name.getText();
getLogger().info("toPhysicalTableName name: {}", nameText);
return name;
}
@Override
public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment context) {
String nameText = name == null ? "" : name.getText();
getLogger().info("toPhysicalSequenceName name: {}", nameText);
return name;
}
@Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
String nameText = name == null ? "" : name.getText();
getLogger().info("toPhysicalColumnName name: {}", nameText);
return name;
}
}
The answer to my question involves the following.
Another source of confusion for me was that many sites refer to the old naming variable hibernate.ejb.naming_strategy or it's spring equivalent. For Hibernate 5 that is obsolete. Instead, as I mention in my question update, Hibernate 5 has implicit and physical naming strategies.
Furthermore, I was confused because there are hibernate properties and then there are Spring properties. I was using this very helpful tutorial. However it shows the unnecessary direct use of hibernate properties (as I list in my update) and then explicit configuration of LocalContainerEntityManagerFactoryBean and JpaTransactionManager. Much easier to use Spring properties and have them automatically picked up. Relevant to me are the naming strategies.
To implement a physical naming strategy one needs to create a class that implements org.hibernate.boot.model.naming.PhysicalNamingStrategy as I show in my update above. Quoting names is actually very easy because the Identifier class passed to the method manages quoting or not. Thus the following method will quote table names.
@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
if (name == null) {
return null;
}
return Identifier.quote(name);
}
Other things I learned that might be helpful to someone who came here searching for answers.
The name is specified in the Entity annotation
@Entity(name = "MyTable_name")
public class MyTableData {
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With