Contents

Hibernate 5 命名策略配置

1. 概述

Hibernate 5 提供了两种用于 Hibernate 实体的不同命名策略:隐式命名策略和物理命名策略。

在本教程中,我们将看到如何配置这些命名策略以将实体映射到自定义的表和列名称。

对于不熟悉 Hibernate 的读者,请务必在此处 查看我们的介绍文章。

2. 依赖

我们将在本教程中使用基本的 Hibernate Core 依赖项:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.3.6.Final</version>
</dependency>

3. 隐式命名策略

Hibernate 使用逻辑名称将实体或属性名称映射到表或列名称。可以通过两种方式自定义此名称:可以使用ImplicitNamingStrategy自动派生,也可以使用注解显式定义。

ImplicitNamingStrategy控制 Hibernate 如何从我们的 Java 类和属性名称派生逻辑名称。我们可以从四种内置策略中进行选择,也可以创建自己的策略。

对于此示例,我们将使用默认策略 ImplicitNamingStrategyJpaCompliantImpl。 使用这种策略,逻辑名称将与我们的 Java 类和属性名称相同。

如果我们想针对特定实体偏离此策略,我们可以使用注解来进行这些自定义。我们可以使用*@Table注解来自定义@Entity的名称。对于属性,我们可以使用@Column*注解:

@Entity
@Table(name = "Customers")
public class Customer {
    @Id
    @GeneratedValue
    private Long id;
    private String firstName;
    private String lastName;
    @Column(name = "email")
    private String emailAddress;
    
    // getters and setters
    
}

使用此配置,Customer实体及其属性的逻辑名称将是:

Customer -> Customers
firstName -> firstName
lastName -> lastName
emailAddress -> email

4. 物理命名策略

现在我们配置了我们的逻辑名称,让我们看看我们的物理名称。

Hibernate 使用物理命名策略将我们的逻辑名称映射到 SQL 表及其列。

**默认情况下,物理名称将与我们在上一节中指定的逻辑名称相同。**如果我们想自定义物理名称,我们可以创建一个自定义 PhysicalNamingStrategy类。

例如,我们可能希望在 Java 代码中使用驼峰式命名,但我们希望对数据库中的实际表名和列名使用下划线分隔的名称。

现在,我们可以使用注解和自定义ImplicitNamingStrategy的组合来正确映射这些名称,但是 Hibernate 5 提供PhysicalNamingStrategy作为简化此过程的一种方式。它从上一节中获取我们的逻辑名称,并允许我们在一个地方自定义它们。

让我们看看这是如何做到的。

首先,我们将创建一个策略,将我们的驼峰式名称转换为使用我们更标准的 SQL 格式:

public class CustomPhysicalNamingStrategy implements PhysicalNamingStrategy {
    @Override
    public Identifier toPhysicalCatalogName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        return convertToSnakeCase(identifier);
    }
    @Override
    public Identifier toPhysicalColumnName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        return convertToSnakeCase(identifier);
    }
    @Override
    public Identifier toPhysicalSchemaName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        return convertToSnakeCase(identifier);
    }
    @Override
    public Identifier toPhysicalSequenceName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        return convertToSnakeCase(identifier);
    }
    @Override
    public Identifier toPhysicalTableName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        return convertToSnakeCase(identifier);
    }
    private Identifier convertToSnakeCase(final Identifier identifier) {
        final String regex = "([a-z])([A-Z])";
        final String replacement = "$1_$2";
        final String newName = identifier.getText()
          .replaceAll(regex, replacement)
          .toLowerCase();
        return Identifier.toIdentifier(newName);
    }
}

最后,我们可以告诉 Hibernate 使用我们的新策略:

hibernate.physical_naming_strategy=com.blogdemo.hibernate.namingstrategy.CustomPhysicalNamingStrategy

使用我们针对Customer实体的新策略,物理名称将是:

Customer -> customers
firstName -> first_name
lastName -> last_name
emailAddress -> email

5. 引用标识符

因为 SQL 是一种声明性语言,构成该语言语法的关键字是为内部使用而保留的,在定义数据库标识符(例如,目录、模式、表、列名)时不能使用它们。

5.1 使用双引号手动转义

转义数据库标识符的第一个选项是使用双引号将表名或列名括起来:

@Entity(name = "Table")
@Table(name = "\"Table\"")
public class Table {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @Column(name = "\"catalog\"")
    private String catalog;
 
    @Column(name = "\"schema\"")
    private String schema;
 
    private String name;
 
    //Getters and setters 
} 

5.2. 使用特定于 Hibernate 的反引号字符手动转义

或者,我们也可以使用反引号字符转义给定的数据库标识符:

@Entity(name = "Table")
@Table(name = "`Table`")
public class Table {
 
    @Id
    @GeneratedValue
    private Long id;
 
    @Column(name = "`catalog`")
    private String catalog;
 
    @Column(name = "`schema`")
    private String schema;
 
    @Column(name = "`name`")
    private String name;
 
    //Getters and setters 
}

5.3. 使用 Hibernate 配置进行全局转义

另一种选择是将hibernate.globally_quoted_identifiers属性设置为true。这样,Hibernate 将转义所有数据库标识符。因此,我们不必手动引用它们。

为了在CustomPhysicalNamingStrategy 中使用带引号的标识符,我们需要在创建新的Identifier对象时显式使用*isQuoted()*方法:

Identifier.toIdentifier(newName, identifier.isQuoted());