[hibernate-dev] Naming and "naming strategies"
Steve Ebersole
steve at hibernate.org
Thu Jan 15 09:38:12 EST 2015
So here ( I think) is a better way to categorize these terms/concepts:
1. logical name - the "raw" name for a table or column (or schema or
catalog). Think along of the lines of the concept of a *logical* domain
model in terms of a parallel.
1. explicit logical name - the logical name is explicitly specified in
the mapping
2. implicit logical name - the logical name is determined via
ImplicitNamingStrategy
2. physical name - The name for a table or column (or schema or catalog)
as it appears in the database. This is the logical name with rules applied
for quoting, identifier length restrictions, etc as controlled by the
PhysicalNamingStrategy
The above relates specifically to unqualified forms, aka Identifiers. A
qualified name, then, is an aggregation of identifiers such as:
class QualifiedSchemaName {
Identifier catalogName;
Identifier schemaName;
}
class QualifiedTableName {
QualifiedSchemaName schemaName;
Identifier tableName;
}
class QualifiedConstraintName {
QualifiedSchemaName schemaName;
Identifier constraintName;
}
class QualifiedColumnName {
QualifiedTableName tableName;
Identifier columnName;
}
There is nothing intrinsically logical or physical about the Identifier or
Qualified* classes. They can represent both.
Hopefully this helps clarify
On Wed, Jan 14, 2015 at 3:20 PM, Steve Ebersole <steve at hibernate.org> wrote:
> On Wed, Jan 14, 2015 at 11:14 AM, Gunnar Morling <gunnar at hibernate.org>
> wrote:
>
>> 2015-01-14 16:19 GMT+01:00 Steve Ebersole <steve at hibernate.org>:
>>
>>> Something like:
>>>
>>>
>>> public interface ImplicitNamingStrategy {
>>> public Identifier determinePrimaryTableName(ImplicitEntityNameSource
>>> source);
>>>
>>> public Identifier determineJoinTableName(ImplicitJoinTableNameSource
>>> source);
>>>
>>> public Identifier
>>> determineCollectionTableName(ImplicitCollectionTableNameSource
>>> source);
>>>
>>> public Identifier
>>> determineDiscriminatorColumnName(ImplicitDiscriminatorColumnNameSource
>>> source);
>>>
>>> public Identifier
>>> determineTenantIdColumnName(ImplicitTenantIdColumnNameSource
>>> source);
>>>
>>> public Identifier
>>> determineAttributeColumnName(ImplicitAttributeColumnNameSource
>>> source);
>>>
>>> public Identifier
>>> determineCollectionJoinColumnName(ImplicitCollectionJoinColumnNameSource
>>> source);
>>>
>>> ...
>>> }
>>>
>>>
>>> The sources are simply parameter objects providing access to information
>>> needed to determine the implicit name. For example:
>>>
>>> /**
>>> * Defines the source for entity naming. Between legacy Hibernate
>>> requirements and
>>> * JPA requirements this is, unfortunately, multi-sourced. This contract
>>> allows
>>> * access to all source values.
>>> *
>>> * @author Steve Ebersole
>>> */
>>> public interface ImplicitEntityNamingSource {
>>> /**
>>> * The FQN of the entity class. Note, this may be {@code null} in
>>> the
>>> case
>>> * of (non-JPA-compliant) dynamic entities).
>>> *
>>> * @return The entity class FQN, or {@code null} if a dynamic entity.
>>> */
>>> public String getEntityClassName();
>>>
>>> /**
>>> * Get the explicitly specified Hibernate entity name. The Hibernate
>>> name is
>>> * very much different from the JPA concept of entity name.
>>> *
>>> * @return The explicitly specified entity name
>>> */
>>> public String getExplicitEntityName();
>>>
>>> /**
>>> * The Hibernate entity name. This might be either:<ul>
>>> * <li>The explicitly specified entity name, if one</li>
>>> * <li>The unqualified entity class name if no entity name was
>>> explicitly specified</li>
>>> * </ul>
>>> *
>>> * @return The Hibernate entity name
>>> */
>>> public String getEntityName();
>>>
>>> /**
>>> * The JPA-specific entity name. See {@link
>>> javax.persistence.Entity#name()} for details.
>>> *
>>> * @return The JPA entity name, if one was specified. May return {@code
>>> null} if one
>>> * was not explicitly specified.
>>> */
>>> public String getJpaEntityName();
>>> }
>>>
>>> etc...
>>>
>>> And then:
>>>
>>> public interface PhysicalNamingStrategy {
>>> public Identifier toPhysicalCatalogName(Identifier name);
>>>
>>> public Identifier toPhysicalSchemaName(Identifier name);
>>>
>>> public Identifier toPhysicalTableName(Identifier name);
>>>
>>> public Identifier toPhysicalColumnName(Identifier name);
>>> }
>>>
>>>
>> So why is PhysicalNamingStrategy returning Identifiers and not Strings? I
>> think I find the double usage of Identifier for logical names (or
>> "components" thereof) and physical names somewhat irritating. As the
>> physical name I'd have expected just the "rendered" String as to be passed
>> to the driver.
>>
>
> First, technically, the "physical" name (the one we send to the database)
> needs to be qualified. So if this is really resolving to that level, we'd
> actually need to have:
>
> public interface PhysicalNamingStrategy {
> public Identifier toPhysicalTableName(Identifier catalog, Identifier
> schema, Identifier table);
>
> public Identifier toPhysicalColumnName(Identifier name);
> }
>
> So you think the naming strategy that is supplied by the user should be
> the thing that interprets Dialect-specific quoting, Dialect-specific
> qualifying, etc? For example, consider:
>
> Identifier catalogName = new Identifier( "some_catalog", true );
> Identifier schemaName = new Identifier( "some_schema", true );
> Identifier tableName = new Identifier( "some_table", true );
> String physicalName =
> physicalNamingStrategy.toPhysicalTableName(catalogName, schemaName,
> tableName );
>
> Do you really think it is best design to have PhysicalNamingStrategy
> (quite often a user-supplied impl) have to deal with Dialect differences in
> qualifying names and quoting names? Aka, is it really the best design to
> have users have to understand to return:
> 1) "some_schema"."some_table"@"some_catalog" - for Oracle
> 2) [some_catalog].[some_schema].[some_table] - for SQLServer
> 3) etc...
>
> IMO it is better to simply have PhysicalNamingStrategy deal with each
> component, and then the Hibernate code that calls PhysicalNamingStrategy be
> the one that renders them to a properly qualified String:
>
> Identifier catalogName = new Identifier( "some_catalog", true );
> Identifier schemaName = new Identifier( "some_schema", true );
> Identifier tableName = new Identifier( "some_table", true );
>
> Identifier physicalCatalogName =
> physicalNamingStrategy.toPhysicalCatalogName(catalogName);
> Identifier physicalSchemaName =
> physicalNamingStrategy.toPhysicalSchemaName(schemaName);
> Identifier physicalTableName = physicalNaming
> Strategy.toPhysicalTableName(tableName);
>
> String qualifiedName = renderQualifiedTableName(physicalCatalogName,
> physicalSchemaName, physicalTableName);
>
> This fits a lot better too with the code as I'd eventually like to see it
> (as it exists on metamodel branch) where the catalog/schema combo is used
> as a hierarchical container for tables, etc. There we would ask the
> PhysicalNamingStrategy to handle each catalog/schema once.
>
>
>
>
>>
>> I think ultimately it makes sense to pass some additional information
>>> into PhysicalNamingStrategy to give access to Dialect, etc.
>>>
>>
>> Interesting; As we had already discussed recently, that raises the
>> question of how to evolve the interface in a compatible way.
>>
>> Java 8's default methods of course help, but until we can rely on that, I
>> find using abstract classes rather than interfaces attractive for such
>> contracts. With these, a method such as void
>> initialize(SomeInitializationContext) can be easily added, and
>> implementations can override it in a future version if they wish. Or you
>> foresee some sort of context parameter on the actual methods of the
>> contract.
>>
>
> I am not following here. Maybe you are thinking I meant that the
> "additional information" would be passed to the PhysicalNamingStrategy
> instance directly (injected as it were). I just meant expanding the
> parameters. Naively:
>
>
> public interface PhysicalNamingStrategy {
> public Identifier toPhysicalCatalogName(Identifier name, Dialect
> dialect);
> public Identifier toPhysicalSchemaName(Identifier name, Dialect
> dialect);
> ...
> }
>
> Although, you know how I generally prefer "parameter objects".
>
>
>>
>>
>>>
>>> On Wed, Jan 14, 2015 at 4:38 AM, Hardy Ferentschik <hardy at hibernate.org>
>>> wrote:
>>>
>>> > Hi,
>>> >
>>> > +1 for ImplicitNamingStrategy and PhysicalNamingStrategy
>>> > What would be the contract of these strategies?
>>> >
>>> > I don't think LogicalNamingStrategy is necessary. I think this might
>>> > just get too complicated for a user. Also, iiuc the logical name is for
>>> > internal lookups.
>>> >
>>> > +1 for actual identifier classes. I think the code would become easier
>>> to
>>> > understand
>>> > and hopefully safer with these typed classes. I liked this approach
>>> when
>>> > working
>>> > on the metamodel branch. I would, however, not make it an interface. I
>>> > also see
>>> > this as a pre-mature optimisation
>>> >
>>> > > Since JPA does not say what is legal/illegal for the @Column.table
>>> > > attribute, it is feasible for us to allow @Column.table to contain
>>> the
>>> > > catalog/schema information in these cases as a selector..
>>> >
>>> > What exactly do you mean with 'selector'?
>>> >
>>> > --Hardy
>>> >
>>> >
>>> >
>>> > On Tue, Jan 13, 2015 at 12:43:37PM -0600, Steve Ebersole wrote:
>>> > > As I am working on 5.0, one of the things I am trying to accomplish
>>> is to
>>> > > make the handling of table/column names more consistent and better
>>> > > defined. The first step in that is to properly define the terms used
>>> > often
>>> > > throughout the codebase.
>>> > >
>>> > > The first level of naming is the "given" name of a table/column. The
>>> > given
>>> > > name might be:
>>> > > * explicit - explicitly specified by the user, as in @Table(
>>> > > name="explicit_name" )
>>> > > * implicit - not explicitly specified by the user and thus implicitly
>>> > > determined (by JPA rules, "naming strategy", etc).
>>> > >
>>> > > Next, we have a logical name which is a normalized form of the
>>> "given"
>>> > > name. This is the form used to reference tables/columns internally.
>>> > E.g.,
>>> > > its how we resolve @Column(..., table="xyz"). More on this form
>>> later.
>>> > >
>>> > > Finally we have the physical name of the thing, which is the actual
>>> name
>>> > of
>>> > > the table/column in the database. Again, this is generally a
>>> > normalization
>>> > > of the given name based on Dialect, "naming strategy", etc.
>>> > >
>>> > > Today, we have a very messy concept called a NamingStrategy. I say
>>> it is
>>> > > messy because it tries to combine unrelated concerns. So I still
>>> plan to
>>> > > split this as I have outlined elsewhere into:
>>> > > 1) ImplicitNamingStrategy
>>> > > 2) PhysicalNamingStrategy
>>> > >
>>> > > Which brings up my first question to y'all. Do we need a contract
>>> for
>>> > > LogicalNamingStrategy? As I have said, the logical names are the
>>> things
>>> > > used to resolve references. Allowing people to plug in custom
>>> strategies
>>> > > for how that normalization works could be very dangerous. But even
>>> more
>>> > > than that, is it really interesting to be able to hook into that
>>> process?
>>> > >
>>> > > Historically, these names are all represented by String. So I also
>>> > propose
>>> > > to shift this to use that Identifier class we developed for the
>>> metamodel
>>> > > redesign. For those that may be unfamiliar, it essentially combines
>>> the
>>> > > String name with a "quoted" boolean:
>>> > >
>>> > > public class Identifier {
>>> > > private final String text;
>>> > > private final boolean isQuoted;
>>> > > ...
>>> > > }
>>> > >
>>> > > Table names, then, are an aggregation of 3 Identifiers: one for
>>> catalog,
>>> > > one for schema, one for table name. Same for named constraints
>>> > > (ultimately, which is part of a improvement for 6.0 to allow indexes,
>>> > > constraints, etc to be created in a separate schema from tables).
>>> > >
>>> > > Since a major goal for 5.0 is to continue to use the
>>> > org.hibernate.mapping
>>> > > package as the representation of the mapping information, we
>>> obviously
>>> > want
>>> > > to minimize changes there to only what is completely essential. To
>>> that
>>> > > end, if we are going to use Identifier over String stuff in the
>>> > > org.hibernate.mapping package will need to deal with both; internally
>>> > they
>>> > > will hold the Identifier and use that to implement the String-based
>>> > > name-related methods they expose.
>>> > >
>>> > > Lastly I wanted to discuss the details of the logical names. For
>>> tables,
>>> > > we currently qualify the table name with the catalog/schema info.
>>> There
>>> > is
>>> > > a mismatch in this regard when it comes to remaining a pure JPA
>>> > > implementation. Consider @Column( ..., table="some_table").
>>> Ultimately
>>> > we
>>> > > need to be able to qualify that with catalog/schema in order to be
>>> able
>>> > to
>>> > > construct a matching logical name (to be able to pair that with the
>>> > > referenced org.hibernate.mapping.Table later). This is trivial when
>>> > table
>>> > > names are unique across all the catalogs/schemas (when there is only
>>> one
>>> > > "some_table" in all the mapped catalogs/schemas). But is poses a
>>> problem
>>> > > when the same table name is used from different schemas (e.g., when
>>> > > "some_table" is mapped from both "schema1" and "schema2"). So we
>>> have a
>>> > > choice. Since JPA does not say what is legal/illegal for the
>>> > @Column.table
>>> > > attribute, it is feasible for us to allow @Column.table to contain
>>> the
>>> > > catalog/schema information in these cases as a selector. The only
>>> other
>>> > > option I can see is to define a limitation that says that a table
>>> name
>>> > must
>>> > > be unique for a given entity across all catalogs/schemas. I don't
>>> think
>>> > > that is generally a restrictive limitation. What are y'alls
>>> thoughts?
>>> > > Perhaps this is one argument for allowing pluggable
>>> > LogicalNamingStrategy?
>>> > > _______________________________________________
>>> > > hibernate-dev mailing list
>>> > > hibernate-dev at lists.jboss.org
>>> > > https://lists.jboss.org/mailman/listinfo/hibernate-dev
>>> >
>>> _______________________________________________
>>> hibernate-dev mailing list
>>> hibernate-dev at lists.jboss.org
>>> https://lists.jboss.org/mailman/listinfo/hibernate-dev
>>>
>>
>>
>
More information about the hibernate-dev
mailing list