[hibernate-dev] Naming and "naming strategies"
Steve Ebersole
steve at hibernate.org
Wed Jan 14 16:20:04 EST 2015
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