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

results matching ""

    No results matching ""