Hibernate5中实体映射命名策略

简介: Hibernate5中实体映射命名策略

对象模型到关系数据库的映射的一部分是将对象模型中的名称映射到相应的数据库名称。

首先说明一点,Hibernate5中不再支持hibernate.ejb.naming_strategy属性配置,可以使用如下两个属性配置替换:

hibernate.implicit_naming_strategy
hibernate.physical_naming_strategy

Hibernate5以前默认命名策略接口类示意图:

Hibernate5将此视为2阶段过程:

  • 第一阶段是从域模型映射中确定正确的逻辑名称。逻辑名可以由用户显式指定(使用@Column或 @Table例如),也可以由Hibernate通过ImplicitNamingStrategy契约隐式确定 。
  • 其次是将此逻辑名解析为PhysicalNamingStrategy合约定义的物理名称。

【1】 ImplicitNamingStrategy

当实体没有显式命名它映射到的数据库表时,我们需要隐式确定该表名。或者,当某个特定属性没有显式命名它映射到的数据库列时,我们需要隐式确定该列名。

org.hibernate.boot.model.naming.ImplicitNamingStrategy当映射未提供显式名称时,有一些规则用于确定逻辑名称的示例。

① ImplicitNamingStrategy的实现

Hibernate定义了多个ImplicitNamingStrategy实现。应用程序也可以插入自定义实现。有多种方法可以指定要使用的ImplicitNamingStrategy。

首先,应用程序可以使用hibernate.implicit_naming_strategy配置设置指定实现,该配置设置接受:

  • 用于开箱即用的实现的预定义“短名称”
  • default:org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl- 别名jpa
  • jpa:org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl- 符合JPA 2.0标准的命名策略
  • legacy-hbm:org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl- 符合原始的Hibernate NamingStrategy
  • legacy-jpa:org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl- 符合为JPA 1.0开发的遗留NamingStrategy,遗憾的是,在很多方面都不清楚隐式命名规则

component-path: org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl- 主要遵循ImplicitNamingStrategyJpaCompliantImpl规则,除了它使用完整的复合路径,而不仅仅是结束属性部分

  • 引用实现org.hibernate.boot.model.naming.ImplicitNamingStrategy的类

其次,应用程序和集成可以利用org.hibernate.boot.MetadataBuilder#applyImplicitNamingStrategy 指定要使用的ImplicitNamingStrategy。


② 配置ImplicitNamingStrategy

方法一

在Hibernate的配置信息设置的时候,如下:

Configuration config = new Configuration().configure();
config.setImplicitNamingStrategy(ImplicitNamingStrategyComponentPathImpl.INSTANCE);

方法二

ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().build();
MetadataSources sources = new MetadataSources(serviceRegistry);
MetadataBuilder builder = sources.getMetadataBuilder();
builder.applyImplicitNamingStrategy(ImplicitNamingStrategyComponentPathImpl.INSTANCE);

方法三

SpringBoot整合JPA中在application.yml中配置:


【2】PhysicalNamingStrategy

① PhysicalNamingStrategy的实现

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

虽然ImplicitNamingStrategy的目的是确定一个名为accountNumbermap 的属性映射到accountNumber未明确指定的逻辑列名称,PhysicalNamingStrategy的目的是,例如,应该说缩写物理列名称acct_num。

默认实现是简单地使用逻辑名称作为物理名称。但是,应用程序和集成可以定义此PhysicalNamingStrategy规则的自定义实现。

PhysicalNamingStrategy 接口源码如下:

public interface PhysicalNamingStrategy {
  public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment);
  public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment);
  public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment);
  public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment);
  public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment);
}

其只有一个实现:

public class PhysicalNamingStrategyStandardImpl implements PhysicalNamingStrategy, Serializable {
  /**
   * Singleton access
   */
  public static final PhysicalNamingStrategyStandardImpl INSTANCE = new PhysicalNamingStrategyStandardImpl();
  @Override
  public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment context) {
    return name;
  }
  @Override
  public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment context) {
    return name;
  }
  @Override
  public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
    return name;
  }
  @Override
  public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment context) {
    return name;
  }
  @Override
  public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
    return name;
  }
}

默认情况下,使用就是这种策略,将ImplicitNamingStrategy传过来的逻辑名直接作为数据库中的物理名称。它的使用设置与ImplicitNamingStrategy相同。

② 配置PhysicalNamingStrategy

方法一

在Hibernate的配置信息设置的时候,如下:

Configuration config = new Configuration().configure();
config.setPhysicalNamingStrategy(new PhysicalNamingStrategyStandardImpl());

方法二

ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().build();
MetadataSources sources = new MetadataSources(serviceRegistry);
MetadataBuilder builder = sources.getMetadataBuilder();
builder.applyPhysicalNamingStrategy(new PhysicalNamingStrategyStandardImpl());

方法三

SpringBoot整合JPA中在application.yml中配置:


如上图配置所示,默认情况下如果Entity中不使用@Column指定属性对应数据库的列名,那么将会使用属性作为数据库列名。

实体类User如下:

@Entity 
@Table(name = "tb_user") 
public class User implements Serializable{
    @Id //这是一个主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)//自增主键
    private Integer id;
    private String lastName;
    private String email;
    //...
}

使用方法三数据库生成属性如下:

  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(255) DEFAULT NULL,
  `lastName` varchar(255) DEFAULT NULL,

如果我们想要将其转换成last_name,除了使用@Column注解外,还可以自定义PhysicalNamingStrategy实现。

如下所示:

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

