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;
}
}
答案 0 :(得分:7)
我的问题的答案涉及以下内容。
另一个令我感到困惑的原因是许多网站引用了旧的命名变量hibernate.ejb.naming_strategy或它的春天等价物。对于已经过时的Hibernate 5。相反,正如我在我的问题更新中提到的,Hibernate 5具有隐式和物理命名策略。
此外,我很困惑,因为有hibernate属性,然后有Spring属性。我正在使用this very helpful tutorial。然而,它显示了不必要的直接使用hibernate属性(我在我的更新中列出),然后显式配置LocalContainerEntityManagerFactoryBean和JpaTransactionManager。更容易使用Spring属性并自动拾取它们。与我相关的是命名策略。
要实现物理命名策略,我需要创建一个实现org.hibernate.boot.model.naming.PhysicalNamingStrategy的类,如上所述。引用名称实际上非常简单,因为传递给方法的Identifier类管理或不管理引用。因此,以下方法将引用表名。
@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
if (name == null) {
return null;
}
return Identifier.quote(name);
}
我学到的其他东西可能对来到这里寻找答案的人有所帮助。
答案 1 :(得分:1)
名称在实体注释中指定
@Entity(name = "MyTable_name")
public class MyTableData {
...
}