前言

JPA和Hibernate都提供了默认映射策略,通过映射将每个实体类映射到具有相同名称的数据库表,它的每个属性都映射到具有相同属性的列, 但是,在实际项目开发中可能出现与默认命名约定不匹配,也就是说我们需要更改默认值,我们应该肿么办呢?此时我们就需要详细了解Hibernate中的命名策略,本文略长,请耐心细读。

Hibernate 5命名策略(naming strategy)

首先我们对于Hibernate 4和Hibernate 5版本中命名策略的不同作一个大的概括,然后接下来以Hibernate 5中的命名策略进行详细讲解。我们知道在Hibernate 4中使用的命名策略是hibernate.ejb.naming_strategy,该策略使用的是EJB3NamingStrategy,ImprovedNamingStrategy,DefaultComponentSafeNamingStrategy和DefaultNamingStrategy来映射名称,同时EJB3NamingStrateg是默认命名策略,它提供驼峰字段和表名,在命名外键列时,它使用下划线(_)作为分隔符,例如,如果有一个名称为table1和主键为id的表,那么在table2中,外键列将被创建为table1_id,并且此EJB3NamingStrategy实现NamingStrategy接口。由于基于NamingStrategy命名策略接口所实现的上述策略还是不够灵活以至于无法正确应用命名规则,因此hibernate.ejb.naming_strategy不再适用,取而代之的是,引入了两种新策略来提供对命名策略的深度定制,它们是ImplicitNamingStrategy和PhysicalNamingStrategy,这两种策略分别对应的键是implicit_naming_strategy和physical_naming_strategy,Hibernate5仅提供了针对PhysicalNamingStrategy的一种实现即PhysicalNamingStrategyStandardImpl,但是针对ImplicitNamingStrategy策略(隐式命名策略)提供了几种实现。当我们未在实体中显式定义表名和列名时,将使用ImplicitNamingStrategy命名策略,其中,PhysicalNamingStrategy策略可用于显式定义实体和属性名称与数据库和列名称的映射规则。

基于前言的描述,若需要统一修改成公司规定的命名规则,我们当然可以为每个实体指定表名且为每个属性指定列名, 这需要在每个类上使用@Table注释,并在每个属性上使用@Column注释,我们称之为显式命名,但是对项目中大量的实体和属性执行此操作需要大量工作,为了提高效率,我们可以调整Hibernate的命名策略才是最佳方式,但是完成调整Hibernate中的命名策略之前,我们首先需要讨论下Hibernate的逻辑命名策略和物理命名策略之间的区别。

默认情况下即我们未进行任何配置的情况下,逻辑名称和物理名称是一样的,Hibernate将实体或属性名称到表或列名称的映射分为两个步骤:

【1】它首先确定实体或属性的逻辑名称。我们可以使用@Table和@Column注解显式设置逻辑名称,如果我们不这样做,则Hibernate将使用其隐式命名策略之一。
【2】它将逻辑名称映射为物理名称。默认情况下,Hibernate使用逻辑名作为物理名,但是,我们也可以使用PhysicalNamingStrategy策略,将逻辑名称映射为遵循内部命名约定的物理名称。

以上两个步骤我相信并不难理解,那么,为什么Hibernate要区分逻辑命名策略和物理命名策略,而在JPA规范却没有呢?JPA的命名策略方式行之也有效,但是我们会发现Hibernate的方法提供了更大的灵活性,通过将过程分为两个步骤,Hibernate允许我们实现转换,该转换将应用于所有属性和类。例如,如果我们的命名约定要求在所有表名称上加上“ s”,则可以在PhysicalNamingStrategy中执行此操作,然后呢,无论我们是在@Table注解中显式指定表名,还是基于实体名隐式地指定表名,都没有任何关系。在这两种情况下,Hibernate都会在表名的末尾添加“ s”。有的童鞋可能问到底究竟何为逻辑名称和物理名称呢?一言以蔽之,逻辑名称是未经任何配置情况下的名称(此时逻辑名称和物理名称相同),而物理名称则是最终存在数据库表中的表名和列名,可以通过注解指定。

显式命名策略(Explicit naming strategy)

显式命名策略非常简单,我们唯一需要做的就是用@Table注解实体类或使用@Column注解实体属性,例如如下:

@Entity
@Table(name = "student")
public class Student { @Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id")
private int id;
}

隐式命名策略(Implicit naming strategy)

如果我们未在注解中设置表名或列名,那么Hibernate将使用其隐式命名策略之一, 我们可以选择如下4种不同的命名策略,4种隐式命名策略存在于包【org.hibernate.boot.model.naming】下,在详细讲解以下4种隐式命名策略之前,我们需要用到以下4个类以及配置其关系,如下:

@Embeddable
public class EmbeddableElement { @Column(name = "quotedField")
private String quotedField; @Column
private String regularField;
}
@Entity
@Table(name = "mainTable")
public class MainEntity { @Id
private long id; @ElementCollection
private Set<EmbeddableElement> mainElements; @OneToMany(targetEntity = DependentEntity.class)
Set<DependentEntity> dependentEntities; @OneToOne(targetEntity = OwnedEntity.class)
OwnedEntity ownedEntity;
}
@Entity
@Table(name = "dependentTable")
public class DependentEntity {
@Id
private Long id; @ManyToOne
MainEntity mainEntity; @ElementCollection
@CollectionTable(name = "dependentElements")
Set<EmbeddableElement> dependentElements;
}
@Entity(name = "owned_table")
public class OwnedEntity {
@Id
private Long id; @ElementCollection
@CollectionTable
Set<EmbeddableElement> ownedElements;
}

在上一节的创建Hibernate配置文件的基础上,添加对上述MainEntity、DependentEntity、OwnedEntity和EmbeddableElement对象的映射,如下:

【1】jpa(ImplicitNamingStrategyJpaCompliantImpl)

它作为隐式默认命名策略,也是符合JPA 2.0规范中定义的命名策略,该规范指出实体类的逻辑名称可以是@Entity批注中提供的名称,也可以是非限定的类名称。对于基本属性,它使用属性名称作为逻辑名称。对于元素集合的逻辑名称由拥有实体的类名称 + '_' +  引用实体的属性名称组成。对于联接表的逻辑名称由拥有实体的物理名称 + '_' + 引用实体的物理名称组成。上述最终生成的表如下:

create table mainTable (id bigint not null, ownedEntity_id bigint, primary key (id))

create table MainEntity_mainElements (MainEntity_id bigint not null, quotedField varchar(255), regularField varchar(255))

create table mainTable_dependentTable (MainEntity_id bigint not null, dependentEntities_id bigint not null, primary key (MainEntity_id, dependentEntities_id))

create table dependentTable (id bigint not null, mainEntity_id bigint, primary key (id))

create table dependentElements (DependentEntity_id bigint not null, quotedField varchar(255), regularField varchar(255))

create table owned_table (id bigint not null, primary key (id))

create table owned_table_ownedElements (owned_table_id bigint not null, quotedField varchar(255), regularField varchar(255))

我们看到上述所创建的元素集合表名为MainEntity_mainElements,此时它的逻辑名称是其拥有实体的类名(MainEntity)而不是定义的物理名称(mainTable)+  '_'  + 元素集合属性名称组成。而关联表mainTable_dependentTable则由拥有实体的物理名称(注解@Table)+ '_' + 引用实体的物理名称(注解@Table)组成。同时发现一个很有意思的问题,上述创建表名有大写的字母,实际生成到数据库中表名全是小写,不知道这是什么原因所造成的,如下:

【2】legacy-hbm(ImplicitNamingStrategyLegacyHbmImpl)

它是Hibernate的原始命名策略,但它无法识别JPA的任何注解,但是我们可以使用Hibernate的专有配置文件和注解来定义列或实体名称,除此之外,它与上述JPA 2.0规范还存在一点差异,对于元素集合的逻辑名称组成相同。对于联接表由拥有实体的物理名称 + ‘_’ + 引用实体的属性名称组成(而不再是物理名称),也就是说引用实体(或者我们可称之为连接列)的逻辑名称为其属性名称。接下来我们将默认隐式策略修改为此隐式策略,注意不要将property节点放在mapping节点下面,否则会出现编译错误。

<property name="hibernate.implicit_naming_strategy">
org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
</property>

create table mainTable (id bigint not null, ownedEntity_id bigint, primary key (id))

create table MainEntity_mainElements (MainEntity_id bigint not null, quotedField varchar(255), regularField varchar(255))

create table mainTable_dependentEntities (MainEntity_id bigint not null, dependentEntities bigint not null, primary key (MainEntity_id, dependentEntities))

create table dependentTable (id bigint not null, mainEntity_id bigint, primary key (id))

create table dependentElements (DependentEntity_id bigint not null, quotedField varchar(255), regularField varchar(255))

create table OwnedEntity (id bigint not null, primary key (id))

create table OwnedEntity_ownedElements (OwnedEntity_id bigint not null, quotedField varchar(255), regularField varchar(255))

比如上述MainEntity的引用实体(引用列)dependentEntities和ownedEntity,此时二者逻辑名称就是其属性名称,所以最终我们看到和JPA 2.0规范中对比下,生成的表名为mainTable_dependentEntities、OwnedEntity和OwnedEntity_ownedElements。

【3】legacy-jpa(ImplicitNamingStrategyLegacyJpaImpl)

该策略实现了JPA 1.0规范中定义的命名策略,它与JPA 2.0规范命名策略的主要区别在于:对于元素集合的逻辑名称由拥有实体的物理名称 + '_'  + 引用实体的属性组成。对于联接表的逻辑名称由拥有方的物理表名称 + '_' + 引用实体的物理名称组成。legacy-jpa策略使用物理名称而不是关联引用的实体名称。

create table mainTable (id bigint not null, ownedEntity_id bigint, primary key (id))

create table mainTable_mainElements (mainTable_id bigint not null, quotedField varchar(255), regularField varchar(255))

create table mainTable_dependentTable (mainTable_id bigint not null, dependentEntities_id bigint not null, primary key (mainTable_id, dependentEntities_id))

create table dependentTable (id bigint not null, mainEntity_id bigint, primary key (id))

create table dependentElements (DependentEntity_id bigint not null, quotedField varchar(255), regularField varchar(255))

create table owned_table (id bigint not null, primary key (id))

create table owned_table_ownedElements (owned_table_id bigint not null, quotedField varchar(255), regularField varchar(255))

【4】component-path(ImplicitNamingStrategyComponentPathImpl)

该策略几乎与JPA 2.0规范中定义的命名策略相同,唯一的区别在于:它在逻辑属性名称中包含了复合名称。

create table mainTable (id bigint not null, ownedEntity_id bigint, primary key (id))

create table MainEntity_mainElements (MainEntity_id bigint not null, quotedField varchar(255), mainElements_regularField varchar(255))

create table mainTable_dependentTable (MainEntity_id bigint not null, dependentEntities_id bigint not null, primary key (MainEntity_id, dependentEntities_id))

create table dependentTable (id bigint not null, mainEntity_id bigint, primary key (id))

create table dependentElements (DependentEntity_id bigint not null, quotedField varchar(255), regularField varchar(255))

create table owned_table (id bigint not null, primary key (id))

create table owned_table_ownedElements (owned_table_id bigint not null, quotedField varchar(255), regularField varchar(255))

我们看到针对元素集合EmbeddableElement,针对regularField属性并未显式配置映射列名,此时将使用拥有实体所引用实体的(集合属性名称 + '_' + 属性名称 )作为复合名称。

物理命名策略(PhysicalNamingStrategy)

上述是对Hibernate 5中对于4种隐式命名策略的详细介绍,最好都以默认隐式命名策略作为对比,这样能更好的理解,当然我们也可以实现自定义的隐式命名策略,只需继承自上述4种隐式命名策略之一即可。回到本文开头,我们实现自定义的物理命名策略并不复杂,我们可以实现PhysicalNamingStrategy接口,也可以扩展Hibernate的PhysicalNamingStrategyStandardImpl类,通过扩展Hibernate的PhysicalNamingStrategyStandardImpl更加简单。 在以下示例中,创建一个针对每个表都添加's'即复数的自定义物理命名策略,如下:

public class TableSuffixPhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {

    private final static String suffix = "s";

    @Override
public Identifier toPhysicalTableName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
if (identifier == null) {
return null;
} final String newName = identifier.getText() + suffix;
return Identifier.toIdentifier(newName);
} }

然后在Hibernate配置文件中添加对物理命名策略的自定义实现,如下:

<property name="hibernate.physical_naming_strategy">
strategy.TableSuffixPhysicalNamingStrategy
</property>

或者实现PhysicalNamingStrategy物理命名策略接口,可以达到上述同样的效果:

public class CustomPhysicalNamingStrategy implements PhysicalNamingStrategy {

    private final static String suffix = "s";

    @Override
public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return name;
} @Override
public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return name;
} @Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return jdbcEnvironment.getIdentifierHelper().toIdentifier(
name.getText() + suffix,
name.isQuoted()
);
} @Override
public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return name;
} @Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return name;
}
}

总结

本节我们回顾了Hibernate 4中存在的命名策略以及在Hibernate 5种重新引入两种命名策略以解决在Hibernate 4中的不灵活性,从而我们更方便的实现自定义深度定制的命名策略,无论是隐式命名策略还是物理命名策略根据相应需求皆可,好了,本节我们到此结束,下节我们继续Hibernate 5探索之旅。

Hibernate入门之命名策略(naming strategy)详解的更多相关文章

  1. java加密算法入门(三)-非对称加密详解

    1.简单介绍 这几天一直在看非对称的加密,相比之前的两篇内容,这次看了两倍多的时间还云里雾里的,所以这篇文章相对之前的两篇,概念性的东西多了些,另外是代码的每一步我都做了介绍,方便自己以后翻阅,也方便 ...

  2. Asp.Net MVC3 简单入门第一季(三)详解Controller之Filter

    前言 前面两篇写的比较简单,刚开始写这个系列的时候我面向的对象是刚开始接触Asp.Net MVC的朋友,所以写的尽量简单.所以写的没多少技术含量.把这些技术总结出来,然后一简单的方式让更多的人很好的接 ...

  3. Spring Boot 2.x 快速入门(下)HelloWorld示例详解

    上篇 Spring Boot 2.x 快速入门(上)HelloWorld示例 进行了Sprint Boot的快速入门,以实际的示例代码来练手,总比光看书要强很多嘛,最好的就是边看.边写.边记.边展示. ...

  4. Linux Shell脚本入门--wget 命令用法详解

    Linux Shell脚本入门--wget 命令用法详解 wget是在Linux下开发的开放源代码的软件,作者是Hrvoje Niksic,后来被移植到包括Windows在内的各个平台上.它有以下功能 ...

  5. 80. Hibernate 5.0命名策略使用naming-strategy 不起作用【从零开始学Spring Boot】

    [原创文章,转载请注明出处] 事情的起因:一不小心从1.3.3升级到了1.4.0版本,结果就碰到了各种悲催的事情了,好吧,Hibernate5.0的新特性就是其中一个坑,我们会发现我们配置的namin ...

  6. Hibernate之创建命名策略

    在开发软件时,通常会要求每个开发人员遵守共同的命名策略.例如,数据库的表名及字段名的所有字符都要大写,表名以“S”结尾.对于Customer类,对应的数据库表名为CUSTOMERS.为了在映射文件中遵 ...

  7. elasticsearch系列二:索引详解(快速入门、索引管理、映射详解、索引别名)

    一.快速入门 1. 查看集群的健康状况 http://localhost:9200/_cat http://localhost:9200/_cat/health?v 说明:v是用来要求在结果中返回表头 ...

  8. elasticsearch最全详细使用教程:入门、索引管理、映射详解、索引别名、分词器、文档管理、路由、搜索详解

    一.快速入门1. 查看集群的健康状况http://localhost:9200/_cat http://localhost:9200/_cat/health?v 说明:v是用来要求在结果中返回表头 状 ...

  9. Python学习入门教程,字符串函数扩充详解

    因有用户反映,在基础文章对字符串函数的讲解太过少,故写一篇文章详细讲解一下常用字符串函数.本文章是对:程序员带你十天快速入门Python,玩转电脑软件开发(三)中字符串函数的详解与扩充. 如果您想学习 ...

随机推荐

  1. Djaingo 日志配置

    1.setting.py文件 # 项目级别的日志配置 BASE_LOG_DIR = os.path.join(BASE_DIR, "log") LOGGING = { 'versi ...

  2. MyBatis3——输出参数ResultType、动语态sql

    输出参数ResultType 1.输出参数为简单类型(8个基本+String) 2.输出参数为对象类型 3.输出参数为实体对象类型的集合:虽然输出类型为集合,但是resultType依然写集合的元素类 ...

  3. [bzoj1297] [洛谷P4159] [SCOI2009] 迷路

    Description windy在有向图中迷路了. 该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1. 现在给出该有向图,你能告诉windy总共有多少种不同 ...

  4. 第二阶段冲刺个人任务——five

    今日任务: 合并程序(统计团队博客). 昨日成果: 优化统计团队博客结果界面的显示.

  5. linux下svn安装和使用(centos)

    1.安装svn 本地测试环境 centos6.5 # yum安装 yum -y install subversion # 查看svn版本 svnserve --version # 建立版本库目录 mk ...

  6. ios--->特定构造方法NS_DESIGNATED_INITIALIZER

    特定构造方法 1> 后面带有NS_DESIGNATED_INITIALIZER的方法,就是特定构造方法 2> 子类如果重写了父类的[特定构造方法],那么必须用super调用父类的[特定构造 ...

  7. php--->把json传来的stdClass Object类型转array

    php把json传来的stdClass Object类型转array 1.Php中stdClass.object.array的概念 stdClass是PHP的一个基类,即一个空白的类,所有的类几乎都继 ...

  8. maven远程部署到tomcat8服务器

    maven远程部署到tomcat8服务器 环境准备 linux服务器一台 服务器安装JDK 服务器安装Tomcat 服务器Tomcat8配置 添加Tomcat权限 配置文件路径: tomcat/con ...

  9. Linux(Centos)安装Java JDK及卸载

    步骤一.下载安装包 a.   因为Java JDK区分32位和64位,所以安装之前需先判断一下我们操作系统为多少位·,命令如下: uname -a 解释:如果有x86_64就是64位的,没有就是32位 ...

  10. 【读书笔记】关于《精通C#(第6版)》与《C#5.0图解教程》中的一点矛盾的地方

    志铭-2020年2月8日 03:32:03 先说明,这是一个旧问题,很久很久以前大家就讨论了, 哈哈哈,而且先声明这是一个很无聊的问题,