【3】SpringBoot中推荐的Spring 命名策略

SpringBoot包下主要有两个:SpringImplicitNamingStrategySpringPhysicalNamingStrategy


① SpringImplicitNamingStrategy

其继承默认的ImplicitNamingStrategyJpaCompliantImpl ,源码如下:

public class SpringImplicitNamingStrategy extends ImplicitNamingStrategyJpaCompliantImpl {
  @Override
  public Identifier determineJoinTableName(ImplicitJoinTableNameSource source) {
    String name = source.getOwningPhysicalTableName() + "_"
        + source.getAssociationOwningAttributePath().getProperty();
    return toIdentifier(name, source.getBuildingContext());
  }
}

类继承示意图如下:


② SpringPhysicalNamingStrategy

源码如下:

public class SpringPhysicalNamingStrategy implements PhysicalNamingStrategy {
  @Override
  public Identifier toPhysicalCatalogName(Identifier name,
      JdbcEnvironment jdbcEnvironment) {
    return apply(name, jdbcEnvironment);
  }
  @Override
  public Identifier toPhysicalSchemaName(Identifier name,
      JdbcEnvironment jdbcEnvironment) {
    return apply(name, jdbcEnvironment);
  }
  @Override
  public Identifier toPhysicalTableName(Identifier name,
      JdbcEnvironment jdbcEnvironment) {
    return apply(name, jdbcEnvironment);
  }
  @Override
  public Identifier toPhysicalSequenceName(Identifier name,
      JdbcEnvironment jdbcEnvironment) {
    return apply(name, jdbcEnvironment);
  }
  @Override
  public Identifier toPhysicalColumnName(Identifier name,
      JdbcEnvironment jdbcEnvironment) {
    return apply(name, jdbcEnvironment);
  }
  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);
  }
  /**
   * Get an identifier for the specified details. By default this method will return an
   * identifier with the name adapted based on the result of
   * {@link #isCaseInsensitive(JdbcEnvironment)}
   * @param name the name of the identifier
   * @param quoted if the identifier is quoted
   * @param jdbcEnvironment the JDBC environment
   * @return an identifier instance
   */
  protected Identifier getIdentifier(String name, boolean quoted,
      JdbcEnvironment jdbcEnvironment) {
    if (isCaseInsensitive(jdbcEnvironment)) {
      name = name.toLowerCase(Locale.ROOT);
    }
    return new Identifier(name, quoted);
  }
  /**
   * Specify whether the database is case sensitive.
   * @param jdbcEnvironment the JDBC environment which can be used to determine case
   * @return true if the database is case insensitive sensitivity
   */
  protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
    return true;
  }
  private boolean isUnderscoreRequired(char before, char current, char after) {
    return Character.isLowerCase(before) && Character.isUpperCase(current)
        && Character.isLowerCase(after);
  }
}

进行了大小写处理和"_"符号处理。故而,我们也可以在SpringBoot环境下直接使用这两个命名策略。

application.yml配置文件如下:

spring: 
  jpa:
        database: mysql
        hibernate:
          ddl-auto: update
          naming:
            implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
            physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
        show-sql: true

此时再次创建数据表,lastName属性将会自动转换成数据表中last_name列:

`id` int(11) NOT NULL AUTO_INCREMENT,
 `email` varchar(255) DEFAULT NULL,
 `last_name` varchar(255) DEFAULT NULL,

参考hibernate官方文档:Hibernate官方用户指南


目录
相关文章
|
10月前
|
SQL XML 存储
Hibernate框架【五】——基本映射——多对多映射
Hibernate框架【五】——基本映射——多对多映射
135 0
|
6天前
|
SQL 缓存 Java
Hibernate - 检索策略入门与详解
Hibernate - 检索策略入门与详解
33 0
|
6天前
|
SQL 存储 Java
Hibernate - 继承关联关系映射
Hibernate - 继承关联关系映射
37 0
|
Java 数据库连接
mybatis-plus和hibernate 忽略映射字段
mybatis-plus和hibernate 忽略映射字段
252 1
|
SQL Java 数据库连接
《Hibernate上课笔记》-----class3----Hibernate的单实体映射
《Hibernate上课笔记》-----class3----Hibernate的单实体映射
97 0
|
XML Java 数据库连接
《Hibernate上课笔记》----class4----Hibernate继承关系映射实现详解
《Hibernate上课笔记》----class4----Hibernate继承关系映射实现详解
69 0
《Hibernate上课笔记》----class4----Hibernate继承关系映射实现详解
|
SQL Java 数据库连接
《Hibernate上课笔记》-----class8----Hibernate的检索方式和检索策略
《Hibernate上课笔记》-----class8----Hibernate的检索方式和检索策略
85 0
《Hibernate上课笔记》-----class8----Hibernate的检索方式和检索策略
|
存储 SQL Java
hibernate学习笔记之二(映射关系与懒加载)
hibernate学习笔记之二(映射关系与懒加载)
hibernate学习笔记之二(映射关系与懒加载)
|
SQL 存储 数据可视化
Java Web之Hibernate的高级应用(数据库实体关联之间的映射规则、实体继承关系映射、Hibernate查询语言)
Java Web之Hibernate的高级应用(数据库实体关联之间的映射规则、实体继承关系映射、Hibernate查询语言)
157 0
Java Web之Hibernate的高级应用(数据库实体关联之间的映射规则、实体继承关系映射、Hibernate查询语言)
|
Oracle 关系型数据库 Java
hibernate注解 映射序列到Oracle
hibernate注解 映射序列到Oracle