techiall / Blog

🍋 [My Blog] See discussions
https://github.com/techiall/Blog/discussions
MIT License
8 stars 1 forks source link

hibernate 命名策略 #44

Open techiall opened 5 years ago

techiall commented 5 years ago

骄傲的使用 谷歌翻译

原文链接 https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/chapters/domain/naming.html

不想看原文的话,最后我会说下的理解。

hibernate 命名策略

对象模型到关系数据库的映射的一部分是将对象模型中的名称映射到相应的数据库名称。 Hibernate 将此视为两阶段过程:

过去的命名策略

之前 Hibernate 只定义了一个 org.hibernate.cfg.NamingStrategy 。这个单一的 NamingStrategy 现在已经拆成ImplicitNamingStrategyPhysicalNamingStrategy

此外,NamingStrategy 通常不够灵活,无法正确应用给定的命名“规则”,因为 API 缺乏可供决定的信息,或者因为 API 实际上没有得到很好的定义。

由于这些限制,org.hibernate.cfg.NamingStrategy 已被弃用,然后被删除,转而使用 ImplicitNamingStrategyPhysicalNamingStrategy

每种命名策略背后的想法是最小化数量,开发人员必须提供的重复信息,用于映射域模型。

JPA 兼容性

JPA 定义了关于隐式逻辑名称确定的固有规则。如果 JPA 提供程序可移植性是一个主要问题,或者您真的喜欢 JPA 定义的隐式命名规则,请务必坚持使用 ImplicitNamingStrategyJpaCompliantImpl(默认值)

此外,JPA 定义逻辑名称和物理名称之间没有分离。遵循 JPA 规范,逻辑名称是物理名称。如果 JPA 提供程序可移植性很重要,应用程序应该不要指定 PhysicalNamingStrategy

ImplicitNamingStrategy

当实体没有显式命名它映射到的数据库表时,我们需要隐式确定该表名。或者,当某个特定属性没有显式命名它映射到的数据库列时,我们需要隐式确定该列名。当映射未提供显式名称时, org.hibernate.boot.model.naming.ImplicitNamingStrategy 用于确定逻辑名称。

Hibernate定义了多个 ImplicitNamingStrategy 实现。应用程序也可以免费插入自定义实现。

有多种方法可以指定要使用的 ImplicitNamingStrategy 。首先,应用程序可以使用 hibernate.implicit_naming_strategy 配置设置来指定实现,该设置接受:

其次,应用程序和集成可以利用 org.hibernate.boot.MetadataBuilder#applyImplicitNamingStrategy 来指定要使用的 ImplicitNamingStrategy。有关引导的其他详细信息,请参阅 Bootstrap

PhysicalNamingStrategy

许多组织围绕数据库对象(表,列,外键等)的命名定义规则。 PhysicalNamingStrategy 的想法是帮助实现这样的命名规则,而不必通过显式名称将它们硬编码到映射中。

虽然 ImplicitNamingStrategy 的目的是确定名称为 accountNumber 的属性在未明确指定时映射到 accountNumber 的逻辑列名称,PhysicalNamingStrategy的目的是,例如,假设物理列名称应缩写为 acct_num

确实,在这种情况下,acct_num 的解析可以在 ImplicitNamingStrategy 中处理。但重点是分离关注点。无论属性是否明确指定列名,或者我们是否隐式确定,都将应用 PhysicalNamingStrategy 。仅当未给出显式名称时,才会应用ImplicitNamingStrategy 。所以它取决于需求和意图。

默认实现是简单地使用逻辑名称作为物理名称。但是,应用程序和集成可以定义此 PhysicalNamingStrategy 合同的自定义实现。以下是一个名为 Acme Corp 的虚构公司的 PhysicalNamingStrategy 示例,其命名标准是:

示例1. PhysicalNamingStrategy 实现示例

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.userguide.naming;

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 name;
    }

    @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();
    }
}

有多种方法可以指定要使用的 PhysicalNamingStrategy。首先,应用程序可以使用 hibernate.physical_naming_strategy 配置设置指定实现,该设置接受:

其次,应用程序和集成可以利用 org.hibernate.boot.MetadataBuilder#applyPhysicalNamingStrategy。有关引导的其他详细信息,请参阅Bootstrap。

个人总结

首先也就是 Hibernate 中的 NamingStrategy 已经被 ImplicitNamingStrategyPhysicalNamingStrategy取代。

PhysicalNamingStrategy 有两个属性

ImplicitNamingStrategy 有四个属性

几个属性的例子可以参考这个链接 https://stackoverflow.com/questions/41267416/hibernate-5-naming-strategy-examples

配置

在 Spring Boot 中的 application.yml 建议做如下配置。

spring:
  jpa:
    hibernate:
      naming:
        physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
        implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl