在Hibernate 5.x中,Hibernate将实体名称映射到数据库中时,将这个过程分成两个步骤:
- 第一个阶段是从对象模型中提取一个合适的逻辑名称,这个逻辑名称可以由用户指定,通过@Column和@Table等注解完成,也可以通过被Hibernate的
ImplicitNamingStrategy指定; - 第二个阶段是将上述的逻辑名称解析成物理名称,物理名称是由Hibernate中的
PhysicalNamingStrategy决定;
在之前,我在Hibernate中实体映射时的命名策略(1)中详细说明了通过继承DefaultNamingStrategy来指定我们的自己的命名策略,但是从Hibernate 5.X开始,命名策略就被分成上述的两个步骤,从官方的文档中的得知这样做的目的是提高灵活性,减少构建命名策略过程中用到的重复的信息。
但是从目前看来,我自身并没有感觉到这种从新设计的好处,反而有点复杂。
一、ImplicitNamingStrategy
当一个实体对象没有显式的指明它要映射的数据库表或者列的名称时,在Hibernate内部就要为我们隐式处理,比如一个实体没有在@Table中的指明表名,那么表名隐式的被认为是实体名,或者@Entity中的提供的名称。
这些隐式的处理都是Hibernate帮我们做的,Hibernate中定义了多个ImplicitNamingStrategy的实现,可以开箱即用。而之前的逻辑名名称就是物理名称这种策略只是其中一种,其他还包括:
- ImplicitNamingStrategyJpaCompliantImpl:默认的命名策略,兼容JPA 2.0的规范;
- ImplicitNamingStrategyLegacyHbmImpl:兼容Hibernate老版本中的命名规范;
- ImplicitNamingStrategyLegacyJpaImpl:兼容JPA 1.0规范中的命名规范
- ImplicitNamingStrategyComponentPathImpl:大部分与ImplicitNamingStrategyJpaCompliantImpl,但是对于@Embedded等注解标志的组件处理是通过使用attributePath完成的,因此如果我们在使用@Embedded注解的时候,如果要指定命名规范,可以直接继承这个类来实现;
它们之间的关系如下:
那我们在哪设置这些策略以便它们起作用呢?
方法1:
在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);
二、PhysicalNamingStrategy
在这个阶段中,是根据业务需要制定自己的命名规范,通过使用PhysicalNamingStrategy可以实现这些规则,而不需要将表名和列名通过@Table和@Column等注解显式指定。
PhysicalNamingStrategy和ImplicitNamingStrategy的区别
- 从处理的效果上来看,其实没有什么区别,但是从程序设计分层的角度来看,类似于MVC的分层,ImplicitNamingStrategy只管模型对象层次的处理,PhysicalNamingStrategy只管映射成真实的数据名称的处理,但是为了达到相同的效果,比如将userName映射城数据列时,在PhysicalNamingStrategy决定映射成user_name,但是在ImplicitNamingStrategy也可以做到;
- 从处理的场景来看:
- 无论对象模型中是否显式地指定列名或者已经被隐式决定,PhysicalNamingStrategy都会应用;
- 但是对于ImplicitNamingStrategy,仅仅只有当没有显式地提供名称时才会使用,也就是说当对象模型中已经指定了@Table或者@Entity等name时,设置的ImplicitNamingStrategy并不会起作用。
它的使用设置与ImplicitNamingStrategy相同,也是通过两种方法来完成设置的。
方法1:
在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());
在Hibernate 5X 中PhysicalNamingStrategy的默认实现是用逻辑名称代替物理名称,但是我们可以根据实际需求来定义自己的策略,比如加上前缀t_等等,或者使用分隔符”-“等等。而官方只给我们提供了一个实现:PhysicalNamingStrategyStandardImpl,看其中的源码如下:
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的交互
由于涉及到数据库映射的属性过多,这里只关注于数据库表名和列名的映射过程,其他的属性大体相同,也可按照如下分析过程进行。
ImplicitNamingStrategy中的determinePrimaryTableName()方法返回的参数输入到PhysicalNamingStrategy的toPhysicalTableName()之中,过程如下:
在EntityBinder$EntityTableNamingStrategyHelper中的bindTable(…)方法中通过determineImplicitName调用determinePrimaryTableName获取Identifier实例,然后将这个实例传递给toPhysicalTableName(…)
ImplicitNamingStrategy中的determineBasicColumnName()方法返回的参数输入PhysicalNamingStrategy的toPhysicalColumnName()之中,通过Ejb3Column中的
redefineColumnName(...)来完成这个转换,其中关键源码如下:if ( StringHelper.isEmpty( columnName ) ) { if ( propertyName != null ) { /// HHH-6005 magic if ( propertyName.contains( ".collection&&element." ) ) { propertyName = propertyName.replace( "collection&&element.", "" ); } final AttributePath attributePath = AttributePath.parse( propertyName ); // 根据ImplicitNamingStrategy获取Identifier实例 final Identifier implicitName = normalizer.normalizeIdentifierQuoting( implicitNamingStrategy.determineBasicColumnName( new ImplicitBasicColumnNameSource() { @Override public AttributePath getAttributePath() { return attributePath; } @Override public boolean isCollectionElement() { // if the propertyHolder is a collection, assume the // @Column refers to the element column return !propertyHolder.isComponent() && !propertyHolder.isEntity(); } @Override public MetadataBuildingContext getBuildingContext() { return context; } } ) ); // 将Identifier实例传入PhysicalNamingStrategy final Identifier physicalName = physicalNamingStrategy.toPhysicalColumnName( implicitName, database.getJdbcEnvironment() ); mappingColumn.setName( physicalName.render( database.getDialect() ) ); } //Do nothing otherwise } else { final Identifier explicitName = database.toIdentifier( columnName ); final Identifier physicalName = physicalNamingStrategy.toPhysicalColumnName( explicitName, database.getJdbcEnvironment() ); mappingColumn.setName( physicalName.render( database.getDialect() ) ); }
本文详细介绍了Hibernate 5.x中实体映射时的命名策略,包括ImplicitNamingStrategy和PhysicalNamingStrategy的职责和交互。ImplicitNamingStrategy负责从对象模型中提取逻辑名称,PhysicalNamingStrategy则将逻辑名称转化为符合业务需求的物理名称。文章讨论了不同实现的策略,并解释了如何设置和使用这些策略来影响数据库表和列的命名。





