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官方用户指南


目录
相关文章
|
3月前
|
缓存 NoSQL Java
揭秘性能提升的超级武器:掌握Hibernate二级缓存策略!
【9月更文挑战第3天】在软件开发中,性能优化至关重要。使用Hibernate进行数据持久化的应用可通过二级缓存提升数据访问速度。一级缓存随Session生命周期变化,而二级缓存是SessionFactory级别的全局缓存,能显著减少数据库访问次数,提高性能。要启用二级缓存,需在映射文件或实体类上添加相应配置。然而,并非所有场景都适合使用二级缓存,需根据业务需求和数据变更频率决定。此外,还可与EhCache、Redis等第三方缓存集成,进一步增强缓存效果。合理运用二级缓存策略,有助于大幅提升应用性能。
103 5
|
3月前
|
Java 数据库连接 API
解锁高效开发秘籍:深入探究 Hibernate 如何优雅处理一对多与多对多关系,让数据映射再无烦恼!
【9月更文挑战第3天】Hibernate 是 Java 领域中最流行的 ORM 框架之一,广泛用于处理实体对象与数据库表之间的映射。尤其在处理复杂关系如一对多和多对多时,Hibernate 提供了丰富的 API 和配置选项。本文通过具体代码示例,展示如何使用 `@OneToMany`、`@JoinColumn`、`@ManyToMany` 和 `@JoinTable` 等注解优雅地实现这些关系,帮助开发者保持代码简洁的同时确保数据一致性。
66 4
|
4月前
|
Java 数据库连接 数据库
AI 时代风起云涌,Hibernate 实体映射引领数据库高效之路,最佳实践与陷阱全解析!
【8月更文挑战第31天】Hibernate 是一款强大的 Java 持久化框架,可将 Java 对象映射到关系数据库表中。本文通过代码示例详细介绍了 Hibernate 实体映射的最佳实践,包括合理使用关联映射(如 `@OneToMany` 和 `@ManyToOne`)以及正确处理继承关系(如单表继承)。此外,还探讨了常见陷阱,例如循环依赖可能导致的无限递归问题,并提供了使用 `@JsonIgnore` 等注解来避免此类问题的方法。通过遵循这些最佳实践,可以显著提升开发效率和数据库操作性能。
93 0
|
4月前
|
数据库 开发者 Java
Hibernate映射注解的魔力:实体类配置的革命,让你的代码量瞬间蒸发!
【8月更文挑战第31天】Hibernate 是一款出色的对象关系映射框架,简化了 Java 应用与数据库的交互。其映射注解让实体类配置变得直观简洁。本文深入剖析核心概念与使用技巧,通过示例展示如何简化配置。
57 0
|
4月前
|
缓存 Java 数据库连接
Hibernate 中的获取策略有哪些?
【8月更文挑战第21天】
24 0
|
5月前
|
SQL 缓存 Java
使用Hibernate实现复杂数据库查询优化策略
使用Hibernate实现复杂数据库查询优化策略
|
5月前
|
SQL 缓存 Java
使用Hibernate实现复杂数据库查询优化策略
使用Hibernate实现复杂数据库查询优化策略
|
6月前
|
缓存 Java 数据库连接
构建高效数据库交互:Hibernate与JPA的性能优化策略
【6月更文挑战第25天】在大数据时代,优化Hibernate和JPA的数据库性能至关重要。本文探讨了优化策略:正确配置映射以减少冗余,利用JPQL/HQL提升查询效率,避免全字段选择,使用索引和分页,有效利用缓存策略,以及管理事务以平衡资源锁定。示例代码展示了分页查询的实现,以防止性能下降。
134 0
|
7月前
|
SQL 缓存 Java
框架分析(9)-Hibernate
框架分析(9)-Hibernate
|
1月前
|
缓存 Java 数据库连接
Hibernate:Java持久层框架的高效应用
通过上述步骤,可以在Java项目中高效应用Hibernate框架,实现对关系数据库的透明持久化管理。Hibernate提供的强大功能和灵活配置,使得开发者能够专注于业务逻辑的实现,而不必过多关注底层数据库操作。
19 1