hibernate 默认的命名策略是不会将驼峰式命名 (cameCase) 映射到蛇形命名 (snake_case) 上去的, 虽然可以直接在 @Column 上指定需要映射的 name,但是使用策略一致地应用命名更方便些。

从 Hibernate 5 开始,数据库对象命名策略由 PhysicalNamingStrategy 接口表示,我们可以自定义该接口以自动将数据库标识符从 cameCase 转换为 snake_case。

public class CamelCaseToSnakeCaseNamingStrategy 
        extends PhysicalNamingStrategyStandardImpl {
 
    public static final CamelCaseToSnakeCaseNamingStrategy INSTANCE = 
        new CamelCaseToSnakeCaseNamingStrategy();
 
    public static final String CAMEL_CASE_REGEX = "([a-z]+)([A-Z]+)";
 
    public static final String SNAKE_CASE_PATTERN = "$1\\_$2";
 
    @Override
    public Identifier toPhysicalCatalogName(
            Identifier name, 
            JdbcEnvironment context) {
        return formatIdentifier(
            super.toPhysicalCatalogName(name, context)
        );
    }
 
    @Override
    public Identifier toPhysicalSchemaName(
            Identifier name, 
            JdbcEnvironment context) {
        return formatIdentifier(
            super.toPhysicalSchemaName(name, context)
        );
    }
 
    @Override
    public Identifier toPhysicalTableName(
            Identifier name, 
            JdbcEnvironment context) {
        return formatIdentifier(
            super.toPhysicalTableName(name, context)
        );
    }
    @Override
    public Identifier toPhysicalSequenceName(
            Identifier name, 
            JdbcEnvironment context) {
        return formatIdentifier(
            super.toPhysicalSequenceName(name, context)
        );
    }
 
    @Override
    public Identifier toPhysicalColumnName(
            Identifier name, 
            JdbcEnvironment context) {
        return formatIdentifier(
            super.toPhysicalColumnName(name, context)
        );
    }
 
    private Identifier formatIdentifier(
            Identifier identifier) {
        if (identifier != null) {
            String name = identifier.getText();
 
        String formattedName = name
        .replaceAll(
            CAMEL_CASE_REGEX, 
            SNAKE_CASE_PATTERN)
        .toLowerCase();
 
        return !formattedName.equals(name) ?
                    Identifier.toIdentifier(
                        formattedName, 
                        identifier.isQuoted()
                    ) :
                    identifier;
        } else {
            return null;
        } 
    }
}

如果你引用了 hibernate-types,则不需要该自定义策略:

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>${hibernate-types.version}</version>
</dependency>

要使用自定义的策略类,只需要将该类配置给 hibernate.physical_naming_strategy 属性:

<property name="hibernate.physical_naming_strategy"
          value="com.vladmihalcea.hibernate.type.util.CamelCaseToSnakeCaseNamingStrategy"
/>

在 Spring Data JPA 中该策略的默认配置为: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy

实现如下:

// 是否驼峰,根据 aBa 类型判断
private boolean isUnderscoreRequired(char before, char current, char after) {
    return Character.isLowerCase(before) && Character.isUpperCase(current)
            && Character.isLowerCase(after);
}

private Identifier apply(Identifier name, JdbcEnvironment jdbcEnvironment) {
    if (name == null) {
        return null;
    }
    StringBuilder builder = new StringBuilder(name.getText().replace('.', '_'));
    for (int i = 1; i < builder.length() - 1; i++) {
        if (isUnderscoreRequired(builder.charAt(i - 1), builder.charAt(i),
                builder.charAt(i + 1))) {
            builder.insert(i++, '_');
        }
    }
    return getIdentifier(builder.toString(), name.isQuoted(), jdbcEnvironment);
}

隐式命名策略 (ImplicitNamingStrategy) 在缺省时提供相应的命名处理:

  • 实体主表: @Table
  • 连表: @JoinTable
  • 集合表: @CollectionTable
  • 列命名: @Column

Spring Data JPA 模式隐式命名策略为: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy