Improved metadata about a basic-typed value

Description

So we know that `someStringValue` is a basic type and that its Java type is String. But we are still left to guess how to handle this in terms of the underlying database column. Atm we make an assumption based on the fact that the field type is a String, we use that to resolve that to Hibernate's StringType which maps a String to a VARCHAR.

There are already recognized "hints" intended to help alter the relational understanding. E.g. @Lob, @Nationalized, etc. What is being proposed here are some additions to these. Specifically I can see:

  1. @JdbcTypeCode - i.e. @JdbcTypeCode(Types#LONGNVARCHAR) @Basic String someStringValue;

  2. @MutabilityPlan

  3. @Comparator

  4. some annotation to specify an "equals and hashCode" stratgey

  5. some annotation to specify a wrap/unwrap strategy

The last 4 really just describe the components of a JavaTypeDescriptor, so we'd only need those if a JavaTypeDescriptor is not known or if the user wants deviations from the normal semantics.

These values would also get picked up by AttributeConverter support for it to be able to better understand the various "types" (the 2 JavaTypeDescriptors and the SqlTypeDescriptor) involved in its conversion

Environment

None

Activity

Show:
Steve Ebersole
January 16, 2017, 11:57 PM
Edited

Quick background.. JavaTypeDescriptor describes the Java type; SqlTypeDescriptor describes the database type. An ORM Type defines the coordination between the two. These ORM Types generally describe the Java type of a persistent attribute (singular or plural) as well as the index/map-key and element/map-value. ORM Type is broadly categorized by:

  1. ManagedType

    1. EmbeddableType

    2. IdentifiableType

    3. EntityType

  2. BasicType

  3. CollectionType

ManagedTypes and CollectionTypes are the same in that their interesting details and capabilities are kept on a separate group of contracts called "persisters" which they resolve as needed via the TypeConfiguration and expose to the Type holder. BasicTypes generally handle their interesting pieces themselves.

Actually this might have to make it into Alpha1 as it gets to the heart of the new Type design especially as concerns resolution of a BasicType in the ORM Type sense (org.hibernate.type.spi.BasicType). The underlying design here is moving BasicType to more of a composition design rather than subclassing. Specifically a BasicType is made up of:

  1. JavaTypeDescriptor

  2. SqlTypeDescriptor

  3. MutabilityPlan

  4. Comparator

  5. javax.persistence.TemporalType (called "temporal precision") for ORM TemporalTypes

Specifying the individual components listed above via various mapping constructs - the end "accumulation" of those being a org.hibernate.type.spi.BasicTypeParameters reference to pass to org.hibernate.type.spi.BasicTypeRegistry#resolveBasicType which is how BasicTypes are resolved and cached during bootstrap based on a "registry key" which BasicType now exposes as well: BasicType#getRegistryKey. BasicTypeParameters exposes this common information regardless of the mapping source (singular attribute, collection element, etc) for resolution.

In terms of how that affects user migrating, it will effect them if they either:

  1. implement custom type via Type, BasicType, UserType or CompositeUserType - I think the ideal option here is to ask the implementors to make some minor changes in their implementation. The changes are pretty straight forward and referenced in the migration guide.

  2. use Type - generally this happens in relation to setting Query parameters - what we have discussed so far is replacing this with a "memento" approach. So long as the user only referenced org.hibernate.type.Type then everything would "just work". If they referenced specific subclasses things would break. We could discuss continuing to support (but deprecate) certain org.hibernate.type.Type sub-types (just {org.hibernate.type.BasicType}} most likely).

In terms of reading various String representations of the Type to use, we'd be able to seamlessly shift the meaning of that to interpret the historical "type name" mapping metadata in terms of the correlated JavaTypeDescriptor/SqlTypeDescriptor combo. For example Hibernate would always interpret numerous variations on the old basic type registry's key for "String" to mean the JavaTypeDescriptor(String)/SqlTypeDescriptor(VARCHAR). So we do that still, but in this new composition way. Unless... If the named Type was a custom Type we'd not be able to handle that.

  •  

    • I wonder too if we should keep org.hibernate.type.BasicType as well somehow as it is by far the most used and implemented of the Types we are removing (having it subclass the new org.hibernate.type.spi.BasicType interface and delegating method calls as needed via default impls).

P.S. Sorry for the length and not all of it pertains directly to this ticket, but I wanted to get these thoughts down so I did

Christian Beikov
January 17, 2017, 8:22 AM

Do you think that "just" the jdbc type code is enough to determine the SqlTypeDescriptor? I mean users might want use proprietary/dbms-specific types that have the same jdbc type code as other SqlTypeDescriptors.
I also think it might be desirable to allow users to override the type mapping strategy. Would that be possible right now? I'm imagining a contract that has access to the Java member and components(JavaTypeDescriptor etc.) like listed above which based on that information would construct a BasicType or maybe even a UserType? I'm thinking of mapping e.g. some user class to a JSON field.

Steve Ebersole
January 17, 2017, 12:56 PM

I never said we'd only allow "just" the JDBC type code for specifying the SqlTypeDescriptor. However, that being said... at the end of the day the JDBC type is an unbounded int value - yes java.sql.Types defines some standard values but JDBC does not restrict the type code to just those defined on java.sql.Types. So, all that said, even limiting this to "just" the JDBC type code would still work because the user would use a unique custom code.

Keep in mind there are 2 aspects to this:

  1. the keyed (by JDBC type code) entry in SqlTypeDescriptorRegistry

  2. the indication on an attribute, etc as to which SqlTypeDescriptor should be used.

That said, a lot of effort has been put into finding more ways to deduce the SqlTypeDescriptor (and JavaTypeDescriptor) in any given BasicType scenario. E.g. I now am looking at AttributeConverter (based on its type param sig) and javax.persistence.TemporalType to help resolve these.

Not to mention if I have one or the other, I have also put in place a nice resolution to the other based on JDBC recommendations (java.lang.String implies VARCHAR, VARCHAR implies java.lang.String, etc). I really liked the way this piece turned out, and the beauty of it is that it is actually part of the SqlTypeDescriptor and JavaTypeDescriptor contracts themselves. So, e.g., if I have a JavaTypeDescriptor I can ask it JavaTypeDescriptor#getJdbcRecommendedSqlType; if I have a SqlTypeDescriptor, I can ask it SqlTypeDescriptor#getJdbcRecommendedJavaTypeMapping. This allows it to fit in nicely with extended type descriptors.

Plus I'd expected to still allow users to specify an actual BasicType to use.

I may keep UserType around. This is something we were going to discuss later in the process. That all depends on how BasicType ends up looking after the refactor. But keep in mind here as well 2 things:

  1. There is a main reason that Types are being redesigned (namely position based reads) which requires changes to UserType as well if we keep it, meaning there is no case of "oh my pre-6.0 UserType can just work". There will have to be some change to these custom types regardless.

  2. IMHO the redesign to BasicType makes composing a BasicType from the pieces much easier than even writing a custom BasicType/UserType.

Steve Ebersole
February 17, 2017, 6:03 PM

Just to spit ball...

Steve Ebersole
May 24, 2017, 3:25 PM

Some minor changes based on 6.0 development. We will no longer integrate handling of AttributeConverter, Comparator and MutabilityPlan into the "Type" itself (in fact "Type" will largely go away). Instead those things are tracked on the Navigable. In terms of annotation/xml there is not much of a difference.

So ultimately for a basic-valued Navigable we need to determine its:

  1. BasicType (which will continue to exist)

    1. JavaTypeDescriptor

    2. SqlTypeDescriptor

  2. Comparator

  3. MutabilityPlan

Assignee

Unassigned

Reporter

Steve Ebersole

Fix versions

Labels

None

backPortable

None

Suitable for new contributors

None

Requires Release Note

None

Pull Request

None

backportDecision

None

Priority

Major
Configure