从 object model 到 database 的 name mapping 有两个阶段:
- 从 domain model mapping 中取得 logical name ,可以是显式设置(@Column / @Table)或者隐式设置(org.hibernate.boot.model.naming.ImplicitNamingStrategy)
- 从 logical name 翻译成 physical name ,通过 org.hibernate.boot.model.naming.PhysicalNamingStrategy
旧版本的 Hibernate 只定义了一个 org.hibernate.cfg.NamingStrategy ,而今拆分成两个: ImplicitNamingStrategy/PhysicalNamingStrategy
JPA 有自己的固有的(inherent) naming 规则,且不区分 logical name/physical name 。因此如果要与 JPA 兼容,则 ImplicitNamingStrategy 应该使用 ImplicitNamingStrategyJpaCompliantImpl 这也是默认值,而 PhysicalNamingStrategy 应该不指定。
ImplicitNamingStrategy
只有没有显式设置(@Column / @Table)时才会使用 ImplicitNamingStrategy
org.hibernate.boot.model.naming.ImplicitNamingStrategy 有几个内建的实现类:
- org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl - JPA 2.0 兼容的 naming strategy
- org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl - 原来的 Hibernate NamingStrategy
- org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl - JPA 1.0 的 naming strategy 在很多情况下命名不清晰
- org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl - 使用 full composite paths 而不只是 ending property part
指定 ImplicitNamingStrategy 的方式
- 使用 hibernate.implicit_naming_strategy 配置项指定
- 使用 org.hibernate.boot.MetadataBuilder#applyImplicitNamingStrategy 指定
hibernate.implicit_naming_strategy 可以使用
- 短名 - default/jpa/legacy-hbm/legacy-jpa/component-path ,其中 default 是 jpa 的别名
- 实现 org.hibernate.boot.model.naming.ImplicitNamingStrategy 的 Class 的引用
- 实现 org.hibernate.boot.model.naming.ImplicitNamingStrategy 的 Class 的全名
PhysicalNamingStrategy
指定 PhysicalNamingStrategy 的方式:
- 使用 hibernate.physical_naming_strategy 配置项指定
- 使用 org.hibernate.boot.MetadataBuilder#applyPhysicalNamingStrategy 指定
hibernate.physical_naming_strategy 可以使用
- 实现 org.hibernate.boot.model.naming.PhysicalNamingStrategy 的 Class 的引用
- 实现 org.hibernate.boot.model.naming.PhysicalNamingStrategy 的 Class 的全名
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.example.demo</groupId>
<artifactId>demo-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>demo-hibernate</artifactId>
<packaging>jar</packaging>
<name>demo-hibernate</name>
<properties>
<hibernate.version>5.2.10.Final</hibernate.version>
</properties>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1212</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package org.example.demo.hibernate;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.apache.commons.lang3.StringUtils;
/**
* An example PhysicalNamingStrategy that implements database object naming standards
* for our fictitious company Acme Corp.
* <p/>
* In general Acme Corp prefers underscore-delimited words rather than camel casing.
* <p/>
* Additionally standards call for the replacement of certain words with abbreviations.
*
* @author Steve Ebersole
*/
public class AcmeCorpPhysicalNamingStrategy implements PhysicalNamingStrategy {
private static final Map<String, String> ABBREVIATIONS = buildAbbreviationMap();
@Override
public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
// Acme naming standards do not apply to catalog names
return name;
}
@Override
public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
// Acme naming standards do not apply to schema names
return null;
}
@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
final List<String> parts = splitAndReplace(name.getText());
return jdbcEnvironment.getIdentifierHelper().toIdentifier(join(parts), name.isQuoted());
}
@Override
public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment) {
final LinkedList<String> parts = splitAndReplace(name.getText());
// Acme Corp says all sequences should end with _seq
if (!"seq".equalsIgnoreCase(parts.getLast())) {
parts.add("seq");
}
return jdbcEnvironment.getIdentifierHelper().toIdentifier(join(parts), name.isQuoted());
}
@Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
final List<String> parts = splitAndReplace(name.getText());
return jdbcEnvironment.getIdentifierHelper().toIdentifier(join(parts), name.isQuoted());
}
private static Map<String, String> buildAbbreviationMap() {
TreeMap<String, String> abbreviationMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
abbreviationMap.put("account", "acct");
abbreviationMap.put("number", "num");
return abbreviationMap;
}
private LinkedList<String> splitAndReplace(String name) {
LinkedList<String> result = new LinkedList<>();
for (String part : StringUtils.splitByCharacterTypeCamelCase(name)) {
if (part == null || part.trim().isEmpty()) {
// skip null and space
continue;
}
part = applyAbbreviationReplacement(part);
result.add(part.toLowerCase(Locale.ROOT));
}
return result;
}
private String applyAbbreviationReplacement(String word) {
if (ABBREVIATIONS.containsKey(word)) {
return ABBREVIATIONS.get(word);
}
return word;
}
private String join(List<String> parts) {
boolean firstPass = true;
String separator = "";
StringBuilder joined = new StringBuilder();
for (String part : parts) {
joined.append(separator).append(part);
if (firstPass) {
firstPass = false;
separator = "_";
}
}
return joined.toString();
}
}
package org.example.demo.hibernate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.service.ServiceRegistry;
public class Test {
public static void main(String[] args) {
ServiceRegistry standardRegistry = new StandardServiceRegistryBuilder()
.applySetting("hibernate.connection.url", "jdbc:p6spy:postgresql://localhost:5432/test")
.applySetting("hibernate.connection.username", "postgres")
.applySetting("hibernate.connection.password", "postgres")
.applySetting("hibernate.physical_naming_strategy",
"org.example.demo.hibernate.AcmeCorpPhysicalNamingStrategy")
.applySetting("hibernate.hbm2ddl.auto", "create").build();
Metadata metadata = new MetadataSources(standardRegistry).addAnnotatedClass(Person.class).getMetadataBuilder()
.build();
SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build();
sessionFactory.close();
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
Long id;
String accountNumber;
}
}
生成的 SQL
drop table if exists person cascade
drop sequence if exists hibernate___sequence_seq
create sequence hibernate___sequence_seq start 1 increment 1
create table person (id int8 not null, acct_num varchar(255), primary key (id))
如果去掉第 19-20 行,则生成 SQL
drop table if exists Person cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int8 not null, accountNumber varchar(255), primary key (id))